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)
7 #ifndef BOOST_FIBERS_CONDITION_VARIABLE_H
8 #define BOOST_FIBERS_CONDITION_VARIABLE_H
16 #include <boost/assert.hpp>
17 #include <boost/config.hpp>
18 #include <boost/context/detail/config.hpp>
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>
28 #ifdef BOOST_HAS_ABI_HEADERS
29 # include BOOST_ABI_PREFIX
33 # pragma warning(push)
34 //# pragma warning(disable:4251)
40 enum class cv_status {
45 class BOOST_FIBERS_DECL condition_variable_any {
47 typedef context::wait_queue_t wait_queue_t;
49 detail::spinlock wait_queue_splk_{};
50 wait_queue_t wait_queue_{};
53 condition_variable_any() = default;
55 ~condition_variable_any() {
56 BOOST_ASSERT( wait_queue_.empty() );
59 condition_variable_any( condition_variable_any const&) = delete;
60 condition_variable_any & operator=( condition_variable_any const&) = delete;
62 void notify_one() noexcept;
64 void notify_all() noexcept;
66 template< typename LockType >
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);
78 active_ctx->suspend( lk);
79 // relock external again before returning
82 #if defined(BOOST_CONTEXT_HAS_CXXABI_H)
83 } catch ( abi::__forced_unwind const&) {
90 BOOST_ASSERT( ! active_ctx->wait_is_linked() );
93 template< typename LockType, typename Pred >
94 void wait( LockType & lt, Pred pred) {
100 template< typename LockType, typename Clock, typename Duration >
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
113 // suspend this fiber
114 if ( ! active_ctx->wait_until( timeout_time, lk) ) {
115 status = cv_status::timeout;
118 // remove from waiting-queue
119 wait_queue_.remove( * active_ctx);
123 // relock external again before returning
126 #if defined(BOOST_CONTEXT_HAS_CXXABI_H)
127 } catch ( abi::__forced_unwind const&) {
134 BOOST_ASSERT( ! active_ctx->wait_is_linked() );
138 template< typename LockType, typename Clock, typename Duration, typename Pred >
139 bool wait_until( LockType & lt,
140 std::chrono::time_point< Clock, Duration > const& timeout_time, Pred pred) {
142 if ( cv_status::timeout == wait_until( lt, timeout_time) ) {
149 template< typename LockType, typename Rep, typename Period >
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);
155 template< typename LockType, typename Rep, typename Period, typename 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,
163 class BOOST_FIBERS_DECL condition_variable {
165 condition_variable_any cnd_;
168 condition_variable() = default;
170 condition_variable( condition_variable const&) = delete;
171 condition_variable & operator=( condition_variable const&) = delete;
173 void notify_one() noexcept {
177 void notify_all() noexcept {
181 void wait( std::unique_lock< mutex > & lt) {
183 BOOST_ASSERT( lt.owns_lock() );
184 BOOST_ASSERT( context::active() == lt.mutex()->owner_);
187 BOOST_ASSERT( lt.owns_lock() );
188 BOOST_ASSERT( context::active() == lt.mutex()->owner_);
191 template< typename Pred >
192 void wait( std::unique_lock< mutex > & lt, Pred pred) {
194 BOOST_ASSERT( lt.owns_lock() );
195 BOOST_ASSERT( context::active() == lt.mutex()->owner_);
196 cnd_.wait( lt, pred);
198 BOOST_ASSERT( lt.owns_lock() );
199 BOOST_ASSERT( context::active() == lt.mutex()->owner_);
202 template< typename Clock, typename Duration >
203 cv_status wait_until( std::unique_lock< mutex > & lt,
204 std::chrono::time_point< Clock, Duration > const& timeout_time) {
206 BOOST_ASSERT( lt.owns_lock() );
207 BOOST_ASSERT( context::active() == lt.mutex()->owner_);
208 cv_status result = cnd_.wait_until( lt, timeout_time);
210 BOOST_ASSERT( lt.owns_lock() );
211 BOOST_ASSERT( context::active() == lt.mutex()->owner_);
215 template< typename Clock, typename Duration, typename Pred >
216 bool wait_until( std::unique_lock< mutex > & lt,
217 std::chrono::time_point< Clock, Duration > const& timeout_time, Pred pred) {
219 BOOST_ASSERT( lt.owns_lock() );
220 BOOST_ASSERT( context::active() == lt.mutex()->owner_);
221 bool result = cnd_.wait_until( lt, timeout_time, pred);
223 BOOST_ASSERT( lt.owns_lock() );
224 BOOST_ASSERT( context::active() == lt.mutex()->owner_);
228 template< typename Rep, typename Period >
229 cv_status wait_for( std::unique_lock< mutex > & lt,
230 std::chrono::duration< Rep, Period > const& timeout_duration) {
232 BOOST_ASSERT( lt.owns_lock() );
233 BOOST_ASSERT( context::active() == lt.mutex()->owner_);
234 cv_status result = cnd_.wait_for( lt, timeout_duration);
236 BOOST_ASSERT( lt.owns_lock() );
237 BOOST_ASSERT( context::active() == lt.mutex()->owner_);
241 template< typename Rep, typename Period, typename Pred >
242 bool wait_for( std::unique_lock< mutex > & lt,
243 std::chrono::duration< Rep, Period > const& timeout_duration, Pred pred) {
245 BOOST_ASSERT( lt.owns_lock() );
246 BOOST_ASSERT( context::active() == lt.mutex()->owner_);
247 bool result = cnd_.wait_for( lt, timeout_duration, pred);
249 BOOST_ASSERT( lt.owns_lock() );
250 BOOST_ASSERT( context::active() == lt.mutex()->owner_);
258 # pragma warning(pop)
261 #ifdef BOOST_HAS_ABI_HEADERS
262 # include BOOST_ABI_SUFFIX
265 #endif // BOOST_FIBERS_CONDITION_VARIABLE_H