condition_variable.hpp 14 KB

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