Line data Source code
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/detail/platform_time.hpp>
10 : #include <boost/thread/pthread/pthread_mutex_scoped_lock.hpp>
11 : #include <boost/thread/pthread/pthread_helpers.hpp>
12 :
13 : #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS
14 : #include <boost/thread/interruption.hpp>
15 : #include <boost/thread/pthread/thread_data.hpp>
16 : #endif
17 : #include <boost/thread/pthread/condition_variable_fwd.hpp>
18 : #ifdef BOOST_THREAD_USES_CHRONO
19 : #include <boost/chrono/system_clocks.hpp>
20 : #include <boost/chrono/ceil.hpp>
21 : #endif
22 : #include <boost/thread/detail/delete.hpp>
23 :
24 : #include <algorithm>
25 :
26 : #include <boost/config/abi_prefix.hpp>
27 :
28 : namespace boost
29 : {
30 : namespace thread_cv_detail
31 : {
32 : template<typename MutexType>
33 : struct lock_on_exit
34 : {
35 : MutexType* m;
36 :
37 0 : lock_on_exit():
38 0 : m(0)
39 : {}
40 :
41 0 : void activate(MutexType& m_)
42 : {
43 0 : m_.unlock();
44 0 : m=&m_;
45 : }
46 0 : void deactivate()
47 : {
48 : if (m)
49 : {
50 0 : m->lock();
51 : }
52 0 : m = 0;
53 : }
54 0 : ~lock_on_exit() BOOST_NOEXCEPT_IF(false)
55 : {
56 0 : if (m)
57 : {
58 0 : m->lock();
59 : }
60 0 : }
61 : };
62 : }
63 :
64 0 : inline void condition_variable::wait(unique_lock<mutex>& m)
65 : {
66 : #if defined BOOST_THREAD_THROW_IF_PRECONDITION_NOT_SATISFIED
67 : if(! m.owns_lock())
68 : {
69 : boost::throw_exception(condition_error(-1, "boost::condition_variable::wait() failed precondition mutex not owned"));
70 : }
71 : #endif
72 0 : int res=0;
73 0 : {
74 : #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS
75 0 : thread_cv_detail::lock_on_exit<unique_lock<mutex> > guard;
76 0 : detail::interruption_checker check_for_interruption(&internal_mutex,&cond);
77 0 : pthread_mutex_t* the_mutex = &internal_mutex;
78 0 : guard.activate(m);
79 0 : res = posix::pthread_cond_wait(&cond,the_mutex);
80 0 : check_for_interruption.unlock_if_locked();
81 0 : guard.deactivate();
82 : #else
83 : pthread_mutex_t* the_mutex = m.mutex()->native_handle();
84 : res = posix::pthread_cond_wait(&cond,the_mutex);
85 : #endif
86 : }
87 : #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS
88 0 : this_thread::interruption_point();
89 : #endif
90 0 : if(res)
91 : {
92 0 : boost::throw_exception(condition_error(res, "boost::condition_variable::wait failed in pthread_cond_wait"));
93 : }
94 0 : }
95 :
96 : // When this function returns true:
97 : // * A notification (or sometimes a spurious OS signal) has been received
98 : // * Do not assume that the timeout has not been reached
99 : // * Do not assume that the predicate has been changed
100 : //
101 : // When this function returns false:
102 : // * The timeout has been reached
103 : // * Do not assume that a notification has not been received
104 : // * Do not assume that the predicate has not been changed
105 : inline bool condition_variable::do_wait_until(
106 : unique_lock<mutex>& m,
107 : detail::internal_platform_timepoint const &timeout)
108 : {
109 : #if defined BOOST_THREAD_THROW_IF_PRECONDITION_NOT_SATISFIED
110 : if (!m.owns_lock())
111 : {
112 : boost::throw_exception(condition_error(EPERM, "boost::condition_variable::do_wait_until() failed precondition mutex not owned"));
113 : }
114 : #endif
115 : int cond_res;
116 : {
117 : #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS
118 : thread_cv_detail::lock_on_exit<unique_lock<mutex> > guard;
119 : detail::interruption_checker check_for_interruption(&internal_mutex,&cond);
120 : pthread_mutex_t* the_mutex = &internal_mutex;
121 : guard.activate(m);
122 : cond_res=posix::pthread_cond_timedwait(&cond,the_mutex,&timeout.getTs());
123 : check_for_interruption.unlock_if_locked();
124 : guard.deactivate();
125 : #else
126 : pthread_mutex_t* the_mutex = m.mutex()->native_handle();
127 : cond_res=posix::pthread_cond_timedwait(&cond,the_mutex,&timeout.getTs());
128 : #endif
129 : }
130 : #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS
131 : this_thread::interruption_point();
132 : #endif
133 : if(cond_res==ETIMEDOUT)
134 : {
135 : return false;
136 : }
137 : if(cond_res)
138 : {
139 : boost::throw_exception(condition_error(cond_res, "boost::condition_variable::do_wait_until failed in pthread_cond_timedwait"));
140 : }
141 : return true;
142 : }
143 :
144 : inline void condition_variable::notify_one() BOOST_NOEXCEPT
145 : {
146 : #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS
147 : boost::pthread::pthread_mutex_scoped_lock internal_lock(&internal_mutex);
148 : #endif
149 : BOOST_VERIFY(!posix::pthread_cond_signal(&cond));
150 : }
151 :
152 : inline void condition_variable::notify_all() BOOST_NOEXCEPT
153 : {
154 : #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS
155 : boost::pthread::pthread_mutex_scoped_lock internal_lock(&internal_mutex);
156 : #endif
157 : BOOST_VERIFY(!posix::pthread_cond_broadcast(&cond));
158 : }
159 :
160 : class condition_variable_any
161 : {
162 : pthread_mutex_t internal_mutex;
163 : pthread_cond_t cond;
164 :
165 : public:
166 : BOOST_THREAD_NO_COPYABLE(condition_variable_any)
167 0 : condition_variable_any()
168 0 : {
169 0 : int const res=posix::pthread_mutex_init(&internal_mutex);
170 0 : if(res)
171 : {
172 0 : boost::throw_exception(thread_resource_error(res, "boost::condition_variable_any::condition_variable_any() failed in pthread_mutex_init"));
173 : }
174 0 : int const res2 = posix::pthread_cond_init(&cond);
175 0 : if(res2)
176 : {
177 0 : BOOST_VERIFY(!posix::pthread_mutex_destroy(&internal_mutex));
178 0 : boost::throw_exception(thread_resource_error(res2, "boost::condition_variable_any::condition_variable_any() failed in pthread_cond_init"));
179 : }
180 0 : }
181 0 : ~condition_variable_any()
182 0 : {
183 0 : BOOST_VERIFY(!posix::pthread_mutex_destroy(&internal_mutex));
184 0 : BOOST_VERIFY(!posix::pthread_cond_destroy(&cond));
185 0 : }
186 :
187 : template<typename lock_type>
188 : void wait(lock_type& m)
189 : {
190 : int res=0;
191 : {
192 : thread_cv_detail::lock_on_exit<lock_type> guard;
193 : #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS
194 : detail::interruption_checker check_for_interruption(&internal_mutex,&cond);
195 : #else
196 : boost::pthread::pthread_mutex_scoped_lock check_for_interruption(&internal_mutex);
197 : #endif
198 : guard.activate(m);
199 : res=posix::pthread_cond_wait(&cond,&internal_mutex);
200 : check_for_interruption.unlock_if_locked();
201 : guard.deactivate();
202 : }
203 : #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS
204 : this_thread::interruption_point();
205 : #endif
206 : if(res)
207 : {
208 : boost::throw_exception(condition_error(res, "boost::condition_variable_any::wait() failed in pthread_cond_wait"));
209 : }
210 : }
211 :
212 : template<typename lock_type,typename predicate_type>
213 : void wait(lock_type& m,predicate_type pred)
214 : {
215 : while (!pred())
216 : {
217 : wait(m);
218 : }
219 : }
220 :
221 : #if defined BOOST_THREAD_USES_DATETIME
222 : template<typename lock_type>
223 : bool timed_wait(lock_type& m,boost::system_time const& abs_time)
224 : {
225 : #if defined BOOST_THREAD_WAIT_BUG
226 : const detail::real_platform_timepoint ts(abs_time + BOOST_THREAD_WAIT_BUG);
227 : #else
228 : const detail::real_platform_timepoint ts(abs_time);
229 : #endif
230 : #if defined BOOST_THREAD_INTERNAL_CLOCK_IS_MONO
231 : // The system time may jump while this function is waiting. To compensate for this and time
232 : // out near the correct time, we could call do_wait_until() in a loop with a short timeout
233 : // and recheck the time remaining each time through the loop. However, because we can't
234 : // check the predicate each time do_wait_until() completes, this introduces the possibility
235 : // of not exiting the function when a notification occurs, since do_wait_until() may report
236 : // that it timed out even though a notification was received. The best this function can do
237 : // is report correctly whether or not it reached the timeout time.
238 : const detail::platform_duration d(ts - detail::real_platform_clock::now());
239 : do_wait_until(m, detail::internal_platform_clock::now() + d);
240 : return ts > detail::real_platform_clock::now();
241 : #else
242 : return do_wait_until(m, ts);
243 : #endif
244 : }
245 : template<typename lock_type>
246 : bool timed_wait(lock_type& m,::boost::xtime const& abs_time)
247 : {
248 : return timed_wait(m,system_time(abs_time));
249 : }
250 :
251 : template<typename lock_type,typename duration_type>
252 : bool timed_wait(lock_type& m,duration_type const& wait_duration)
253 : {
254 : if (wait_duration.is_pos_infinity())
255 : {
256 : wait(m);
257 : return true;
258 : }
259 : if (wait_duration.is_special())
260 : {
261 : return true;
262 : }
263 : detail::platform_duration d(wait_duration);
264 : #if defined(BOOST_THREAD_HAS_MONO_CLOCK) && !defined(BOOST_THREAD_INTERNAL_CLOCK_IS_MONO)
265 : // The system time may jump while this function is waiting. To compensate for this and time
266 : // out near the correct time, we could call do_wait_until() in a loop with a short timeout
267 : // and recheck the time remaining each time through the loop. However, because we can't
268 : // check the predicate each time do_wait_until() completes, this introduces the possibility
269 : // of not exiting the function when a notification occurs, since do_wait_until() may report
270 : // that it timed out even though a notification was received. The best this function can do
271 : // is report correctly whether or not it reached the timeout time.
272 : const detail::mono_platform_timepoint ts(detail::mono_platform_clock::now() + d);
273 : do_wait_until(m, detail::internal_platform_clock::now() + d);
274 : return ts > detail::mono_platform_clock::now();
275 : #else
276 : return do_wait_until(m, detail::internal_platform_clock::now() + d);
277 : #endif
278 : }
279 :
280 : template<typename lock_type,typename predicate_type>
281 : bool timed_wait(lock_type& m,boost::system_time const& abs_time, predicate_type pred)
282 : {
283 : #if defined BOOST_THREAD_WAIT_BUG
284 : const detail::real_platform_timepoint ts(abs_time + BOOST_THREAD_WAIT_BUG);
285 : #else
286 : const detail::real_platform_timepoint ts(abs_time);
287 : #endif
288 : while (!pred())
289 : {
290 : #if defined BOOST_THREAD_INTERNAL_CLOCK_IS_MONO
291 : // The system time may jump while this function is waiting. To compensate for this
292 : // and time out near the correct time, we call do_wait_until() in a loop with a
293 : // short timeout and recheck the time remaining each time through the loop.
294 : detail::platform_duration d(ts - detail::real_platform_clock::now());
295 : if (d <= detail::platform_duration::zero()) break; // timeout occurred
296 : d = (std::min)(d, detail::platform_milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS));
297 : do_wait_until(m, detail::internal_platform_clock::now() + d);
298 : #else
299 : if (!do_wait_until(m, ts)) break; // timeout occurred
300 : #endif
301 : }
302 : return pred();
303 : }
304 :
305 : template<typename lock_type,typename predicate_type>
306 : bool timed_wait(lock_type& m,::boost::xtime const& abs_time, predicate_type pred)
307 : {
308 : return timed_wait(m,system_time(abs_time),pred);
309 : }
310 :
311 : template<typename lock_type,typename duration_type,typename predicate_type>
312 : bool timed_wait(lock_type& m,duration_type const& wait_duration,predicate_type pred)
313 : {
314 : if (wait_duration.is_pos_infinity())
315 : {
316 : while (!pred())
317 : {
318 : wait(m);
319 : }
320 : return true;
321 : }
322 : if (wait_duration.is_special())
323 : {
324 : return pred();
325 : }
326 : detail::platform_duration d(wait_duration);
327 : #if defined(BOOST_THREAD_HAS_MONO_CLOCK) && !defined(BOOST_THREAD_INTERNAL_CLOCK_IS_MONO)
328 : // The system time may jump while this function is waiting. To compensate for this
329 : // and time out near the correct time, we call do_wait_until() in a loop with a
330 : // short timeout and recheck the time remaining each time through the loop.
331 : const detail::mono_platform_timepoint ts(detail::mono_platform_clock::now() + d);
332 : while (!pred())
333 : {
334 : if (d <= detail::platform_duration::zero()) break; // timeout occurred
335 : d = (std::min)(d, detail::platform_milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS));
336 : do_wait_until(m, detail::internal_platform_clock::now() + d);
337 : d = ts - detail::mono_platform_clock::now();
338 : }
339 : #else
340 : const detail::internal_platform_timepoint ts(detail::internal_platform_clock::now() + d);
341 : while (!pred())
342 : {
343 : if (!do_wait_until(m, ts)) break; // timeout occurred
344 : }
345 : #endif
346 : return pred();
347 : }
348 : #endif
349 :
350 : #ifdef BOOST_THREAD_USES_CHRONO
351 : template <class lock_type,class Duration>
352 : cv_status
353 : wait_until(
354 : lock_type& lock,
355 : const chrono::time_point<detail::internal_chrono_clock, Duration>& t)
356 : {
357 : const boost::detail::internal_platform_timepoint ts(t);
358 : if (do_wait_until(lock, ts)) return cv_status::no_timeout;
359 : else return cv_status::timeout;
360 : }
361 :
362 : template <class lock_type, class Clock, class Duration>
363 : cv_status
364 : wait_until(
365 : lock_type& lock,
366 : const chrono::time_point<Clock, Duration>& t)
367 : {
368 : // The system time may jump while this function is waiting. To compensate for this and time
369 : // out near the correct time, we could call do_wait_until() in a loop with a short timeout
370 : // and recheck the time remaining each time through the loop. However, because we can't
371 : // check the predicate each time do_wait_until() completes, this introduces the possibility
372 : // of not exiting the function when a notification occurs, since do_wait_until() may report
373 : // that it timed out even though a notification was received. The best this function can do
374 : // is report correctly whether or not it reached the timeout time.
375 : typedef typename common_type<Duration, typename Clock::duration>::type common_duration;
376 : common_duration d(t - Clock::now());
377 : do_wait_until(lock, detail::internal_chrono_clock::now() + d);
378 : if (t > Clock::now()) return cv_status::no_timeout;
379 : else return cv_status::timeout;
380 : }
381 :
382 : template <class lock_type, class Rep, class Period>
383 : cv_status
384 : wait_for(
385 : lock_type& lock,
386 : const chrono::duration<Rep, Period>& d)
387 : {
388 : return wait_until(lock, chrono::steady_clock::now() + d);
389 : }
390 :
391 : template <class lock_type, class Duration, class Predicate>
392 : bool
393 : wait_until(
394 : lock_type& lock,
395 : const chrono::time_point<detail::internal_chrono_clock, Duration>& t,
396 : Predicate pred)
397 : {
398 : const detail::internal_platform_timepoint ts(t);
399 : while (!pred())
400 : {
401 : if (!do_wait_until(lock, ts)) break; // timeout occurred
402 : }
403 : return pred();
404 : }
405 :
406 : template <class lock_type, class Clock, class Duration, class Predicate>
407 : bool
408 : wait_until(
409 : lock_type& lock,
410 : const chrono::time_point<Clock, Duration>& t,
411 : Predicate pred)
412 : {
413 : // The system time may jump while this function is waiting. To compensate for this
414 : // and time out near the correct time, we call do_wait_until() in a loop with a
415 : // short timeout and recheck the time remaining each time through the loop.
416 : typedef typename common_type<Duration, typename Clock::duration>::type common_duration;
417 : while (!pred())
418 : {
419 : common_duration d(t - Clock::now());
420 : if (d <= common_duration::zero()) break; // timeout occurred
421 : d = (std::min)(d, common_duration(chrono::milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS)));
422 : do_wait_until(lock, detail::internal_platform_clock::now() + detail::platform_duration(d));
423 : }
424 : return pred();
425 : }
426 :
427 : template <class lock_type, class Rep, class Period, class Predicate>
428 : bool
429 : wait_for(
430 : lock_type& lock,
431 : const chrono::duration<Rep, Period>& d,
432 : Predicate pred)
433 : {
434 : return wait_until(lock, chrono::steady_clock::now() + d, boost::move(pred));
435 : }
436 : #endif
437 :
438 : void notify_one() BOOST_NOEXCEPT
439 : {
440 : boost::pthread::pthread_mutex_scoped_lock internal_lock(&internal_mutex);
441 : BOOST_VERIFY(!posix::pthread_cond_signal(&cond));
442 : }
443 :
444 0 : void notify_all() BOOST_NOEXCEPT
445 : {
446 0 : boost::pthread::pthread_mutex_scoped_lock internal_lock(&internal_mutex);
447 0 : BOOST_VERIFY(!posix::pthread_cond_broadcast(&cond));
448 0 : }
449 : private:
450 :
451 : // When this function returns true:
452 : // * A notification (or sometimes a spurious OS signal) has been received
453 : // * Do not assume that the timeout has not been reached
454 : // * Do not assume that the predicate has been changed
455 : //
456 : // When this function returns false:
457 : // * The timeout has been reached
458 : // * Do not assume that a notification has not been received
459 : // * Do not assume that the predicate has not been changed
460 : template <class lock_type>
461 : bool do_wait_until(
462 : lock_type& m,
463 : detail::internal_platform_timepoint const &timeout)
464 : {
465 : int res=0;
466 : {
467 : thread_cv_detail::lock_on_exit<lock_type> guard;
468 : #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS
469 : detail::interruption_checker check_for_interruption(&internal_mutex,&cond);
470 : #else
471 : boost::pthread::pthread_mutex_scoped_lock check_for_interruption(&internal_mutex);
472 : #endif
473 : guard.activate(m);
474 : res=posix::pthread_cond_timedwait(&cond,&internal_mutex,&timeout.getTs());
475 : check_for_interruption.unlock_if_locked();
476 : guard.deactivate();
477 : }
478 : #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS
479 : this_thread::interruption_point();
480 : #endif
481 : if(res==ETIMEDOUT)
482 : {
483 : return false;
484 : }
485 : if(res)
486 : {
487 : boost::throw_exception(condition_error(res, "boost::condition_variable_any::do_wait_until() failed in pthread_cond_timedwait"));
488 : }
489 : return true;
490 : }
491 : };
492 : }
493 :
494 : #include <boost/config/abi_suffix.hpp>
495 :
496 : #endif
|