condition_variable.hpp 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. // Copyright Oliver Kowalke 2013.
  2. // Distributed under the Boost Software License, Version 1.0.
  3. // (See accompanying file LICENSE_1_0.txt or copy at
  4. // http://www.boost.org/LICENSE_1_0.txt)
  5. #ifndef BOOST_FIBERS_CONDITION_VARIABLE_H
  6. #define BOOST_FIBERS_CONDITION_VARIABLE_H
  7. #include <algorithm>
  8. #include <atomic>
  9. #include <chrono>
  10. #include <functional>
  11. #include <mutex>
  12. #include <boost/assert.hpp>
  13. #include <boost/config.hpp>
  14. #include <boost/context/detail/config.hpp>
  15. #include <boost/fiber/context.hpp>
  16. #include <boost/fiber/detail/config.hpp>
  17. #include <boost/fiber/detail/convert.hpp>
  18. #include <boost/fiber/detail/spinlock.hpp>
  19. #include <boost/fiber/exceptions.hpp>
  20. #include <boost/fiber/mutex.hpp>
  21. #include <boost/fiber/operations.hpp>
  22. #include <boost/fiber/waker.hpp>
  23. #ifdef BOOST_HAS_ABI_HEADERS
  24. # include BOOST_ABI_PREFIX
  25. #endif
  26. #ifdef _MSC_VER
  27. # pragma warning(push)
  28. //# pragma warning(disable:4251)
  29. #endif
  30. namespace boost {
  31. namespace fibers {
  32. enum class cv_status {
  33. no_timeout = 1,
  34. timeout
  35. };
  36. class BOOST_FIBERS_DECL condition_variable_any {
  37. private:
  38. detail::spinlock wait_queue_splk_{};
  39. wait_queue wait_queue_{};
  40. public:
  41. condition_variable_any() = default;
  42. ~condition_variable_any() {
  43. BOOST_ASSERT( wait_queue_.empty() );
  44. }
  45. condition_variable_any( condition_variable_any const&) = delete;
  46. condition_variable_any & operator=( condition_variable_any const&) = delete;
  47. void notify_one() noexcept;
  48. void notify_all() noexcept;
  49. template< typename LockType >
  50. void wait( LockType & lt) {
  51. context * active_ctx = context::active();
  52. // atomically call lt.unlock() and block on *this
  53. // store this fiber in waiting-queue
  54. detail::spinlock_lock lk{ wait_queue_splk_ };
  55. lt.unlock();
  56. wait_queue_.suspend_and_wait( lk, active_ctx);
  57. // relock external again before returning
  58. try {
  59. lt.lock();
  60. #if defined(BOOST_CONTEXT_HAS_CXXABI_H)
  61. } catch ( abi::__forced_unwind const&) {
  62. throw;
  63. #endif
  64. } catch (...) {
  65. std::terminate();
  66. }
  67. }
  68. template< typename LockType, typename Pred >
  69. void wait( LockType & lt, Pred pred) {
  70. while ( ! pred() ) {
  71. wait( lt);
  72. }
  73. }
  74. template< typename LockType, typename Clock, typename Duration >
  75. cv_status wait_until( LockType & lt, std::chrono::time_point< Clock, Duration > const& timeout_time_) {
  76. context * active_ctx = context::active();
  77. cv_status status = cv_status::no_timeout;
  78. std::chrono::steady_clock::time_point timeout_time = detail::convert( timeout_time_);
  79. // atomically call lt.unlock() and block on *this
  80. // store this fiber in waiting-queue
  81. detail::spinlock_lock lk{ wait_queue_splk_ };
  82. // unlock external lt
  83. lt.unlock();
  84. if ( ! wait_queue_.suspend_and_wait_until( lk, active_ctx, timeout_time)) {
  85. status = cv_status::timeout;
  86. }
  87. // relock external again before returning
  88. try {
  89. lt.lock();
  90. #if defined(BOOST_CONTEXT_HAS_CXXABI_H)
  91. } catch ( abi::__forced_unwind const&) {
  92. throw;
  93. #endif
  94. } catch (...) {
  95. std::terminate();
  96. }
  97. return status;
  98. }
  99. template< typename LockType, typename Clock, typename Duration, typename Pred >
  100. bool wait_until( LockType & lt,
  101. std::chrono::time_point< Clock, Duration > const& timeout_time, Pred pred) {
  102. while ( ! pred() ) {
  103. if ( cv_status::timeout == wait_until( lt, timeout_time) ) {
  104. return pred();
  105. }
  106. }
  107. return true;
  108. }
  109. template< typename LockType, typename Rep, typename Period >
  110. cv_status wait_for( LockType & lt, std::chrono::duration< Rep, Period > const& timeout_duration) {
  111. return wait_until( lt,
  112. std::chrono::steady_clock::now() + timeout_duration);
  113. }
  114. template< typename LockType, typename Rep, typename Period, typename Pred >
  115. bool wait_for( LockType & lt, std::chrono::duration< Rep, Period > const& timeout_duration, Pred pred) {
  116. return wait_until( lt,
  117. std::chrono::steady_clock::now() + timeout_duration,
  118. pred);
  119. }
  120. };
  121. class BOOST_FIBERS_DECL condition_variable {
  122. private:
  123. condition_variable_any cnd_;
  124. public:
  125. condition_variable() = default;
  126. condition_variable( condition_variable const&) = delete;
  127. condition_variable & operator=( condition_variable const&) = delete;
  128. void notify_one() noexcept {
  129. cnd_.notify_one();
  130. }
  131. void notify_all() noexcept {
  132. cnd_.notify_all();
  133. }
  134. void wait( std::unique_lock< mutex > & lt) {
  135. // pre-condition
  136. BOOST_ASSERT( lt.owns_lock() );
  137. BOOST_ASSERT( context::active() == lt.mutex()->owner_);
  138. cnd_.wait( lt);
  139. // post-condition
  140. BOOST_ASSERT( lt.owns_lock() );
  141. BOOST_ASSERT( context::active() == lt.mutex()->owner_);
  142. }
  143. template< typename Pred >
  144. void wait( std::unique_lock< mutex > & lt, Pred pred) {
  145. // pre-condition
  146. BOOST_ASSERT( lt.owns_lock() );
  147. BOOST_ASSERT( context::active() == lt.mutex()->owner_);
  148. cnd_.wait( lt, pred);
  149. // post-condition
  150. BOOST_ASSERT( lt.owns_lock() );
  151. BOOST_ASSERT( context::active() == lt.mutex()->owner_);
  152. }
  153. template< typename Clock, typename Duration >
  154. cv_status wait_until( std::unique_lock< mutex > & lt,
  155. std::chrono::time_point< Clock, Duration > const& timeout_time) {
  156. // pre-condition
  157. BOOST_ASSERT( lt.owns_lock() );
  158. BOOST_ASSERT( context::active() == lt.mutex()->owner_);
  159. cv_status result = cnd_.wait_until( lt, timeout_time);
  160. // post-condition
  161. BOOST_ASSERT( lt.owns_lock() );
  162. BOOST_ASSERT( context::active() == lt.mutex()->owner_);
  163. return result;
  164. }
  165. template< typename Clock, typename Duration, typename Pred >
  166. bool wait_until( std::unique_lock< mutex > & lt,
  167. std::chrono::time_point< Clock, Duration > const& timeout_time, Pred pred) {
  168. // pre-condition
  169. BOOST_ASSERT( lt.owns_lock() );
  170. BOOST_ASSERT( context::active() == lt.mutex()->owner_);
  171. bool result = cnd_.wait_until( lt, timeout_time, pred);
  172. // post-condition
  173. BOOST_ASSERT( lt.owns_lock() );
  174. BOOST_ASSERT( context::active() == lt.mutex()->owner_);
  175. return result;
  176. }
  177. template< typename Rep, typename Period >
  178. cv_status wait_for( std::unique_lock< mutex > & lt,
  179. std::chrono::duration< Rep, Period > const& timeout_duration) {
  180. // pre-condition
  181. BOOST_ASSERT( lt.owns_lock() );
  182. BOOST_ASSERT( context::active() == lt.mutex()->owner_);
  183. cv_status result = cnd_.wait_for( lt, timeout_duration);
  184. // post-condition
  185. BOOST_ASSERT( lt.owns_lock() );
  186. BOOST_ASSERT( context::active() == lt.mutex()->owner_);
  187. return result;
  188. }
  189. template< typename Rep, typename Period, typename Pred >
  190. bool wait_for( std::unique_lock< mutex > & lt,
  191. std::chrono::duration< Rep, Period > const& timeout_duration, Pred pred) {
  192. // pre-condition
  193. BOOST_ASSERT( lt.owns_lock() );
  194. BOOST_ASSERT( context::active() == lt.mutex()->owner_);
  195. bool result = cnd_.wait_for( lt, timeout_duration, pred);
  196. // post-condition
  197. BOOST_ASSERT( lt.owns_lock() );
  198. BOOST_ASSERT( context::active() == lt.mutex()->owner_);
  199. return result;
  200. }
  201. };
  202. }}
  203. #ifdef _MSC_VER
  204. # pragma warning(pop)
  205. #endif
  206. #ifdef BOOST_HAS_ABI_HEADERS
  207. # include BOOST_ABI_SUFFIX
  208. #endif
  209. #endif // BOOST_FIBERS_CONDITION_VARIABLE_H