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/pthread/thread_data.hpp> 15 #endif 16 #include <boost/thread/pthread/condition_variable_fwd.hpp> 17 #ifdef BOOST_THREAD_USES_CHRONO 18 #include <boost/chrono/system_clocks.hpp> 19 #include <boost/chrono/ceil.hpp> 20 #endif 21 #include <boost/thread/detail/delete.hpp> 22 23 #include <algorithm> 24 25 #include <boost/config/abi_prefix.hpp> 26 27 namespace boost 28 { 29 #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS 30 namespace this_thread 31 { 32 void BOOST_THREAD_DECL interruption_point(); 33 } 34 #endif 35 36 namespace thread_cv_detail 37 { 38 template<typename MutexType> 39 struct lock_on_exit 40 { 41 MutexType* m; 42 lock_on_exitboost::thread_cv_detail::lock_on_exit43 lock_on_exit(): 44 m(0) 45 {} 46 activateboost::thread_cv_detail::lock_on_exit47 void activate(MutexType& m_) 48 { 49 m_.unlock(); 50 m=&m_; 51 } deactivateboost::thread_cv_detail::lock_on_exit52 void deactivate() 53 { 54 if (m) 55 { 56 m->lock(); 57 } 58 m = 0; 59 } BOOST_NOEXCEPT_IFboost::thread_cv_detail::lock_on_exit60 ~lock_on_exit() BOOST_NOEXCEPT_IF(false) 61 { 62 if (m) 63 { 64 m->lock(); 65 } 66 } 67 }; 68 } 69 wait(unique_lock<mutex> & m)70 inline void condition_variable::wait(unique_lock<mutex>& m) 71 { 72 #if defined BOOST_THREAD_THROW_IF_PRECONDITION_NOT_SATISFIED 73 if(! m.owns_lock()) 74 { 75 boost::throw_exception(condition_error(-1, "boost::condition_variable::wait() failed precondition mutex not owned")); 76 } 77 #endif 78 int res=0; 79 { 80 #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS 81 thread_cv_detail::lock_on_exit<unique_lock<mutex> > guard; 82 detail::interruption_checker check_for_interruption(&internal_mutex,&cond); 83 pthread_mutex_t* the_mutex = &internal_mutex; 84 guard.activate(m); 85 res = pthread_cond_wait(&cond,the_mutex); 86 check_for_interruption.unlock_if_locked(); 87 guard.deactivate(); 88 #else 89 pthread_mutex_t* the_mutex = m.mutex()->native_handle(); 90 res = pthread_cond_wait(&cond,the_mutex); 91 #endif 92 } 93 #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS 94 this_thread::interruption_point(); 95 #endif 96 if(res && res != EINTR) 97 { 98 boost::throw_exception(condition_error(res, "boost::condition_variable::wait failed in pthread_cond_wait")); 99 } 100 } 101 102 // When this function returns true: 103 // * A notification (or sometimes a spurious OS signal) has been received 104 // * Do not assume that the timeout has not been reached 105 // * Do not assume that the predicate has been changed 106 // 107 // When this function returns false: 108 // * The timeout has been reached 109 // * Do not assume that a notification has not been received 110 // * Do not assume that the predicate has not been changed do_wait_until(unique_lock<mutex> & m,detail::internal_platform_timepoint const & timeout)111 inline bool condition_variable::do_wait_until( 112 unique_lock<mutex>& m, 113 detail::internal_platform_timepoint const &timeout) 114 { 115 #if defined BOOST_THREAD_THROW_IF_PRECONDITION_NOT_SATISFIED 116 if (!m.owns_lock()) 117 { 118 boost::throw_exception(condition_error(EPERM, "boost::condition_variable::do_wait_until() failed precondition mutex not owned")); 119 } 120 #endif 121 int cond_res; 122 { 123 #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS 124 thread_cv_detail::lock_on_exit<unique_lock<mutex> > guard; 125 detail::interruption_checker check_for_interruption(&internal_mutex,&cond); 126 pthread_mutex_t* the_mutex = &internal_mutex; 127 guard.activate(m); 128 cond_res=pthread_cond_timedwait(&cond,the_mutex,&timeout.getTs()); 129 check_for_interruption.unlock_if_locked(); 130 guard.deactivate(); 131 #else 132 pthread_mutex_t* the_mutex = m.mutex()->native_handle(); 133 cond_res=pthread_cond_timedwait(&cond,the_mutex,&timeout.getTs()); 134 #endif 135 } 136 #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS 137 this_thread::interruption_point(); 138 #endif 139 if(cond_res==ETIMEDOUT) 140 { 141 return false; 142 } 143 if(cond_res) 144 { 145 boost::throw_exception(condition_error(cond_res, "boost::condition_variable::do_wait_until failed in pthread_cond_timedwait")); 146 } 147 return true; 148 } 149 notify_one()150 inline void condition_variable::notify_one() BOOST_NOEXCEPT 151 { 152 #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS 153 boost::pthread::pthread_mutex_scoped_lock internal_lock(&internal_mutex); 154 #endif 155 BOOST_VERIFY(!pthread_cond_signal(&cond)); 156 } 157 notify_all()158 inline void condition_variable::notify_all() BOOST_NOEXCEPT 159 { 160 #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS 161 boost::pthread::pthread_mutex_scoped_lock internal_lock(&internal_mutex); 162 #endif 163 BOOST_VERIFY(!pthread_cond_broadcast(&cond)); 164 } 165 166 class condition_variable_any 167 { 168 pthread_mutex_t internal_mutex; 169 pthread_cond_t cond; 170 171 public: 172 BOOST_THREAD_NO_COPYABLE(condition_variable_any) condition_variable_any()173 condition_variable_any() 174 { 175 int const res=pthread_mutex_init(&internal_mutex,NULL); 176 if(res) 177 { 178 boost::throw_exception(thread_resource_error(res, "boost::condition_variable_any::condition_variable_any() failed in pthread_mutex_init")); 179 } 180 int const res2 = pthread::cond_init(cond); 181 if(res2) 182 { 183 BOOST_VERIFY(!pthread_mutex_destroy(&internal_mutex)); 184 boost::throw_exception(thread_resource_error(res2, "boost::condition_variable_any::condition_variable_any() failed in pthread::cond_init")); 185 } 186 } ~condition_variable_any()187 ~condition_variable_any() 188 { 189 BOOST_VERIFY(!pthread_mutex_destroy(&internal_mutex)); 190 BOOST_VERIFY(!pthread_cond_destroy(&cond)); 191 } 192 193 template<typename lock_type> wait(lock_type & m)194 void wait(lock_type& m) 195 { 196 int res=0; 197 { 198 thread_cv_detail::lock_on_exit<lock_type> guard; 199 #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS 200 detail::interruption_checker check_for_interruption(&internal_mutex,&cond); 201 #else 202 boost::pthread::pthread_mutex_scoped_lock check_for_interruption(&internal_mutex); 203 #endif 204 guard.activate(m); 205 res=pthread_cond_wait(&cond,&internal_mutex); 206 check_for_interruption.unlock_if_locked(); 207 guard.deactivate(); 208 } 209 #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS 210 this_thread::interruption_point(); 211 #endif 212 if(res) 213 { 214 boost::throw_exception(condition_error(res, "boost::condition_variable_any::wait() failed in pthread_cond_wait")); 215 } 216 } 217 218 template<typename lock_type,typename predicate_type> wait(lock_type & m,predicate_type pred)219 void wait(lock_type& m,predicate_type pred) 220 { 221 while (!pred()) 222 { 223 wait(m); 224 } 225 } 226 227 #if defined BOOST_THREAD_USES_DATETIME 228 template<typename lock_type> timed_wait(lock_type & m,boost::system_time const & abs_time)229 bool timed_wait(lock_type& m,boost::system_time const& abs_time) 230 { 231 #if defined BOOST_THREAD_WAIT_BUG 232 const detail::real_platform_timepoint ts(abs_time + BOOST_THREAD_WAIT_BUG); 233 #else 234 const detail::real_platform_timepoint ts(abs_time); 235 #endif 236 #if defined BOOST_THREAD_INTERNAL_CLOCK_IS_MONO 237 // The system time may jump while this function is waiting. To compensate for this and time 238 // out near the correct time, we could call do_wait_until() in a loop with a short timeout 239 // and recheck the time remaining each time through the loop. However, because we can't 240 // check the predicate each time do_wait_until() completes, this introduces the possibility 241 // of not exiting the function when a notification occurs, since do_wait_until() may report 242 // that it timed out even though a notification was received. The best this function can do 243 // is report correctly whether or not it reached the timeout time. 244 const detail::platform_duration d(ts - detail::real_platform_clock::now()); 245 do_wait_until(m, detail::internal_platform_clock::now() + d); 246 return ts > detail::real_platform_clock::now(); 247 #else 248 return do_wait_until(m, ts); 249 #endif 250 } 251 template<typename lock_type> timed_wait(lock_type & m,::boost::xtime const & abs_time)252 bool timed_wait(lock_type& m,::boost::xtime const& abs_time) 253 { 254 return timed_wait(m,system_time(abs_time)); 255 } 256 257 template<typename lock_type,typename duration_type> timed_wait(lock_type & m,duration_type const & wait_duration)258 bool timed_wait(lock_type& m,duration_type const& wait_duration) 259 { 260 if (wait_duration.is_pos_infinity()) 261 { 262 wait(m); 263 return true; 264 } 265 if (wait_duration.is_special()) 266 { 267 return true; 268 } 269 detail::platform_duration d(wait_duration); 270 #if defined(BOOST_THREAD_HAS_MONO_CLOCK) && !defined(BOOST_THREAD_INTERNAL_CLOCK_IS_MONO) 271 // The system time may jump while this function is waiting. To compensate for this and time 272 // out near the correct time, we could call do_wait_until() in a loop with a short timeout 273 // and recheck the time remaining each time through the loop. However, because we can't 274 // check the predicate each time do_wait_until() completes, this introduces the possibility 275 // of not exiting the function when a notification occurs, since do_wait_until() may report 276 // that it timed out even though a notification was received. The best this function can do 277 // is report correctly whether or not it reached the timeout time. 278 const detail::mono_platform_timepoint ts(detail::mono_platform_clock::now() + d); 279 do_wait_until(m, detail::internal_platform_clock::now() + d); 280 return ts > detail::mono_platform_clock::now(); 281 #else 282 return do_wait_until(m, detail::internal_platform_clock::now() + d); 283 #endif 284 } 285 286 template<typename lock_type,typename predicate_type> timed_wait(lock_type & m,boost::system_time const & abs_time,predicate_type pred)287 bool timed_wait(lock_type& m,boost::system_time const& abs_time, predicate_type pred) 288 { 289 #if defined BOOST_THREAD_WAIT_BUG 290 const detail::real_platform_timepoint ts(abs_time + BOOST_THREAD_WAIT_BUG); 291 #else 292 const detail::real_platform_timepoint ts(abs_time); 293 #endif 294 while (!pred()) 295 { 296 #if defined BOOST_THREAD_INTERNAL_CLOCK_IS_MONO 297 // The system time may jump while this function is waiting. To compensate for this 298 // and time out near the correct time, we call do_wait_until() in a loop with a 299 // short timeout and recheck the time remaining each time through the loop. 300 detail::platform_duration d(ts - detail::real_platform_clock::now()); 301 if (d <= detail::platform_duration::zero()) break; // timeout occurred 302 d = (std::min)(d, detail::platform_milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS)); 303 do_wait_until(m, detail::internal_platform_clock::now() + d); 304 #else 305 if (!do_wait_until(m, ts)) break; // timeout occurred 306 #endif 307 } 308 return pred(); 309 } 310 311 template<typename lock_type,typename predicate_type> timed_wait(lock_type & m,::boost::xtime const & abs_time,predicate_type pred)312 bool timed_wait(lock_type& m,::boost::xtime const& abs_time, predicate_type pred) 313 { 314 return timed_wait(m,system_time(abs_time),pred); 315 } 316 317 template<typename lock_type,typename duration_type,typename predicate_type> timed_wait(lock_type & m,duration_type const & wait_duration,predicate_type pred)318 bool timed_wait(lock_type& m,duration_type const& wait_duration,predicate_type pred) 319 { 320 if (wait_duration.is_pos_infinity()) 321 { 322 while (!pred()) 323 { 324 wait(m); 325 } 326 return true; 327 } 328 if (wait_duration.is_special()) 329 { 330 return pred(); 331 } 332 detail::platform_duration d(wait_duration); 333 #if defined(BOOST_THREAD_HAS_MONO_CLOCK) && !defined(BOOST_THREAD_INTERNAL_CLOCK_IS_MONO) 334 // The system time may jump while this function is waiting. To compensate for this 335 // and time out near the correct time, we call do_wait_until() in a loop with a 336 // short timeout and recheck the time remaining each time through the loop. 337 const detail::mono_platform_timepoint ts(detail::mono_platform_clock::now() + d); 338 while (!pred()) 339 { 340 if (d <= detail::platform_duration::zero()) break; // timeout occurred 341 d = (std::min)(d, detail::platform_milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS)); 342 do_wait_until(m, detail::internal_platform_clock::now() + d); 343 d = ts - detail::mono_platform_clock::now(); 344 } 345 #else 346 const detail::internal_platform_timepoint ts(detail::internal_platform_clock::now() + d); 347 while (!pred()) 348 { 349 if (!do_wait_until(m, ts)) break; // timeout occurred 350 } 351 #endif 352 return pred(); 353 } 354 #endif 355 356 #ifdef BOOST_THREAD_USES_CHRONO 357 template <class lock_type,class Duration> 358 cv_status wait_until(lock_type & lock,const chrono::time_point<detail::internal_chrono_clock,Duration> & t)359 wait_until( 360 lock_type& lock, 361 const chrono::time_point<detail::internal_chrono_clock, Duration>& t) 362 { 363 const boost::detail::internal_platform_timepoint ts(t); 364 if (do_wait_until(lock, ts)) return cv_status::no_timeout; 365 else return cv_status::timeout; 366 } 367 368 template <class lock_type, class Clock, class Duration> 369 cv_status wait_until(lock_type & lock,const chrono::time_point<Clock,Duration> & t)370 wait_until( 371 lock_type& lock, 372 const chrono::time_point<Clock, Duration>& t) 373 { 374 // The system time may jump while this function is waiting. To compensate for this and time 375 // out near the correct time, we could call do_wait_until() in a loop with a short timeout 376 // and recheck the time remaining each time through the loop. However, because we can't 377 // check the predicate each time do_wait_until() completes, this introduces the possibility 378 // of not exiting the function when a notification occurs, since do_wait_until() may report 379 // that it timed out even though a notification was received. The best this function can do 380 // is report correctly whether or not it reached the timeout time. 381 typedef typename common_type<Duration, typename Clock::duration>::type common_duration; 382 common_duration d(t - Clock::now()); 383 do_wait_until(lock, detail::internal_chrono_clock::now() + d); 384 if (t > Clock::now()) return cv_status::no_timeout; 385 else return cv_status::timeout; 386 } 387 388 template <class lock_type, class Rep, class Period> 389 cv_status wait_for(lock_type & lock,const chrono::duration<Rep,Period> & d)390 wait_for( 391 lock_type& lock, 392 const chrono::duration<Rep, Period>& d) 393 { 394 return wait_until(lock, chrono::steady_clock::now() + d); 395 } 396 397 template <class lock_type, class Duration, class Predicate> 398 bool wait_until(lock_type & lock,const chrono::time_point<detail::internal_chrono_clock,Duration> & t,Predicate pred)399 wait_until( 400 lock_type& lock, 401 const chrono::time_point<detail::internal_chrono_clock, Duration>& t, 402 Predicate pred) 403 { 404 const detail::internal_platform_timepoint ts(t); 405 while (!pred()) 406 { 407 if (!do_wait_until(lock, ts)) break; // timeout occurred 408 } 409 return pred(); 410 } 411 412 template <class lock_type, class Clock, class Duration, class Predicate> 413 bool wait_until(lock_type & lock,const chrono::time_point<Clock,Duration> & t,Predicate pred)414 wait_until( 415 lock_type& lock, 416 const chrono::time_point<Clock, Duration>& t, 417 Predicate pred) 418 { 419 // The system time may jump while this function is waiting. To compensate for this 420 // and time out near the correct time, we call do_wait_until() in a loop with a 421 // short timeout and recheck the time remaining each time through the loop. 422 typedef typename common_type<Duration, typename Clock::duration>::type common_duration; 423 while (!pred()) 424 { 425 common_duration d(t - Clock::now()); 426 if (d <= common_duration::zero()) break; // timeout occurred 427 d = (std::min)(d, common_duration(chrono::milliseconds(BOOST_THREAD_POLL_INTERVAL_MILLISECONDS))); 428 do_wait_until(lock, detail::internal_platform_clock::now() + detail::platform_duration(d)); 429 } 430 return pred(); 431 } 432 433 template <class lock_type, class Rep, class Period, class Predicate> 434 bool wait_for(lock_type & lock,const chrono::duration<Rep,Period> & d,Predicate pred)435 wait_for( 436 lock_type& lock, 437 const chrono::duration<Rep, Period>& d, 438 Predicate pred) 439 { 440 return wait_until(lock, chrono::steady_clock::now() + d, boost::move(pred)); 441 } 442 #endif 443 notify_one()444 void notify_one() BOOST_NOEXCEPT 445 { 446 boost::pthread::pthread_mutex_scoped_lock internal_lock(&internal_mutex); 447 BOOST_VERIFY(!pthread_cond_signal(&cond)); 448 } 449 notify_all()450 void notify_all() BOOST_NOEXCEPT 451 { 452 boost::pthread::pthread_mutex_scoped_lock internal_lock(&internal_mutex); 453 BOOST_VERIFY(!pthread_cond_broadcast(&cond)); 454 } 455 private: 456 457 // When this function returns true: 458 // * A notification (or sometimes a spurious OS signal) has been received 459 // * Do not assume that the timeout has not been reached 460 // * Do not assume that the predicate has been changed 461 // 462 // When this function returns false: 463 // * The timeout has been reached 464 // * Do not assume that a notification has not been received 465 // * Do not assume that the predicate has not been changed 466 template <class lock_type> do_wait_until(lock_type & m,detail::internal_platform_timepoint const & timeout)467 bool do_wait_until( 468 lock_type& m, 469 detail::internal_platform_timepoint const &timeout) 470 { 471 int res=0; 472 { 473 thread_cv_detail::lock_on_exit<lock_type> guard; 474 #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS 475 detail::interruption_checker check_for_interruption(&internal_mutex,&cond); 476 #else 477 boost::pthread::pthread_mutex_scoped_lock check_for_interruption(&internal_mutex); 478 #endif 479 guard.activate(m); 480 res=pthread_cond_timedwait(&cond,&internal_mutex,&timeout.getTs()); 481 check_for_interruption.unlock_if_locked(); 482 guard.deactivate(); 483 } 484 #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS 485 this_thread::interruption_point(); 486 #endif 487 if(res==ETIMEDOUT) 488 { 489 return false; 490 } 491 if(res) 492 { 493 boost::throw_exception(condition_error(res, "boost::condition_variable_any::do_wait_until() failed in pthread_cond_timedwait")); 494 } 495 return true; 496 } 497 }; 498 } 499 500 #include <boost/config/abi_suffix.hpp> 501 502 #endif 503