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
23namespace 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