1 //////////////////////////////////////////////////////////////////////////////
3 // (C) Copyright Ion Gaztanaga 2005-2012. Distributed under the Boost
4 // Software License, Version 1.0. (See accompanying file
5 // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 // See http://www.boost.org/libs/interprocess for documentation.
9 //////////////////////////////////////////////////////////////////////////////
11 #ifndef BOOST_INTERPROCESS_DETAIL_SPIN_CONDITION_HPP
12 #define BOOST_INTERPROCESS_DETAIL_SPIN_CONDITION_HPP
14 #ifndef BOOST_CONFIG_HPP
15 # include <boost/config.hpp>
18 #if defined(BOOST_HAS_PRAGMA_ONCE)
22 #include <boost/interprocess/detail/config_begin.hpp>
23 #include <boost/interprocess/detail/workaround.hpp>
24 #include <boost/interprocess/sync/spin/mutex.hpp>
25 #include <boost/interprocess/detail/posix_time_types_wrk.hpp>
26 #include <boost/interprocess/detail/atomic.hpp>
27 #include <boost/interprocess/sync/scoped_lock.hpp>
28 #include <boost/interprocess/exceptions.hpp>
29 #include <boost/interprocess/detail/os_thread_functions.hpp>
30 #include <boost/interprocess/sync/spin/wait.hpp>
31 #include <boost/move/utility_core.hpp>
32 #include <boost/cstdint.hpp>
35 namespace interprocess {
40 spin_condition(const spin_condition &);
41 spin_condition &operator=(const spin_condition &);
50 bool timed_wait(L& lock, const boost::posix_time::ptime &abs_time)
53 throw lock_exception();
54 //Handle infinity absolute time here to avoid complications in do_timed_wait
55 if(abs_time == boost::posix_time::pos_infin){
59 return this->do_timed_wait(abs_time, *lock.mutex());
62 template <typename L, typename Pr>
63 bool timed_wait(L& lock, const boost::posix_time::ptime &abs_time, Pr pred)
66 throw lock_exception();
67 //Handle infinity absolute time here to avoid complications in do_timed_wait
68 if(abs_time == boost::posix_time::pos_infin){
69 this->wait(lock, pred);
73 if (!this->do_timed_wait(abs_time, *lock.mutex()))
83 throw lock_exception();
84 do_wait(*lock.mutex());
87 template <typename L, typename Pr>
88 void wait(L& lock, Pr pred)
91 throw lock_exception();
94 do_wait(*lock.mutex());
97 template<class InterprocessMutex>
98 void do_wait(InterprocessMutex &mut);
100 template<class InterprocessMutex>
101 bool do_timed_wait(const boost::posix_time::ptime &abs_time, InterprocessMutex &mut);
104 template<class InterprocessMutex>
105 bool do_timed_wait(bool tout_enabled, const boost::posix_time::ptime &abs_time, InterprocessMutex &mut);
107 enum { SLEEP = 0, NOTIFY_ONE, NOTIFY_ALL };
108 spin_mutex m_enter_mut;
109 volatile boost::uint32_t m_command;
110 volatile boost::uint32_t m_num_waiters;
111 void notify(boost::uint32_t command);
114 inline spin_condition::spin_condition()
116 //Note that this class is initialized to zero.
117 //So zeroed memory can be interpreted as an initialized
123 inline spin_condition::~spin_condition()
125 //Notify all waiting threads
126 //to allow POSIX semantics on condition destruction
130 inline void spin_condition::notify_one()
132 this->notify(NOTIFY_ONE);
135 inline void spin_condition::notify_all()
137 this->notify(NOTIFY_ALL);
140 inline void spin_condition::notify(boost::uint32_t command)
142 //This mutex guarantees that no other thread can enter to the
143 //do_timed_wait method logic, so that thread count will be
144 //constant until the function writes a NOTIFY_ALL command.
145 //It also guarantees that no other notification can be signaled
146 //on this spin_condition before this one ends
149 //Return if there are no waiters
150 if(!atomic_read32(&m_num_waiters)) {
151 m_enter_mut.unlock();
155 //Notify that all threads should execute wait logic
157 while(SLEEP != atomic_cas32(const_cast<boost::uint32_t*>(&m_command), command, SLEEP)){
160 //The enter mutex will rest locked until the last waiting thread unlocks it
163 template<class InterprocessMutex>
164 inline void spin_condition::do_wait(InterprocessMutex &mut)
166 this->do_timed_wait(false, boost::posix_time::ptime(), mut);
169 template<class InterprocessMutex>
170 inline bool spin_condition::do_timed_wait
171 (const boost::posix_time::ptime &abs_time, InterprocessMutex &mut)
173 return this->do_timed_wait(true, abs_time, mut);
176 template<class InterprocessMutex>
177 inline bool spin_condition::do_timed_wait(bool tout_enabled,
178 const boost::posix_time::ptime &abs_time,
179 InterprocessMutex &mut)
181 boost::posix_time::ptime now = microsec_clock::universal_time();
184 if(now >= abs_time) return false;
187 typedef boost::interprocess::scoped_lock<spin_mutex> InternalLock;
188 //The enter mutex guarantees that while executing a notification,
189 //no other thread can execute the do_timed_wait method.
191 //---------------------------------------------------------------
194 InternalLock dummy(m_enter_mut, abs_time);
195 lock = boost::move(dummy);
198 InternalLock dummy(m_enter_mut);
199 lock = boost::move(dummy);
204 //---------------------------------------------------------------
205 //We increment the waiting thread count protected so that it will be
206 //always constant when another thread enters the notification logic.
207 //The increment marks this thread as "waiting on spin_condition"
208 atomic_inc32(const_cast<boost::uint32_t*>(&m_num_waiters));
210 //We unlock the external mutex atomically with the increment
214 //By default, we suppose that no timeout has happened
215 bool timed_out = false, unlock_enter_mut= false;
217 //Loop until a notification indicates that the thread should
218 //exit or timeout occurs
220 //The thread sleeps/spins until a spin_condition commands a notification
221 //Notification occurred, we will lock the checking mutex so that
223 while(atomic_read32(&m_command) == SLEEP){
228 now = microsec_clock::universal_time();
231 //If we can lock the mutex it means that no notification
232 //is being executed in this spin_condition variable
233 timed_out = m_enter_mut.try_lock();
235 //If locking fails, indicates that another thread is executing
236 //notification, so we play the notification game
238 //There is an ongoing notification, we will try again later
241 //No notification in execution, since enter mutex is locked.
242 //We will execute time-out logic, so we will decrement count,
243 //release the enter mutex and return false.
249 //If a timeout occurred, the mutex will not execute checking logic
250 if(tout_enabled && timed_out){
251 //Decrement wait count
252 atomic_dec32(const_cast<boost::uint32_t*>(&m_num_waiters));
253 unlock_enter_mut = true;
257 boost::uint32_t result = atomic_cas32
258 (const_cast<boost::uint32_t*>(&m_command), SLEEP, NOTIFY_ONE);
260 //Other thread has been notified and since it was a NOTIFY one
261 //command, this thread must sleep again
264 else if(result == NOTIFY_ONE){
265 //If it was a NOTIFY_ONE command, only this thread should
266 //exit. This thread has atomically marked command as sleep before
267 //so no other thread will exit.
268 //Decrement wait count.
269 unlock_enter_mut = true;
270 atomic_dec32(const_cast<boost::uint32_t*>(&m_num_waiters));
274 //If it is a NOTIFY_ALL command, all threads should return
275 //from do_timed_wait function. Decrement wait count.
276 unlock_enter_mut = 1 == atomic_dec32(const_cast<boost::uint32_t*>(&m_num_waiters));
277 //Check if this is the last thread of notify_all waiters
278 //Only the last thread will release the mutex
279 if(unlock_enter_mut){
280 atomic_cas32(const_cast<boost::uint32_t*>(&m_command), SLEEP, NOTIFY_ALL);
287 //Unlock the enter mutex if it is a single notification, if this is
288 //the last notified thread in a notify_all or a timeout has occurred
289 if(unlock_enter_mut){
290 m_enter_mut.unlock();
293 //Lock external again before returning from the method
298 } //namespace ipcdetail
299 } //namespace interprocess
302 #include <boost/interprocess/detail/config_end.hpp>
304 #endif //BOOST_INTERPROCESS_DETAIL_SPIN_CONDITION_HPP