1 | #ifndef BOOST_THREAD_CONDITION_VARIABLE_PTHREAD_HPP |
2 | #define BOOST_THREAD_CONDITION_VARIABLE_PTHREAD_HPP |
3 | // Distributed under the Boost Software License, Version 1.0. (See |
4 | // accompanying file LICENSE_1_0.txt or copy at |
5 | // http://www.boost.org/LICENSE_1_0.txt) |
6 | // (C) Copyright 2007-10 Anthony Williams |
7 | // (C) Copyright 2011-2012 Vicente J. Botet Escriba |
8 | |
9 | #include <boost/thread/pthread/timespec.hpp> |
10 | #include <boost/thread/pthread/pthread_mutex_scoped_lock.hpp> |
11 | #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS |
12 | #include <boost/thread/pthread/thread_data.hpp> |
13 | #endif |
14 | #include <boost/thread/pthread/condition_variable_fwd.hpp> |
15 | #ifdef BOOST_THREAD_USES_CHRONO |
16 | #include <boost/chrono/system_clocks.hpp> |
17 | #include <boost/chrono/ceil.hpp> |
18 | #endif |
19 | #include <boost/thread/detail/delete.hpp> |
20 | |
21 | #include <boost/config/abi_prefix.hpp> |
22 | |
23 | namespace boost |
24 | { |
25 | #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS |
26 | namespace this_thread |
27 | { |
28 | void BOOST_THREAD_DECL interruption_point(); |
29 | } |
30 | #endif |
31 | |
32 | namespace thread_cv_detail |
33 | { |
34 | template<typename MutexType> |
35 | struct lock_on_exit |
36 | { |
37 | MutexType* m; |
38 | |
39 | lock_on_exit(): |
40 | m(0) |
41 | {} |
42 | |
43 | void activate(MutexType& m_) |
44 | { |
45 | m_.unlock(); |
46 | m=&m_; |
47 | } |
48 | void deactivate() |
49 | { |
50 | if (m) |
51 | { |
52 | m->lock(); |
53 | } |
54 | m = 0; |
55 | } |
56 | ~lock_on_exit() BOOST_NOEXCEPT_IF(false) |
57 | { |
58 | if (m) |
59 | { |
60 | m->lock(); |
61 | } |
62 | } |
63 | }; |
64 | } |
65 | |
66 | inline void condition_variable::wait(unique_lock<mutex>& m) |
67 | { |
68 | #if defined BOOST_THREAD_THROW_IF_PRECONDITION_NOT_SATISFIED |
69 | if(! m.owns_lock()) |
70 | { |
71 | boost::throw_exception(condition_error(-1, "boost::condition_variable::wait() failed precondition mutex not owned" )); |
72 | } |
73 | #endif |
74 | int res=0; |
75 | { |
76 | #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS |
77 | thread_cv_detail::lock_on_exit<unique_lock<mutex> > guard; |
78 | detail::interruption_checker check_for_interruption(&internal_mutex,&cond); |
79 | pthread_mutex_t* the_mutex = &internal_mutex; |
80 | guard.activate(m); |
81 | res = pthread_cond_wait(&cond,the_mutex); |
82 | check_for_interruption.check(); |
83 | guard.deactivate(); |
84 | #else |
85 | pthread_mutex_t* the_mutex = m.mutex()->native_handle(); |
86 | res = pthread_cond_wait(&cond,the_mutex); |
87 | #endif |
88 | } |
89 | #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS |
90 | this_thread::interruption_point(); |
91 | #endif |
92 | if(res && res != EINTR) |
93 | { |
94 | boost::throw_exception(condition_error(res, "boost::condition_variable::wait failed in pthread_cond_wait" )); |
95 | } |
96 | } |
97 | |
98 | inline bool condition_variable::do_wait_until( |
99 | unique_lock<mutex>& m, |
100 | struct timespec const &timeout) |
101 | { |
102 | #if defined BOOST_THREAD_THROW_IF_PRECONDITION_NOT_SATISFIED |
103 | if (!m.owns_lock()) |
104 | { |
105 | boost::throw_exception(condition_error(EPERM, "boost::condition_variable::do_wait_until() failed precondition mutex not owned" )); |
106 | } |
107 | #endif |
108 | int cond_res; |
109 | { |
110 | #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS |
111 | thread_cv_detail::lock_on_exit<unique_lock<mutex> > guard; |
112 | detail::interruption_checker check_for_interruption(&internal_mutex,&cond); |
113 | pthread_mutex_t* the_mutex = &internal_mutex; |
114 | guard.activate(m); |
115 | cond_res=pthread_cond_timedwait(&cond,the_mutex,&timeout); |
116 | check_for_interruption.check(); |
117 | guard.deactivate(); |
118 | #else |
119 | pthread_mutex_t* the_mutex = m.mutex()->native_handle(); |
120 | cond_res=pthread_cond_timedwait(&cond,the_mutex,&timeout); |
121 | #endif |
122 | } |
123 | #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS |
124 | this_thread::interruption_point(); |
125 | #endif |
126 | if(cond_res==ETIMEDOUT) |
127 | { |
128 | return false; |
129 | } |
130 | if(cond_res) |
131 | { |
132 | boost::throw_exception(condition_error(cond_res, "boost::condition_variable::do_wait_until failed in pthread_cond_timedwait" )); |
133 | } |
134 | return true; |
135 | } |
136 | |
137 | inline void condition_variable::notify_one() BOOST_NOEXCEPT |
138 | { |
139 | #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS |
140 | boost::pthread::pthread_mutex_scoped_lock internal_lock(&internal_mutex); |
141 | #endif |
142 | BOOST_VERIFY(!pthread_cond_signal(&cond)); |
143 | } |
144 | |
145 | inline void condition_variable::notify_all() BOOST_NOEXCEPT |
146 | { |
147 | #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS |
148 | boost::pthread::pthread_mutex_scoped_lock internal_lock(&internal_mutex); |
149 | #endif |
150 | BOOST_VERIFY(!pthread_cond_broadcast(&cond)); |
151 | } |
152 | |
153 | class condition_variable_any |
154 | { |
155 | pthread_mutex_t internal_mutex; |
156 | pthread_cond_t cond; |
157 | |
158 | public: |
159 | BOOST_THREAD_NO_COPYABLE(condition_variable_any) |
160 | condition_variable_any() |
161 | { |
162 | int const res=pthread_mutex_init(&internal_mutex,NULL); |
163 | if(res) |
164 | { |
165 | boost::throw_exception(thread_resource_error(res, "boost::condition_variable_any::condition_variable_any() failed in pthread_mutex_init" )); |
166 | } |
167 | int const res2 = detail::monotonic_pthread_cond_init(cond); |
168 | if(res2) |
169 | { |
170 | BOOST_VERIFY(!pthread_mutex_destroy(&internal_mutex)); |
171 | boost::throw_exception(thread_resource_error(res2, "boost::condition_variable_any::condition_variable_any() failed in detail::monotonic_pthread_cond_init" )); |
172 | } |
173 | } |
174 | ~condition_variable_any() |
175 | { |
176 | BOOST_VERIFY(!pthread_mutex_destroy(&internal_mutex)); |
177 | BOOST_VERIFY(!pthread_cond_destroy(&cond)); |
178 | } |
179 | |
180 | template<typename lock_type> |
181 | void wait(lock_type& m) |
182 | { |
183 | int res=0; |
184 | { |
185 | thread_cv_detail::lock_on_exit<lock_type> guard; |
186 | #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS |
187 | detail::interruption_checker check_for_interruption(&internal_mutex,&cond); |
188 | #else |
189 | boost::pthread::pthread_mutex_scoped_lock check_for_interruption(&internal_mutex); |
190 | #endif |
191 | guard.activate(m); |
192 | res=pthread_cond_wait(&cond,&internal_mutex); |
193 | check_for_interruption.check(); |
194 | guard.deactivate(); |
195 | } |
196 | #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS |
197 | this_thread::interruption_point(); |
198 | #endif |
199 | if(res) |
200 | { |
201 | boost::throw_exception(condition_error(res, "boost::condition_variable_any::wait() failed in pthread_cond_wait" )); |
202 | } |
203 | } |
204 | |
205 | template<typename lock_type,typename predicate_type> |
206 | void wait(lock_type& m,predicate_type pred) |
207 | { |
208 | while(!pred()) wait(m); |
209 | } |
210 | |
211 | #if defined BOOST_THREAD_USES_DATETIME |
212 | template<typename lock_type> |
213 | bool timed_wait(lock_type& m,boost::system_time const& abs_time) |
214 | { |
215 | struct timespec const timeout=detail::to_timespec(abs_time); |
216 | return do_wait_until(m, timeout); |
217 | } |
218 | template<typename lock_type> |
219 | bool timed_wait(lock_type& m,xtime const& abs_time) |
220 | { |
221 | return timed_wait(m,system_time(abs_time)); |
222 | } |
223 | |
224 | template<typename lock_type,typename duration_type> |
225 | bool timed_wait(lock_type& m,duration_type const& wait_duration) |
226 | { |
227 | return timed_wait(m,get_system_time()+wait_duration); |
228 | } |
229 | |
230 | template<typename lock_type,typename predicate_type> |
231 | bool timed_wait(lock_type& m,boost::system_time const& abs_time, predicate_type pred) |
232 | { |
233 | while (!pred()) |
234 | { |
235 | if(!timed_wait(m, abs_time)) |
236 | return pred(); |
237 | } |
238 | return true; |
239 | } |
240 | |
241 | template<typename lock_type,typename predicate_type> |
242 | bool timed_wait(lock_type& m,xtime const& abs_time, predicate_type pred) |
243 | { |
244 | return timed_wait(m,system_time(abs_time),pred); |
245 | } |
246 | |
247 | template<typename lock_type,typename duration_type,typename predicate_type> |
248 | bool timed_wait(lock_type& m,duration_type const& wait_duration,predicate_type pred) |
249 | { |
250 | return timed_wait(m,get_system_time()+wait_duration,pred); |
251 | } |
252 | #endif |
253 | #ifndef BOOST_THREAD_HAS_CONDATTR_SET_CLOCK_MONOTONIC |
254 | |
255 | #ifdef BOOST_THREAD_USES_CHRONO |
256 | template <class lock_type,class Duration> |
257 | cv_status |
258 | wait_until( |
259 | lock_type& lock, |
260 | const chrono::time_point<chrono::system_clock, Duration>& t) |
261 | { |
262 | using namespace chrono; |
263 | typedef time_point<system_clock, nanoseconds> nano_sys_tmpt; |
264 | wait_until(lock, |
265 | nano_sys_tmpt(ceil<nanoseconds>(t.time_since_epoch()))); |
266 | return system_clock::now() < t ? cv_status::no_timeout : |
267 | cv_status::timeout; |
268 | } |
269 | |
270 | template <class lock_type, class Clock, class Duration> |
271 | cv_status |
272 | wait_until( |
273 | lock_type& lock, |
274 | const chrono::time_point<Clock, Duration>& t) |
275 | { |
276 | using namespace chrono; |
277 | system_clock::time_point s_now = system_clock::now(); |
278 | typename Clock::time_point c_now = Clock::now(); |
279 | wait_until(lock, s_now + ceil<nanoseconds>(t - c_now)); |
280 | return Clock::now() < t ? cv_status::no_timeout : cv_status::timeout; |
281 | } |
282 | |
283 | template <class lock_type, class Rep, class Period> |
284 | cv_status |
285 | wait_for( |
286 | lock_type& lock, |
287 | const chrono::duration<Rep, Period>& d) |
288 | { |
289 | using namespace chrono; |
290 | system_clock::time_point s_now = system_clock::now(); |
291 | steady_clock::time_point c_now = steady_clock::now(); |
292 | wait_until(lock, s_now + ceil<nanoseconds>(d)); |
293 | return steady_clock::now() - c_now < d ? cv_status::no_timeout : |
294 | cv_status::timeout; |
295 | |
296 | } |
297 | |
298 | template <class lock_type> |
299 | cv_status wait_until( |
300 | lock_type& lk, |
301 | chrono::time_point<chrono::system_clock, chrono::nanoseconds> tp) |
302 | { |
303 | using namespace chrono; |
304 | nanoseconds d = tp.time_since_epoch(); |
305 | timespec ts = boost::detail::to_timespec(d); |
306 | if (do_wait_until(lk, ts)) return cv_status::no_timeout; |
307 | else return cv_status::timeout; |
308 | } |
309 | #endif |
310 | #else // defined BOOST_THREAD_HAS_CONDATTR_SET_CLOCK_MONOTONIC |
311 | #ifdef BOOST_THREAD_USES_CHRONO |
312 | |
313 | template <class lock_type, class Duration> |
314 | cv_status |
315 | wait_until( |
316 | lock_type& lock, |
317 | const chrono::time_point<chrono::steady_clock, Duration>& t) |
318 | { |
319 | using namespace chrono; |
320 | typedef time_point<steady_clock, nanoseconds> nano_sys_tmpt; |
321 | wait_until(lock, |
322 | nano_sys_tmpt(ceil<nanoseconds>(t.time_since_epoch()))); |
323 | return steady_clock::now() < t ? cv_status::no_timeout : |
324 | cv_status::timeout; |
325 | } |
326 | |
327 | template <class lock_type, class Clock, class Duration> |
328 | cv_status |
329 | wait_until( |
330 | lock_type& lock, |
331 | const chrono::time_point<Clock, Duration>& t) |
332 | { |
333 | using namespace chrono; |
334 | steady_clock::time_point s_now = steady_clock::now(); |
335 | typename Clock::time_point c_now = Clock::now(); |
336 | wait_until(lock, s_now + ceil<nanoseconds>(t - c_now)); |
337 | return Clock::now() < t ? cv_status::no_timeout : cv_status::timeout; |
338 | } |
339 | |
340 | template <class lock_type, class Rep, class Period> |
341 | cv_status |
342 | wait_for( |
343 | lock_type& lock, |
344 | const chrono::duration<Rep, Period>& d) |
345 | { |
346 | using namespace chrono; |
347 | steady_clock::time_point c_now = steady_clock::now(); |
348 | wait_until(lock, c_now + ceil<nanoseconds>(d)); |
349 | return steady_clock::now() - c_now < d ? cv_status::no_timeout : |
350 | cv_status::timeout; |
351 | } |
352 | |
353 | template <class lock_type> |
354 | inline cv_status wait_until( |
355 | lock_type& lock, |
356 | chrono::time_point<chrono::steady_clock, chrono::nanoseconds> tp) |
357 | { |
358 | using namespace chrono; |
359 | nanoseconds d = tp.time_since_epoch(); |
360 | timespec ts = boost::detail::to_timespec(d); |
361 | if (do_wait_until(lock, ts)) return cv_status::no_timeout; |
362 | else return cv_status::timeout; |
363 | } |
364 | |
365 | #endif |
366 | #endif // defined BOOST_THREAD_HAS_CONDATTR_SET_CLOCK_MONOTONIC |
367 | |
368 | #ifdef BOOST_THREAD_USES_CHRONO |
369 | template <class lock_type, class Clock, class Duration, class Predicate> |
370 | bool |
371 | wait_until( |
372 | lock_type& lock, |
373 | const chrono::time_point<Clock, Duration>& t, |
374 | Predicate pred) |
375 | { |
376 | while (!pred()) |
377 | { |
378 | if (wait_until(lock, t) == cv_status::timeout) |
379 | return pred(); |
380 | } |
381 | return true; |
382 | } |
383 | |
384 | template <class lock_type, class Rep, class Period, class Predicate> |
385 | bool |
386 | wait_for( |
387 | lock_type& lock, |
388 | const chrono::duration<Rep, Period>& d, |
389 | Predicate pred) |
390 | { |
391 | return wait_until(lock, chrono::steady_clock::now() + d, boost::move(pred)); |
392 | } |
393 | #endif |
394 | |
395 | void notify_one() BOOST_NOEXCEPT |
396 | { |
397 | boost::pthread::pthread_mutex_scoped_lock internal_lock(&internal_mutex); |
398 | BOOST_VERIFY(!pthread_cond_signal(&cond)); |
399 | } |
400 | |
401 | void notify_all() BOOST_NOEXCEPT |
402 | { |
403 | boost::pthread::pthread_mutex_scoped_lock internal_lock(&internal_mutex); |
404 | BOOST_VERIFY(!pthread_cond_broadcast(&cond)); |
405 | } |
406 | private: // used by boost::thread::try_join_until |
407 | |
408 | template <class lock_type> |
409 | bool do_wait_until( |
410 | lock_type& m, |
411 | struct timespec const &timeout) |
412 | { |
413 | int res=0; |
414 | { |
415 | thread_cv_detail::lock_on_exit<lock_type> guard; |
416 | #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS |
417 | detail::interruption_checker check_for_interruption(&internal_mutex,&cond); |
418 | #else |
419 | boost::pthread::pthread_mutex_scoped_lock check_for_interruption(&internal_mutex); |
420 | #endif |
421 | guard.activate(m); |
422 | res=pthread_cond_timedwait(&cond,&internal_mutex,&timeout); |
423 | check_for_interruption.check(); |
424 | guard.deactivate(); |
425 | } |
426 | #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS |
427 | this_thread::interruption_point(); |
428 | #endif |
429 | if(res==ETIMEDOUT) |
430 | { |
431 | return false; |
432 | } |
433 | if(res) |
434 | { |
435 | boost::throw_exception(condition_error(res, "boost::condition_variable_any::do_wait_until() failed in pthread_cond_timedwait" )); |
436 | } |
437 | return true; |
438 | } |
439 | }; |
440 | |
441 | } |
442 | |
443 | #include <boost/config/abi_suffix.hpp> |
444 | |
445 | #endif |
446 | |