condition_variable.hpp 19 KB

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