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