• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
lock_on_exitboost::thread_cv_detail::lock_on_exit37             lock_on_exit():
38                 m(0)
39             {}
40 
activateboost::thread_cv_detail::lock_on_exit41             void activate(MutexType& m_)
42             {
43                 m_.unlock();
44                 m=&m_;
45             }
deactivateboost::thread_cv_detail::lock_on_exit46             void deactivate()
47             {
48                 if (m)
49                 {
50                     m->lock();
51                 }
52                 m = 0;
53             }
BOOST_NOEXCEPT_IFboost::thread_cv_detail::lock_on_exit54             ~lock_on_exit() BOOST_NOEXCEPT_IF(false)
55             {
56                 if (m)
57                 {
58                     m->lock();
59                 }
60            }
61         };
62     }
63 
wait(unique_lock<mutex> & m)64     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         int res=0;
73         {
74 #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS
75             thread_cv_detail::lock_on_exit<unique_lock<mutex> > guard;
76             detail::interruption_checker check_for_interruption(&internal_mutex,&cond);
77             pthread_mutex_t* the_mutex = &internal_mutex;
78             guard.activate(m);
79             res = posix::pthread_cond_wait(&cond,the_mutex);
80             check_for_interruption.unlock_if_locked();
81             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         this_thread::interruption_point();
89 #endif
90         if(res)
91         {
92             boost::throw_exception(condition_error(res, "boost::condition_variable::wait failed in pthread_cond_wait"));
93         }
94     }
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
do_wait_until(unique_lock<mutex> & m,detail::internal_platform_timepoint const & timeout)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 
notify_one()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 
notify_all()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)
condition_variable_any()167         condition_variable_any()
168         {
169             int const res=posix::pthread_mutex_init(&internal_mutex);
170             if(res)
171             {
172                 boost::throw_exception(thread_resource_error(res, "boost::condition_variable_any::condition_variable_any() failed in pthread_mutex_init"));
173             }
174             int const res2 = posix::pthread_cond_init(&cond);
175             if(res2)
176             {
177                 BOOST_VERIFY(!posix::pthread_mutex_destroy(&internal_mutex));
178                 boost::throw_exception(thread_resource_error(res2, "boost::condition_variable_any::condition_variable_any() failed in pthread_cond_init"));
179             }
180         }
~condition_variable_any()181         ~condition_variable_any()
182         {
183             BOOST_VERIFY(!posix::pthread_mutex_destroy(&internal_mutex));
184             BOOST_VERIFY(!posix::pthread_cond_destroy(&cond));
185         }
186 
187         template<typename lock_type>
wait(lock_type & m)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>
wait(lock_type & m,predicate_type pred)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>
timed_wait(lock_type & m,boost::system_time const & abs_time)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>
timed_wait(lock_type & m,::boost::xtime const & abs_time)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>
timed_wait(lock_type & m,duration_type const & wait_duration)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>
timed_wait(lock_type & m,boost::system_time const & abs_time,predicate_type pred)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>
timed_wait(lock_type & m,::boost::xtime const & abs_time,predicate_type pred)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>
timed_wait(lock_type & m,duration_type const & wait_duration,predicate_type pred)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
wait_until(lock_type & lock,const chrono::time_point<detail::internal_chrono_clock,Duration> & t)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
wait_until(lock_type & lock,const chrono::time_point<Clock,Duration> & t)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
wait_for(lock_type & lock,const chrono::duration<Rep,Period> & d)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
wait_until(lock_type & lock,const chrono::time_point<detail::internal_chrono_clock,Duration> & t,Predicate pred)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
wait_until(lock_type & lock,const chrono::time_point<Clock,Duration> & t,Predicate pred)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
wait_for(lock_type & lock,const chrono::duration<Rep,Period> & d,Predicate pred)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 
notify_one()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 
notify_all()444         void notify_all() BOOST_NOEXCEPT
445         {
446             boost::pthread::pthread_mutex_scoped_lock internal_lock(&internal_mutex);
447             BOOST_VERIFY(!posix::pthread_cond_broadcast(&cond));
448         }
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>
do_wait_until(lock_type & m,detail::internal_platform_timepoint const & timeout)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
497