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