comparison DEPENDENCIES/generic/include/boost/interprocess/sync/spin/condition.hpp @ 16:2665513ce2d3

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