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 wait_queue_t wait_queue_{}; 49 detail::spinlock wait_queue_splk_{}; 50 51 public: 52 condition_variable_any() = default; 53 ~condition_variable_any()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 > wait(LockType & lt)66 void wait( LockType & lt) { 67 context * 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( ! ctx->wait_is_linked() ); 72 ctx->wait_link( wait_queue_); 73 // unlock external lt 74 lt.unlock(); 75 // suspend this fiber 76 ctx->suspend( lk); 77 // relock local lk 78 lk.lock(); 79 // remove from waiting-queue 80 ctx->wait_unlink(); 81 // unlock local lk 82 lk.unlock(); 83 // relock external again before returning 84 try { 85 lt.lock(); 86 } catch (...) { 87 std::terminate(); 88 } 89 // post-conditions 90 BOOST_ASSERT( ! 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 cv_status status = cv_status::no_timeout; 103 std::chrono::steady_clock::time_point timeout_time( 104 detail::convert( timeout_time_) ); 105 context * ctx = context::active(); 106 // atomically call lt.unlock() and block on *this 107 // store this fiber in waiting-queue 108 detail::spinlock_lock lk( wait_queue_splk_); 109 BOOST_ASSERT( ! ctx->wait_is_linked() ); 110 ctx->wait_link( wait_queue_); 111 // unlock external lt 112 lt.unlock(); 113 // suspend this fiber 114 if ( ! ctx->wait_until( timeout_time, lk) ) { 115 status = cv_status::timeout; 116 } 117 // relock local lk 118 lk.lock(); 119 // remove from waiting-queue 120 ctx->wait_unlink(); 121 // unlock local lk 122 lk.unlock(); 123 // relock external again before returning 124 try { 125 lt.lock(); 126 } catch (...) { 127 std::terminate(); 128 } 129 // post-conditions 130 BOOST_ASSERT( ! ctx->wait_is_linked() ); 131 return status; 132 } 133 134 template< typename LockType, typename Clock, typename Duration, typename Pred > wait_until(LockType & lt,std::chrono::time_point<Clock,Duration> const & timeout_time,Pred pred)135 bool wait_until( LockType & lt, 136 std::chrono::time_point< Clock, Duration > const& timeout_time, Pred pred) { 137 while ( ! pred() ) { 138 if ( cv_status::timeout == wait_until( lt, timeout_time) ) { 139 return pred(); 140 } 141 } 142 return true; 143 } 144 145 template< typename LockType, typename Rep, typename Period > wait_for(LockType & lt,std::chrono::duration<Rep,Period> const & timeout_duration)146 cv_status wait_for( LockType & lt, std::chrono::duration< Rep, Period > const& timeout_duration) { 147 return wait_until( lt, 148 std::chrono::steady_clock::now() + timeout_duration); 149 } 150 151 template< typename LockType, typename Rep, typename Period, typename Pred > wait_for(LockType & lt,std::chrono::duration<Rep,Period> const & timeout_duration,Pred pred)152 bool wait_for( LockType & lt, std::chrono::duration< Rep, Period > const& timeout_duration, Pred pred) { 153 return wait_until( lt, 154 std::chrono::steady_clock::now() + timeout_duration, 155 pred); 156 } 157 }; 158 159 class BOOST_FIBERS_DECL condition_variable { 160 private: 161 condition_variable_any cnd_; 162 163 public: 164 condition_variable() = default; 165 166 condition_variable( condition_variable const&) = delete; 167 condition_variable & operator=( condition_variable const&) = delete; 168 notify_one()169 void notify_one() noexcept { 170 cnd_.notify_one(); 171 } 172 notify_all()173 void notify_all() noexcept { 174 cnd_.notify_all(); 175 } 176 wait(std::unique_lock<mutex> & lt)177 void wait( std::unique_lock< mutex > & lt) { 178 // pre-condition 179 BOOST_ASSERT( lt.owns_lock() ); 180 BOOST_ASSERT( context::active() == lt.mutex()->owner_); 181 cnd_.wait( lt); 182 // post-condition 183 BOOST_ASSERT( lt.owns_lock() ); 184 BOOST_ASSERT( context::active() == lt.mutex()->owner_); 185 } 186 187 template< typename Pred > wait(std::unique_lock<mutex> & lt,Pred pred)188 void wait( std::unique_lock< mutex > & lt, Pred pred) { 189 // pre-condition 190 BOOST_ASSERT( lt.owns_lock() ); 191 BOOST_ASSERT( context::active() == lt.mutex()->owner_); 192 cnd_.wait( lt, pred); 193 // post-condition 194 BOOST_ASSERT( lt.owns_lock() ); 195 BOOST_ASSERT( context::active() == lt.mutex()->owner_); 196 } 197 198 template< typename Clock, typename Duration > wait_until(std::unique_lock<mutex> & lt,std::chrono::time_point<Clock,Duration> const & timeout_time)199 cv_status wait_until( std::unique_lock< mutex > & lt, 200 std::chrono::time_point< Clock, Duration > const& timeout_time) { 201 // pre-condition 202 BOOST_ASSERT( lt.owns_lock() ); 203 BOOST_ASSERT( context::active() == lt.mutex()->owner_); 204 cv_status result = cnd_.wait_until( lt, timeout_time); 205 // post-condition 206 BOOST_ASSERT( lt.owns_lock() ); 207 BOOST_ASSERT( context::active() == lt.mutex()->owner_); 208 return result; 209 } 210 211 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)212 bool wait_until( std::unique_lock< mutex > & lt, 213 std::chrono::time_point< Clock, Duration > const& timeout_time, Pred pred) { 214 // pre-condition 215 BOOST_ASSERT( lt.owns_lock() ); 216 BOOST_ASSERT( context::active() == lt.mutex()->owner_); 217 bool result = cnd_.wait_until( lt, timeout_time, pred); 218 // post-condition 219 BOOST_ASSERT( lt.owns_lock() ); 220 BOOST_ASSERT( context::active() == lt.mutex()->owner_); 221 return result; 222 } 223 224 template< typename Rep, typename Period > wait_for(std::unique_lock<mutex> & lt,std::chrono::duration<Rep,Period> const & timeout_duration)225 cv_status wait_for( std::unique_lock< mutex > & lt, 226 std::chrono::duration< Rep, Period > const& timeout_duration) { 227 // pre-condition 228 BOOST_ASSERT( lt.owns_lock() ); 229 BOOST_ASSERT( context::active() == lt.mutex()->owner_); 230 cv_status result = cnd_.wait_for( lt, timeout_duration); 231 // post-condition 232 BOOST_ASSERT( lt.owns_lock() ); 233 BOOST_ASSERT( context::active() == lt.mutex()->owner_); 234 return result; 235 } 236 237 template< typename Rep, typename Period, typename Pred > wait_for(std::unique_lock<mutex> & lt,std::chrono::duration<Rep,Period> const & timeout_duration,Pred pred)238 bool wait_for( std::unique_lock< mutex > & lt, 239 std::chrono::duration< Rep, Period > const& timeout_duration, Pred pred) { 240 // pre-condition 241 BOOST_ASSERT( lt.owns_lock() ); 242 BOOST_ASSERT( context::active() == lt.mutex()->owner_); 243 bool result = cnd_.wait_for( lt, timeout_duration, pred); 244 // post-condition 245 BOOST_ASSERT( lt.owns_lock() ); 246 BOOST_ASSERT( context::active() == lt.mutex()->owner_); 247 return result; 248 } 249 }; 250 251 }} 252 253 #ifdef _MSC_VER 254 # pragma warning(pop) 255 #endif 256 257 #ifdef BOOST_HAS_ABI_HEADERS 258 # include BOOST_ABI_SUFFIX 259 #endif 260 261 #endif // BOOST_FIBERS_CONDITION_VARIABLE_H 262