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