Chris@16
|
1 //////////////////////////////////////////////////////////////////////////////
|
Chris@16
|
2 //
|
Chris@16
|
3 // (C) Copyright Ion Gaztanaga 2005-2012. Distributed under the Boost
|
Chris@16
|
4 // Software License, Version 1.0. (See accompanying file
|
Chris@16
|
5 // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
Chris@16
|
6 //
|
Chris@16
|
7 // See http://www.boost.org/libs/interprocess for documentation.
|
Chris@16
|
8 //
|
Chris@16
|
9 //////////////////////////////////////////////////////////////////////////////
|
Chris@16
|
10
|
Chris@16
|
11 #ifndef BOOST_INTERPROCESS_DETAIL_SPIN_CONDITION_HPP
|
Chris@16
|
12 #define BOOST_INTERPROCESS_DETAIL_SPIN_CONDITION_HPP
|
Chris@16
|
13
|
Chris@16
|
14 #include <boost/interprocess/detail/config_begin.hpp>
|
Chris@16
|
15 #include <boost/interprocess/detail/workaround.hpp>
|
Chris@16
|
16 #include <boost/interprocess/sync/spin/mutex.hpp>
|
Chris@16
|
17 #include <boost/interprocess/detail/posix_time_types_wrk.hpp>
|
Chris@16
|
18 #include <boost/interprocess/detail/atomic.hpp>
|
Chris@16
|
19 #include <boost/interprocess/sync/scoped_lock.hpp>
|
Chris@16
|
20 #include <boost/interprocess/exceptions.hpp>
|
Chris@16
|
21 #include <boost/interprocess/detail/os_thread_functions.hpp>
|
Chris@16
|
22 #include <boost/interprocess/sync/spin/wait.hpp>
|
Chris@16
|
23 #include <boost/move/move.hpp>
|
Chris@16
|
24 #include <boost/cstdint.hpp>
|
Chris@16
|
25
|
Chris@16
|
26 namespace boost {
|
Chris@16
|
27 namespace interprocess {
|
Chris@16
|
28 namespace ipcdetail {
|
Chris@16
|
29
|
Chris@16
|
30 class spin_condition
|
Chris@16
|
31 {
|
Chris@16
|
32 spin_condition(const spin_condition &);
|
Chris@16
|
33 spin_condition &operator=(const spin_condition &);
|
Chris@16
|
34 public:
|
Chris@16
|
35 spin_condition();
|
Chris@16
|
36 ~spin_condition();
|
Chris@16
|
37
|
Chris@16
|
38 void notify_one();
|
Chris@16
|
39 void notify_all();
|
Chris@16
|
40
|
Chris@16
|
41 template <typename L>
|
Chris@16
|
42 bool timed_wait(L& lock, const boost::posix_time::ptime &abs_time)
|
Chris@16
|
43 {
|
Chris@16
|
44 if(abs_time == boost::posix_time::pos_infin){
|
Chris@16
|
45 this->wait(lock);
|
Chris@16
|
46 return true;
|
Chris@16
|
47 }
|
Chris@16
|
48 if (!lock)
|
Chris@16
|
49 throw lock_exception();
|
Chris@16
|
50 return this->do_timed_wait(abs_time, *lock.mutex());
|
Chris@16
|
51 }
|
Chris@16
|
52
|
Chris@16
|
53 template <typename L, typename Pr>
|
Chris@16
|
54 bool timed_wait(L& lock, const boost::posix_time::ptime &abs_time, Pr pred)
|
Chris@16
|
55 {
|
Chris@16
|
56 if(abs_time == boost::posix_time::pos_infin){
|
Chris@16
|
57 this->wait(lock, pred);
|
Chris@16
|
58 return true;
|
Chris@16
|
59 }
|
Chris@16
|
60 if (!lock)
|
Chris@16
|
61 throw lock_exception();
|
Chris@16
|
62 while (!pred()){
|
Chris@16
|
63 if (!this->do_timed_wait(abs_time, *lock.mutex()))
|
Chris@16
|
64 return pred();
|
Chris@16
|
65 }
|
Chris@16
|
66 return true;
|
Chris@16
|
67 }
|
Chris@16
|
68
|
Chris@16
|
69 template <typename L>
|
Chris@16
|
70 void wait(L& lock)
|
Chris@16
|
71 {
|
Chris@16
|
72 if (!lock)
|
Chris@16
|
73 throw lock_exception();
|
Chris@16
|
74 do_wait(*lock.mutex());
|
Chris@16
|
75 }
|
Chris@16
|
76
|
Chris@16
|
77 template <typename L, typename Pr>
|
Chris@16
|
78 void wait(L& lock, Pr pred)
|
Chris@16
|
79 {
|
Chris@16
|
80 if (!lock)
|
Chris@16
|
81 throw lock_exception();
|
Chris@16
|
82
|
Chris@16
|
83 while (!pred())
|
Chris@16
|
84 do_wait(*lock.mutex());
|
Chris@16
|
85 }
|
Chris@16
|
86
|
Chris@16
|
87 template<class InterprocessMutex>
|
Chris@16
|
88 void do_wait(InterprocessMutex &mut);
|
Chris@16
|
89
|
Chris@16
|
90 template<class InterprocessMutex>
|
Chris@16
|
91 bool do_timed_wait(const boost::posix_time::ptime &abs_time, InterprocessMutex &mut);
|
Chris@16
|
92
|
Chris@16
|
93 private:
|
Chris@16
|
94 template<class InterprocessMutex>
|
Chris@16
|
95 bool do_timed_wait(bool tout_enabled, const boost::posix_time::ptime &abs_time, InterprocessMutex &mut);
|
Chris@16
|
96
|
Chris@16
|
97 enum { SLEEP = 0, NOTIFY_ONE, NOTIFY_ALL };
|
Chris@16
|
98 spin_mutex m_enter_mut;
|
Chris@16
|
99 volatile boost::uint32_t m_command;
|
Chris@16
|
100 volatile boost::uint32_t m_num_waiters;
|
Chris@16
|
101 void notify(boost::uint32_t command);
|
Chris@16
|
102 };
|
Chris@16
|
103
|
Chris@16
|
104 inline spin_condition::spin_condition()
|
Chris@16
|
105 {
|
Chris@16
|
106 //Note that this class is initialized to zero.
|
Chris@16
|
107 //So zeroed memory can be interpreted as an initialized
|
Chris@16
|
108 //condition variable
|
Chris@16
|
109 m_command = SLEEP;
|
Chris@16
|
110 m_num_waiters = 0;
|
Chris@16
|
111 }
|
Chris@16
|
112
|
Chris@16
|
113 inline spin_condition::~spin_condition()
|
Chris@16
|
114 {
|
Chris@16
|
115 //Trivial destructor
|
Chris@16
|
116 }
|
Chris@16
|
117
|
Chris@16
|
118 inline void spin_condition::notify_one()
|
Chris@16
|
119 {
|
Chris@16
|
120 this->notify(NOTIFY_ONE);
|
Chris@16
|
121 }
|
Chris@16
|
122
|
Chris@16
|
123 inline void spin_condition::notify_all()
|
Chris@16
|
124 {
|
Chris@16
|
125 this->notify(NOTIFY_ALL);
|
Chris@16
|
126 }
|
Chris@16
|
127
|
Chris@16
|
128 inline void spin_condition::notify(boost::uint32_t command)
|
Chris@16
|
129 {
|
Chris@16
|
130 //This mutex guarantees that no other thread can enter to the
|
Chris@16
|
131 //do_timed_wait method logic, so that thread count will be
|
Chris@16
|
132 //constant until the function writes a NOTIFY_ALL command.
|
Chris@16
|
133 //It also guarantees that no other notification can be signaled
|
Chris@16
|
134 //on this spin_condition before this one ends
|
Chris@16
|
135 m_enter_mut.lock();
|
Chris@16
|
136
|
Chris@16
|
137 //Return if there are no waiters
|
Chris@16
|
138 if(!atomic_read32(&m_num_waiters)) {
|
Chris@16
|
139 m_enter_mut.unlock();
|
Chris@16
|
140 return;
|
Chris@16
|
141 }
|
Chris@16
|
142
|
Chris@16
|
143 //Notify that all threads should execute wait logic
|
Chris@16
|
144 spin_wait swait;
|
Chris@16
|
145 while(SLEEP != atomic_cas32(const_cast<boost::uint32_t*>(&m_command), command, SLEEP)){
|
Chris@16
|
146 swait.yield();
|
Chris@16
|
147 }
|
Chris@16
|
148 //The enter mutex will rest locked until the last waiting thread unlocks it
|
Chris@16
|
149 }
|
Chris@16
|
150
|
Chris@16
|
151 template<class InterprocessMutex>
|
Chris@16
|
152 inline void spin_condition::do_wait(InterprocessMutex &mut)
|
Chris@16
|
153 {
|
Chris@16
|
154 this->do_timed_wait(false, boost::posix_time::ptime(), mut);
|
Chris@16
|
155 }
|
Chris@16
|
156
|
Chris@16
|
157 template<class InterprocessMutex>
|
Chris@16
|
158 inline bool spin_condition::do_timed_wait
|
Chris@16
|
159 (const boost::posix_time::ptime &abs_time, InterprocessMutex &mut)
|
Chris@16
|
160 {
|
Chris@16
|
161 return this->do_timed_wait(true, abs_time, mut);
|
Chris@16
|
162 }
|
Chris@16
|
163
|
Chris@16
|
164 template<class InterprocessMutex>
|
Chris@16
|
165 inline bool spin_condition::do_timed_wait(bool tout_enabled,
|
Chris@16
|
166 const boost::posix_time::ptime &abs_time,
|
Chris@16
|
167 InterprocessMutex &mut)
|
Chris@16
|
168 {
|
Chris@16
|
169 boost::posix_time::ptime now = microsec_clock::universal_time();
|
Chris@16
|
170
|
Chris@16
|
171 if(tout_enabled){
|
Chris@16
|
172 if(now >= abs_time) return false;
|
Chris@16
|
173 }
|
Chris@16
|
174
|
Chris@16
|
175 typedef boost::interprocess::scoped_lock<spin_mutex> InternalLock;
|
Chris@16
|
176 //The enter mutex guarantees that while executing a notification,
|
Chris@16
|
177 //no other thread can execute the do_timed_wait method.
|
Chris@16
|
178 {
|
Chris@16
|
179 //---------------------------------------------------------------
|
Chris@16
|
180 InternalLock lock;
|
Chris@16
|
181 if(tout_enabled){
|
Chris@16
|
182 InternalLock dummy(m_enter_mut, abs_time);
|
Chris@16
|
183 lock = boost::move(dummy);
|
Chris@16
|
184 }
|
Chris@16
|
185 else{
|
Chris@16
|
186 InternalLock dummy(m_enter_mut);
|
Chris@16
|
187 lock = boost::move(dummy);
|
Chris@16
|
188 }
|
Chris@16
|
189
|
Chris@16
|
190 if(!lock)
|
Chris@16
|
191 return false;
|
Chris@16
|
192 //---------------------------------------------------------------
|
Chris@16
|
193 //We increment the waiting thread count protected so that it will be
|
Chris@16
|
194 //always constant when another thread enters the notification logic.
|
Chris@16
|
195 //The increment marks this thread as "waiting on spin_condition"
|
Chris@16
|
196 atomic_inc32(const_cast<boost::uint32_t*>(&m_num_waiters));
|
Chris@16
|
197
|
Chris@16
|
198 //We unlock the external mutex atomically with the increment
|
Chris@16
|
199 mut.unlock();
|
Chris@16
|
200 }
|
Chris@16
|
201
|
Chris@16
|
202 //By default, we suppose that no timeout has happened
|
Chris@16
|
203 bool timed_out = false, unlock_enter_mut= false;
|
Chris@16
|
204
|
Chris@16
|
205 //Loop until a notification indicates that the thread should
|
Chris@16
|
206 //exit or timeout occurs
|
Chris@16
|
207 while(1){
|
Chris@16
|
208 //The thread sleeps/spins until a spin_condition commands a notification
|
Chris@16
|
209 //Notification occurred, we will lock the checking mutex so that
|
Chris@16
|
210 spin_wait swait;
|
Chris@16
|
211 while(atomic_read32(&m_command) == SLEEP){
|
Chris@16
|
212 swait.yield();
|
Chris@16
|
213
|
Chris@16
|
214 //Check for timeout
|
Chris@16
|
215 if(tout_enabled){
|
Chris@16
|
216 now = microsec_clock::universal_time();
|
Chris@16
|
217
|
Chris@16
|
218 if(now >= abs_time){
|
Chris@16
|
219 //If we can lock the mutex it means that no notification
|
Chris@16
|
220 //is being executed in this spin_condition variable
|
Chris@16
|
221 timed_out = m_enter_mut.try_lock();
|
Chris@16
|
222
|
Chris@16
|
223 //If locking fails, indicates that another thread is executing
|
Chris@16
|
224 //notification, so we play the notification game
|
Chris@16
|
225 if(!timed_out){
|
Chris@16
|
226 //There is an ongoing notification, we will try again later
|
Chris@16
|
227 continue;
|
Chris@16
|
228 }
|
Chris@16
|
229 //No notification in execution, since enter mutex is locked.
|
Chris@16
|
230 //We will execute time-out logic, so we will decrement count,
|
Chris@16
|
231 //release the enter mutex and return false.
|
Chris@16
|
232 break;
|
Chris@16
|
233 }
|
Chris@16
|
234 }
|
Chris@16
|
235 }
|
Chris@16
|
236
|
Chris@16
|
237 //If a timeout occurred, the mutex will not execute checking logic
|
Chris@16
|
238 if(tout_enabled && timed_out){
|
Chris@16
|
239 //Decrement wait count
|
Chris@16
|
240 atomic_dec32(const_cast<boost::uint32_t*>(&m_num_waiters));
|
Chris@16
|
241 unlock_enter_mut = true;
|
Chris@16
|
242 break;
|
Chris@16
|
243 }
|
Chris@16
|
244 else{
|
Chris@16
|
245 boost::uint32_t result = atomic_cas32
|
Chris@16
|
246 (const_cast<boost::uint32_t*>(&m_command), SLEEP, NOTIFY_ONE);
|
Chris@16
|
247 if(result == SLEEP){
|
Chris@16
|
248 //Other thread has been notified and since it was a NOTIFY one
|
Chris@16
|
249 //command, this thread must sleep again
|
Chris@16
|
250 continue;
|
Chris@16
|
251 }
|
Chris@16
|
252 else if(result == NOTIFY_ONE){
|
Chris@16
|
253 //If it was a NOTIFY_ONE command, only this thread should
|
Chris@16
|
254 //exit. This thread has atomically marked command as sleep before
|
Chris@16
|
255 //so no other thread will exit.
|
Chris@16
|
256 //Decrement wait count.
|
Chris@16
|
257 unlock_enter_mut = true;
|
Chris@16
|
258 atomic_dec32(const_cast<boost::uint32_t*>(&m_num_waiters));
|
Chris@16
|
259 break;
|
Chris@16
|
260 }
|
Chris@16
|
261 else{
|
Chris@16
|
262 //If it is a NOTIFY_ALL command, all threads should return
|
Chris@16
|
263 //from do_timed_wait function. Decrement wait count.
|
Chris@16
|
264 unlock_enter_mut = 1 == atomic_dec32(const_cast<boost::uint32_t*>(&m_num_waiters));
|
Chris@16
|
265 //Check if this is the last thread of notify_all waiters
|
Chris@16
|
266 //Only the last thread will release the mutex
|
Chris@16
|
267 if(unlock_enter_mut){
|
Chris@16
|
268 atomic_cas32(const_cast<boost::uint32_t*>(&m_command), SLEEP, NOTIFY_ALL);
|
Chris@16
|
269 }
|
Chris@16
|
270 break;
|
Chris@16
|
271 }
|
Chris@16
|
272 }
|
Chris@16
|
273 }
|
Chris@16
|
274
|
Chris@16
|
275 //Unlock the enter mutex if it is a single notification, if this is
|
Chris@16
|
276 //the last notified thread in a notify_all or a timeout has occurred
|
Chris@16
|
277 if(unlock_enter_mut){
|
Chris@16
|
278 m_enter_mut.unlock();
|
Chris@16
|
279 }
|
Chris@16
|
280
|
Chris@16
|
281 //Lock external again before returning from the method
|
Chris@16
|
282 mut.lock();
|
Chris@16
|
283 return !timed_out;
|
Chris@16
|
284 }
|
Chris@16
|
285
|
Chris@16
|
286 } //namespace ipcdetail
|
Chris@16
|
287 } //namespace interprocess
|
Chris@16
|
288 } //namespace boost
|
Chris@16
|
289
|
Chris@16
|
290 #include <boost/interprocess/detail/config_end.hpp>
|
Chris@16
|
291
|
Chris@16
|
292 #endif //BOOST_INTERPROCESS_DETAIL_SPIN_CONDITION_HPP
|