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