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