// Copyright Oliver Kowalke 2013. // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #ifndef BOOST_FIBERS_CONDITION_VARIABLE_H #define BOOST_FIBERS_CONDITION_VARIABLE_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_PREFIX #endif #ifdef _MSC_VER # pragma warning(push) //# pragma warning(disable:4251) #endif namespace boost { namespace fibers { enum class cv_status { no_timeout = 1, timeout }; class BOOST_FIBERS_DECL condition_variable_any { private: typedef context::wait_queue_t wait_queue_t; wait_queue_t wait_queue_{}; detail::spinlock wait_queue_splk_{}; public: condition_variable_any() = default; ~condition_variable_any() { BOOST_ASSERT( wait_queue_.empty() ); } condition_variable_any( condition_variable_any const&) = delete; condition_variable_any & operator=( condition_variable_any const&) = delete; void notify_one() noexcept; void notify_all() noexcept; template< typename LockType > void wait( LockType & lt) { context * ctx = context::active(); // atomically call lt.unlock() and block on *this // store this fiber in waiting-queue detail::spinlock_lock lk( wait_queue_splk_); BOOST_ASSERT( ! ctx->wait_is_linked() ); ctx->wait_link( wait_queue_); // unlock external lt lt.unlock(); // suspend this fiber ctx->suspend( lk); // relock local lk lk.lock(); // remove from waiting-queue ctx->wait_unlink(); // unlock local lk lk.unlock(); // relock external again before returning try { lt.lock(); } catch (...) { std::terminate(); } // post-conditions BOOST_ASSERT( ! ctx->wait_is_linked() ); } template< typename LockType, typename Pred > void wait( LockType & lt, Pred pred) { while ( ! pred() ) { wait( lt); } } template< typename LockType, typename Clock, typename Duration > cv_status wait_until( LockType & lt, std::chrono::time_point< Clock, Duration > const& timeout_time_) { cv_status status = cv_status::no_timeout; std::chrono::steady_clock::time_point timeout_time( detail::convert( timeout_time_) ); context * ctx = context::active(); // atomically call lt.unlock() and block on *this // store this fiber in waiting-queue detail::spinlock_lock lk( wait_queue_splk_); BOOST_ASSERT( ! ctx->wait_is_linked() ); ctx->wait_link( wait_queue_); // unlock external lt lt.unlock(); // suspend this fiber if ( ! ctx->wait_until( timeout_time, lk) ) { status = cv_status::timeout; } // relock local lk lk.lock(); // remove from waiting-queue ctx->wait_unlink(); // unlock local lk lk.unlock(); // relock external again before returning try { lt.lock(); } catch (...) { std::terminate(); } // post-conditions BOOST_ASSERT( ! ctx->wait_is_linked() ); return status; } template< typename LockType, typename Clock, typename Duration, typename Pred > bool wait_until( LockType & lt, std::chrono::time_point< Clock, Duration > const& timeout_time, Pred pred) { while ( ! pred() ) { if ( cv_status::timeout == wait_until( lt, timeout_time) ) { return pred(); } } return true; } template< typename LockType, typename Rep, typename Period > cv_status wait_for( LockType & lt, std::chrono::duration< Rep, Period > const& timeout_duration) { return wait_until( lt, std::chrono::steady_clock::now() + timeout_duration); } template< typename LockType, typename Rep, typename Period, typename Pred > bool wait_for( LockType & lt, std::chrono::duration< Rep, Period > const& timeout_duration, Pred pred) { return wait_until( lt, std::chrono::steady_clock::now() + timeout_duration, pred); } }; class BOOST_FIBERS_DECL condition_variable { private: condition_variable_any cnd_; public: condition_variable() = default; condition_variable( condition_variable const&) = delete; condition_variable & operator=( condition_variable const&) = delete; void notify_one() noexcept { cnd_.notify_one(); } void notify_all() noexcept { cnd_.notify_all(); } void wait( std::unique_lock< mutex > & lt) { // pre-condition BOOST_ASSERT( lt.owns_lock() ); BOOST_ASSERT( context::active() == lt.mutex()->owner_); cnd_.wait( lt); // post-condition BOOST_ASSERT( lt.owns_lock() ); BOOST_ASSERT( context::active() == lt.mutex()->owner_); } template< typename Pred > void wait( std::unique_lock< mutex > & lt, Pred pred) { // pre-condition BOOST_ASSERT( lt.owns_lock() ); BOOST_ASSERT( context::active() == lt.mutex()->owner_); cnd_.wait( lt, pred); // post-condition BOOST_ASSERT( lt.owns_lock() ); BOOST_ASSERT( context::active() == lt.mutex()->owner_); } template< typename Clock, typename Duration > cv_status wait_until( std::unique_lock< mutex > & lt, std::chrono::time_point< Clock, Duration > const& timeout_time) { // pre-condition BOOST_ASSERT( lt.owns_lock() ); BOOST_ASSERT( context::active() == lt.mutex()->owner_); cv_status result = cnd_.wait_until( lt, timeout_time); // post-condition BOOST_ASSERT( lt.owns_lock() ); BOOST_ASSERT( context::active() == lt.mutex()->owner_); return result; } template< typename Clock, typename Duration, typename Pred > bool wait_until( std::unique_lock< mutex > & lt, std::chrono::time_point< Clock, Duration > const& timeout_time, Pred pred) { // pre-condition BOOST_ASSERT( lt.owns_lock() ); BOOST_ASSERT( context::active() == lt.mutex()->owner_); bool result = cnd_.wait_until( lt, timeout_time, pred); // post-condition BOOST_ASSERT( lt.owns_lock() ); BOOST_ASSERT( context::active() == lt.mutex()->owner_); return result; } template< typename Rep, typename Period > cv_status wait_for( std::unique_lock< mutex > & lt, std::chrono::duration< Rep, Period > const& timeout_duration) { // pre-condition BOOST_ASSERT( lt.owns_lock() ); BOOST_ASSERT( context::active() == lt.mutex()->owner_); cv_status result = cnd_.wait_for( lt, timeout_duration); // post-condition BOOST_ASSERT( lt.owns_lock() ); BOOST_ASSERT( context::active() == lt.mutex()->owner_); return result; } template< typename Rep, typename Period, typename Pred > bool wait_for( std::unique_lock< mutex > & lt, std::chrono::duration< Rep, Period > const& timeout_duration, Pred pred) { // pre-condition BOOST_ASSERT( lt.owns_lock() ); BOOST_ASSERT( context::active() == lt.mutex()->owner_); bool result = cnd_.wait_for( lt, timeout_duration, pred); // post-condition BOOST_ASSERT( lt.owns_lock() ); BOOST_ASSERT( context::active() == lt.mutex()->owner_); return result; } }; }} #ifdef _MSC_VER # pragma warning(pop) #endif #ifdef BOOST_HAS_ABI_HEADERS # include BOOST_ABI_SUFFIX #endif #endif // BOOST_FIBERS_CONDITION_VARIABLE_H