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