From 4121cac3182e315cf2b30a90fab53136f8eb08ea Mon Sep 17 00:00:00 2001 From: Oliver Kowalke Date: Fri, 16 Aug 2013 21:24:32 +0200 Subject: [PATCH] add support for timed-operations (wait_for/wait_until) --- build/Jamfile.v2 | 2 + include/boost/fiber/algorithm.hpp | 10 +- include/boost/fiber/all.hpp | 2 + include/boost/fiber/condition.hpp | 104 +++- include/boost/fiber/detail/config.hpp | 10 + include/boost/fiber/exceptions.hpp | 8 +- include/boost/fiber/mutex.hpp | 4 - include/boost/fiber/operations.hpp | 13 +- include/boost/fiber/recursive_mutex.hpp | 2 - include/boost/fiber/recursive_timed_mutex.hpp | 81 +++ include/boost/fiber/round_robin.hpp | 18 +- include/boost/fiber/timed_mutex.hpp | 77 +++ src/mutex.cpp | 6 +- src/recursive_mutex.cpp | 15 +- src/recursive_timed_mutex.cpp | 215 ++++++++ src/round_robin.cpp | 26 +- src/timed_mutex.cpp | 184 +++++++ test/Jamfile.v2 | 1 + test/test_condition.cpp | 457 ++++++++++++----- test/test_mutex.cpp | 476 ++++++++++++++---- 20 files changed, 1451 insertions(+), 260 deletions(-) create mode 100644 include/boost/fiber/recursive_timed_mutex.hpp create mode 100644 include/boost/fiber/timed_mutex.hpp create mode 100644 src/recursive_timed_mutex.cpp create mode 100644 src/timed_mutex.cpp diff --git a/build/Jamfile.v2 b/build/Jamfile.v2 index 9cb776f9..aa30a294 100644 --- a/build/Jamfile.v2 +++ b/build/Jamfile.v2 @@ -67,7 +67,9 @@ lib boost_fibers interruption.cpp mutex.cpp recursive_mutex.cpp + recursive_timed_mutex.cpp round_robin.cpp + timed_mutex.cpp : shared:../../context/build//boost_context shared:../../chrono/build//boost_chrono ; diff --git a/include/boost/fiber/algorithm.hpp b/include/boost/fiber/algorithm.hpp index a44d2946..1134868e 100644 --- a/include/boost/fiber/algorithm.hpp +++ b/include/boost/fiber/algorithm.hpp @@ -10,7 +10,6 @@ #include #include -#include #include #include @@ -39,10 +38,11 @@ struct BOOST_FIBERS_DECL algorithm : private noncopyable virtual bool run() = 0; virtual void wait() = 0; - virtual void timed_wait( chrono::system_clock::time_point const&) = 0; - template< typename TimeDuration > - void timed_wait( TimeDuration const& dt) - { timed_wait( chrono::system_clock::now() + dt); } + virtual bool wait_until( clock_type::time_point const&) = 0; + + template< typename Rep, typename Period > + bool wait_for( chrono::duration< Rep, Period > const& timeout_duration) + { return wait_until( clock_type::now() + timeout_duration); } virtual void yield() = 0; diff --git a/include/boost/fiber/all.hpp b/include/boost/fiber/all.hpp index e8bde3c2..951bb418 100644 --- a/include/boost/fiber/all.hpp +++ b/include/boost/fiber/all.hpp @@ -26,7 +26,9 @@ #include #include #include +#include #include +#include #include #endif // BOOST_FIBERS_H diff --git a/include/boost/fiber/condition.hpp b/include/boost/fiber/condition.hpp index 1b8b9aeb..fd6ab4fc 100644 --- a/include/boost/fiber/condition.hpp +++ b/include/boost/fiber/condition.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -38,6 +39,13 @@ namespace boost { namespace fibers { +BOOST_SCOPED_ENUM_DECLARE_BEGIN(cv_status) +{ + no_timeout = 1, + timeout +} +BOOST_SCOPED_ENUM_DECLARE_END(cv_status) + class BOOST_FIBERS_DECL condition : private noncopyable { private: @@ -96,7 +104,7 @@ public: } catch (...) { - // remove fiber from waiting_ + // remove fiber from waiting-list waiting_.erase( std::find( waiting_.begin(), waiting_.end(), n) ); throw; @@ -105,6 +113,100 @@ public: // lock external again before returning lt.lock(); } + + template< typename LockType > + cv_status wait_until( LockType & lt, clock_type::time_point const& timeout_time) + { + cv_status status = cv_status::no_timeout; + + detail::notify::ptr_t n( detail::scheduler::instance()->active() ); + try + { + if ( n) + { + // store this fiber in order to be notified later + waiting_.push_back( n); + lt.unlock(); + + // suspend fiber + if ( ! detail::scheduler::instance()->wait_until( timeout_time) ) + { + // remove fiber from waiting-list + waiting_.erase( + std::find( waiting_.begin(), waiting_.end(), n) ); + + status = cv_status::timeout; + } + + // check if fiber was interrupted + this_fiber::interruption_point(); + } + else + { + // notifier for main-fiber + detail::main_notifier mn; + n = detail::main_notifier::make_pointer( mn); + + // store this fiber in order to be notified later + waiting_.push_back( n); + + lt.unlock(); + while ( ! n->is_ready() ) + { + if ( ! ( clock_type::now() < timeout_time) ) + { + // remove fiber from waiting-list + waiting_.erase( + std::find( waiting_.begin(), waiting_.end(), n) ); + + status = cv_status::timeout; + + break; + } + // run scheduler + detail::scheduler::instance()->run(); + } + } + } + catch (...) + { + // remove fiber from waiting-list + waiting_.erase( + std::find( waiting_.begin(), waiting_.end(), n) ); + throw; + } + + // lock external again before returning + lt.lock(); + + return status; + } + + template< typename LockType, typename Pred > + bool wait_until( LockType & lt, clock_type::time_point 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, chrono::duration< Rep, Period > const& timeout_duration) + { return wait_until( lt, clock_type::now() + timeout_duration); } + + template< typename LockType, typename Rep, typename Period, typename Pred > + bool wait_for( LockType & lt, chrono::duration< Rep, Period > const& timeout_duration, Pred pred) + { + while ( ! pred() ) + { + if ( cv_status::timeout == wait_for( lt, timeout_duration) ) + return pred(); + } + return true; + } }; typedef condition condition_variable; diff --git a/include/boost/fiber/detail/config.hpp b/include/boost/fiber/detail/config.hpp index a7f71a81..14258990 100644 --- a/include/boost/fiber/detail/config.hpp +++ b/include/boost/fiber/detail/config.hpp @@ -7,6 +7,7 @@ #ifndef BOOST_FIBERS_DETAIL_CONFIG_H #define BOOST_FIBERS_DETAIL_CONFIG_H +#include #include #include @@ -42,4 +43,13 @@ # define BOOST_FIBERS_SEGMENTS 10 #endif +namespace boost { +namespace fibers { +#if defined(BOOST_HAS_CLOCK_STEADY) + typedef boost::chrono::steady_clock clock_type; +#else + typedef boost::chrono::system_clock clock_type; +#endif +}} + #endif // BOOST_FIBERS_DETAIL_CONFIG_H diff --git a/include/boost/fiber/exceptions.hpp b/include/boost/fiber/exceptions.hpp index 1ab964be..100e90b1 100644 --- a/include/boost/fiber/exceptions.hpp +++ b/include/boost/fiber/exceptions.hpp @@ -158,10 +158,10 @@ public: BOOST_SCOPED_ENUM_DECLARE_BEGIN(future_errc) { - broken_promise = 1, - future_already_retrieved, - promise_already_satisfied, - no_state + broken_promise = 1, + future_already_retrieved, + promise_already_satisfied, + no_state } BOOST_SCOPED_ENUM_DECLARE_END(future_errc) diff --git a/include/boost/fiber/mutex.hpp b/include/boost/fiber/mutex.hpp index c9a91423..6abe3ee8 100644 --- a/include/boost/fiber/mutex.hpp +++ b/include/boost/fiber/mutex.hpp @@ -3,8 +3,6 @@ // 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) -// -// based on boost::interprocess::sync::interprocess_spinlock #ifndef BOOST_FIBERS_MUTEX_H #define BOOST_FIBERS_MUTEX_H @@ -60,8 +58,6 @@ public: void unlock(); }; -typedef mutex try_mutex; - }} # if defined(BOOST_MSVC) diff --git a/include/boost/fiber/operations.hpp b/include/boost/fiber/operations.hpp index 640cdc59..2323e11a 100644 --- a/include/boost/fiber/operations.hpp +++ b/include/boost/fiber/operations.hpp @@ -7,6 +7,7 @@ #define BOOST_THIS_FIBER_OPERATIONS_H #include +#include #include #include @@ -20,8 +21,8 @@ namespace this_fiber { inline fibers::fiber::id get_id() { - return fibers::detail::scheduler::instance()->active() - ? fibers::detail::scheduler::instance()->active()->get_id() + return fibers::detail::scheduler::instance()->active() + ? fibers::detail::scheduler::instance()->active()->get_id() : fibers::fiber::id(); } @@ -29,6 +30,14 @@ inline void yield() { fibers::detail::scheduler::instance()->yield(); } +inline +void sleep_until( fibers::clock_type::time_point const& sleep_time) +{ fibers::detail::scheduler::instance()->wait_until( sleep_time); } + +template< typename Rep, typename Period > +void sleep_for( chrono::duration< Rep, Period > const& timeout_duration) +{ fibers::detail::scheduler::instance()->wait_for( timeout_duration); } + } namespace fibers { diff --git a/include/boost/fiber/recursive_mutex.hpp b/include/boost/fiber/recursive_mutex.hpp index 564c618e..1f0c55b9 100644 --- a/include/boost/fiber/recursive_mutex.hpp +++ b/include/boost/fiber/recursive_mutex.hpp @@ -62,8 +62,6 @@ public: void unlock(); }; -typedef recursive_mutex recursive_try_mutex; - }} # if defined(BOOST_MSVC) diff --git a/include/boost/fiber/recursive_timed_mutex.hpp b/include/boost/fiber/recursive_timed_mutex.hpp new file mode 100644 index 00000000..f4b6d881 --- /dev/null +++ b/include/boost/fiber/recursive_timed_mutex.hpp @@ -0,0 +1,81 @@ + +// 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) +// +// based on boost::interprocess::sync::interprocess_spinlock + +#ifndef BOOST_FIBERS_RECURSIVE_TIMED_MUTEX_H +#define BOOST_FIBERS_RECURSIVE_TIMED_MUTEX_H + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +# if defined(BOOST_MSVC) +# pragma warning(push) +# pragma warning(disable:4355 4251 4275) +# endif + +namespace boost { +namespace fibers { + +class BOOST_FIBERS_DECL recursive_timed_mutex : private noncopyable +{ +private: + enum state_t + { + LOCKED = 0, + UNLOCKED + }; + + detail::fiber_base::id owner_; + std::size_t count_; + volatile state_t state_; + std::deque< + detail::notify::ptr_t + > waiting_; + +public: + typedef unique_lock< recursive_timed_mutex > scoped_lock; + + recursive_timed_mutex(); + + ~recursive_timed_mutex(); + + void lock(); + + bool try_lock(); + + bool try_lock_until( clock_type::time_point const& timeout_time); + + template< typename Rep, typename Period > + bool try_lock_for( chrono::duration< Rep, Period > const& timeout_duration) + { return try_lock_until( clock_type::now() + timeout_duration); } + + void unlock(); +}; + +}} + +# if defined(BOOST_MSVC) +# pragma warning(pop) +# endif + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_FIBERS_RECURSIVE_TIMED_MUTEX_H diff --git a/include/boost/fiber/round_robin.hpp b/include/boost/fiber/round_robin.hpp index 25653dda..c8e8d8e4 100644 --- a/include/boost/fiber/round_robin.hpp +++ b/include/boost/fiber/round_robin.hpp @@ -9,7 +9,6 @@ #include #include -#include #include #include #include @@ -38,20 +37,13 @@ private: struct schedulable { detail::fiber_base::ptr_t f; - chrono::system_clock::time_point tp; - - schedulable( detail::fiber_base::ptr_t const& f_) : - f( f_), - tp( (chrono::system_clock::time_point::max)() ) - { BOOST_ASSERT( f); } + clock_type::time_point tp; schedulable( detail::fiber_base::ptr_t const& f_, - chrono::system_clock::time_point const& tp_) : + clock_type::time_point const& tp_ = + (clock_type::time_point::max)() ) : f( f_), tp( tp_) - { - BOOST_ASSERT( f); - BOOST_ASSERT( (chrono::system_clock::time_point::max)() != tp); - } + { BOOST_ASSERT( f); } }; typedef std::deque< schedulable > wqueue_t; @@ -78,7 +70,7 @@ public: bool run(); void wait(); - void timed_wait( chrono::system_clock::time_point const&); + bool wait_until( clock_type::time_point const&); void yield(); }; diff --git a/include/boost/fiber/timed_mutex.hpp b/include/boost/fiber/timed_mutex.hpp new file mode 100644 index 00000000..fd88751c --- /dev/null +++ b/include/boost/fiber/timed_mutex.hpp @@ -0,0 +1,77 @@ + +// 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_TIMED_MUTEX_H +#define BOOST_FIBERS_TIMED_MUTEX_H + +#include + +#include +#include +#include + +#include +#include +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +# if defined(BOOST_MSVC) +# pragma warning(push) +# pragma warning(disable:4355 4251 4275) +# endif + +namespace boost { +namespace fibers { + +class BOOST_FIBERS_DECL timed_mutex : private noncopyable +{ +private: + enum state_t + { + LOCKED = 0, + UNLOCKED + }; + + detail::fiber_base::id owner_; + volatile state_t state_; + std::deque< + detail::notify::ptr_t + > waiting_; + +public: + typedef unique_lock< timed_mutex > scoped_lock; + + timed_mutex(); + + ~timed_mutex(); + + void lock(); + + bool try_lock(); + + bool try_lock_until( clock_type::time_point const& timeout_time); + + template< typename Rep, typename Period > + bool try_lock_for( chrono::duration< Rep, Period > const& timeout_duration) + { return try_lock_until( clock_type::now() + timeout_duration); } + + void unlock(); +}; + +}} + +# if defined(BOOST_MSVC) +# pragma warning(pop) +# endif + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_FIBERS_TIMED_MUTEX_H diff --git a/src/mutex.cpp b/src/mutex.cpp index f28759be..aa3a490a 100644 --- a/src/mutex.cpp +++ b/src/mutex.cpp @@ -84,7 +84,11 @@ mutex::lock() bool mutex::try_lock() { - if ( LOCKED == state_) return false; + if ( LOCKED == state_) { + // let other fiber release the lock + detail::scheduler::instance()->yield(); + return false; + } state_ = LOCKED; owner_ = this_fiber::get_id(); diff --git a/src/recursive_mutex.cpp b/src/recursive_mutex.cpp index 4a980ec5..63ff2cc8 100644 --- a/src/recursive_mutex.cpp +++ b/src/recursive_mutex.cpp @@ -94,7 +94,20 @@ recursive_mutex::lock() bool recursive_mutex::try_lock() { - if ( LOCKED == state_) return false; + if ( LOCKED == state_) + { + if ( this_fiber::get_id() == owner_) + { + ++count_; + return true; + } + else + { + // let other fiber release the lock + detail::scheduler::instance()->yield(); + return false; + } + } state_ = LOCKED; owner_ = this_fiber::get_id(); diff --git a/src/recursive_timed_mutex.cpp b/src/recursive_timed_mutex.cpp new file mode 100644 index 00000000..c2443c2b --- /dev/null +++ b/src/recursive_timed_mutex.cpp @@ -0,0 +1,215 @@ + +// 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) + +#include "boost/fiber/recursive_timed_mutex.hpp" + +#include + +#include + +#include "boost/fiber/detail/main_notifier.hpp" +#include "boost/fiber/detail/scheduler.hpp" +#include "boost/fiber/interruption.hpp" +#include "boost/fiber/operations.hpp" + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace fibers { + +recursive_timed_mutex::recursive_timed_mutex() : + owner_(), + count_( 0), + state_( UNLOCKED), + waiting_() +{} + +recursive_timed_mutex::~recursive_timed_mutex() +{ + BOOST_ASSERT( ! owner_); + BOOST_ASSERT( 0 == count_); + BOOST_ASSERT( waiting_.empty() ); +} + +void +recursive_timed_mutex::lock() +{ + if ( LOCKED == state_ && this_fiber::get_id() == owner_) + { + ++count_; + return; + } + + while ( LOCKED == state_) + { + detail::notify::ptr_t n( detail::scheduler::instance()->active() ); + try + { + if ( n) + { + // store this fiber in order to be notified later + waiting_.push_back( n); + + // suspend this fiber + detail::scheduler::instance()->wait(); + } + else + { + // notifier for main-fiber + detail::main_notifier mn; + n = detail::main_notifier::make_pointer( mn); + + // store this fiber in order to be notified later + waiting_.push_back( n); + + // wait until main-fiber gets notified + while ( ! n->is_ready() ) + { + // run scheduler + detail::scheduler::instance()->run(); + } + } + } + catch (...) + { + // remove fiber from waiting_ + waiting_.erase( + std::find( waiting_.begin(), waiting_.end(), n) ); + throw; + } + } + BOOST_ASSERT( ! owner_); + BOOST_ASSERT( 0 == count_); + + state_ = LOCKED; + owner_ = this_fiber::get_id(); + ++count_; +} + +bool +recursive_timed_mutex::try_lock() +{ + if ( LOCKED == state_) + { + if ( this_fiber::get_id() == owner_) + { + ++count_; + return true; + } + else + { + // let other fiber release the lock + detail::scheduler::instance()->yield(); + return false; + } + } + + state_ = LOCKED; + owner_ = this_fiber::get_id(); + ++count_; + return true; +} + +bool +recursive_timed_mutex::try_lock_until( clock_type::time_point const& timeout_time) +{ + if ( LOCKED == state_ && this_fiber::get_id() == owner_) + { + ++count_; + return true; + } + + while ( LOCKED == state_ && clock_type::now() < timeout_time) + { + detail::notify::ptr_t n( detail::scheduler::instance()->active() ); + try + { + if ( n) + { + // store this fiber in order to be notified later + waiting_.push_back( n); + + // suspend this fiber until notified or timed-out + if ( ! detail::scheduler::instance()->wait_until( timeout_time) ) + // remove fiber from waiting-list + waiting_.erase( + std::find( waiting_.begin(), waiting_.end(), n) ); + } + else + { + // notifier for main-fiber + detail::main_notifier mn; + n = detail::main_notifier::make_pointer( mn); + + // store this fiber in order to be notified later + waiting_.push_back( n); + + // wait until main-fiber gets notified + while ( ! n->is_ready() ) + { + if ( ! ( clock_type::now() < timeout_time) ) + { + // remove fiber from waiting-list + waiting_.erase( + std::find( waiting_.begin(), waiting_.end(), n) ); + break; + } + // run scheduler + detail::scheduler::instance()->run(); + } + } + } + catch (...) + { + // remove fiber from waiting-list + waiting_.erase( + std::find( waiting_.begin(), waiting_.end(), n) ); + throw; + } + } + + if ( LOCKED == state_) return false; + + BOOST_ASSERT( ! owner_); + BOOST_ASSERT( 0 == count_); + + state_ = LOCKED; + owner_ = this_fiber::get_id(); + ++count_; + + return true; +} + +void +recursive_timed_mutex::unlock() +{ + BOOST_ASSERT( LOCKED == state_); + BOOST_ASSERT( this_fiber::get_id() == owner_); + + if ( 0 == --count_) + { + detail::notify::ptr_t n; + + if ( ! waiting_.empty() ) { + n.swap( waiting_.front() ); + waiting_.pop_front(); + } + + state_ = UNLOCKED; + owner_ = detail::fiber_base::id(); + + if ( n) + n->set_ready(); + } +} + +}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif diff --git a/src/round_robin.cpp b/src/round_robin.cpp index f1f29831..8127938a 100644 --- a/src/round_robin.cpp +++ b/src/round_robin.cpp @@ -36,13 +36,11 @@ round_robin::round_robin() BOOST_NOEXCEPT : round_robin::~round_robin() BOOST_NOEXCEPT { -#if 0 - BOOST_FOREACH( detail::fiber_base::ptr_t const& p, rqueue_) - { p->release(); } - - BOOST_FOREACH( detail::fiber_base::ptr_t const& p, wqueue_) - { p->release(); } -#endif + // fibers will be destroyed (stack-unwinding) + // if last reference goes out-of-scope + // therefore destructing wqueue_ && rqueue_ + // will destroy the fibers in this scheduler + // if not referenced on other places if ( detail::scheduler::instance() == this) detail::scheduler::replace( 0); } @@ -91,7 +89,7 @@ round_robin::run() BOOST_ASSERT( ! f->is_terminated() ); // set fiber to state_ready if dead-line was reached - if ( s.tp <= chrono::system_clock::now() ) + if ( s.tp <= clock_type::now() ) f->set_ready(); // set fiber to state_ready if interruption was requested if ( f->interruption_requested() ) @@ -126,18 +124,20 @@ round_robin::run() void round_robin::wait() -{ timed_wait( chrono::system_clock::time_point() ); } +{ wait_until( clock_type::time_point( (clock_type::duration::max)() ) ); } -void -round_robin::timed_wait( chrono::system_clock::time_point const& abs_time) +bool +round_robin::wait_until( clock_type::time_point const& timeout_time) { + clock_type::time_point start( clock_type::now() ); + BOOST_ASSERT( active_fiber_); BOOST_ASSERT( active_fiber_->is_running() ); // set active fiber to state_waiting active_fiber_->set_waiting(); // push active fiber to wqueue_ - wqueue_.push_back( schedulable( active_fiber_, abs_time) ); + wqueue_.push_back( schedulable( active_fiber_, timeout_time) ); // store active fiber in local var detail::fiber_base::ptr_t tmp = active_fiber_; // suspend active fiber @@ -146,6 +146,8 @@ round_robin::timed_wait( chrono::system_clock::time_point const& abs_time) BOOST_ASSERT( tmp == active_fiber_); BOOST_ASSERT( active_fiber_->is_running() ); + + return clock_type::now() < timeout_time; } void diff --git a/src/timed_mutex.cpp b/src/timed_mutex.cpp new file mode 100644 index 00000000..21131a75 --- /dev/null +++ b/src/timed_mutex.cpp @@ -0,0 +1,184 @@ + +// 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) + +#include "boost/fiber/timed_mutex.hpp" + +#include + +#include + +#include "boost/fiber/detail/main_notifier.hpp" +#include "boost/fiber/detail/scheduler.hpp" +#include "boost/fiber/interruption.hpp" +#include "boost/fiber/operations.hpp" + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace fibers { + +timed_mutex::timed_mutex() : + owner_(), + state_( UNLOCKED), + waiting_() +{} + +timed_mutex::~timed_mutex() +{ + BOOST_ASSERT( ! owner_); + BOOST_ASSERT( waiting_.empty() ); +} + +void +timed_mutex::lock() +{ + while ( LOCKED == state_) + { + detail::notify::ptr_t n( detail::scheduler::instance()->active() ); + try + { + if ( n) + { + // store this fiber in order to be notified later + waiting_.push_back( n); + + // suspend this fiber + detail::scheduler::instance()->wait(); + } + else + { + // notifier for main-fiber + detail::main_notifier mn; + n = detail::main_notifier::make_pointer( mn); + + // store this fiber in order to be notified later + waiting_.push_back( n); + + // wait until main-fiber gets notified + while ( ! n->is_ready() ) + { + // run scheduler + detail::scheduler::instance()->run(); + } + } + } + catch (...) + { + // remove fiber from waiting_ + waiting_.erase( + std::find( waiting_.begin(), waiting_.end(), n) ); + throw; + } + } + BOOST_ASSERT( ! owner_); + + state_ = LOCKED; + owner_ = this_fiber::get_id(); +} + +bool +timed_mutex::try_lock() +{ + if ( LOCKED == state_) { + // let other fiber release the lock + detail::scheduler::instance()->yield(); + return false; + } + + state_ = LOCKED; + owner_ = this_fiber::get_id(); + return true; +} + +bool +timed_mutex::try_lock_until( clock_type::time_point const& timeout_time) +{ + while ( LOCKED == state_ && clock_type::now() < timeout_time) + { + detail::notify::ptr_t n( detail::scheduler::instance()->active() ); + try + { + if ( n) + { + // store this fiber in order to be notified later + waiting_.push_back( n); + + // suspend this fiber until notified or timed-out + if ( ! detail::scheduler::instance()->wait_until( timeout_time) ) + // remove fiber from waiting-list + waiting_.erase( + std::find( waiting_.begin(), waiting_.end(), n) ); + } + else + { + // notifier for main-fiber + detail::main_notifier mn; + n = detail::main_notifier::make_pointer( mn); + + // store this fiber in order to be notified later + waiting_.push_back( n); + + // wait until main-fiber gets notified + while ( ! n->is_ready() ) + { + if ( ! ( clock_type::now() < timeout_time) ) + { + // remove fiber from waiting-list + waiting_.erase( + std::find( waiting_.begin(), waiting_.end(), n) ); + break; + } + // run scheduler + detail::scheduler::instance()->run(); + } + } + } + catch (...) + { + // remove fiber from waiting-list + waiting_.erase( + std::find( waiting_.begin(), waiting_.end(), n) ); + throw; + } + } + + if ( LOCKED == state_) return false; + + BOOST_ASSERT( ! owner_); + + state_ = LOCKED; + owner_ = this_fiber::get_id(); + + return true; +} + +void +timed_mutex::unlock() +{ + BOOST_ASSERT( LOCKED == state_); + BOOST_ASSERT( this_fiber::get_id() == owner_); + + detail::notify::ptr_t n; + + if ( ! waiting_.empty() ) { + n.swap( waiting_.front() ); + waiting_.pop_front(); + } + + state_ = UNLOCKED; + owner_ = detail::fiber_base::id(); + + if ( n) + n->set_ready(); +} + +}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index a39413ea..d1fa5801 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -12,6 +12,7 @@ project boost/fiber/test ../../test/build//boost_unit_test_framework /boost/fiber//boost_fibers /boost/system//boost_system + /boost/thread//boost_thread static multi ; diff --git a/test/test_condition.cpp b/test/test_condition.cpp index 53c3e362..993aa9f5 100644 --- a/test/test_condition.cpp +++ b/test/test_condition.cpp @@ -3,6 +3,8 @@ // 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) +// +// This test is based on the tests of Boost.Thread #include #include @@ -21,6 +23,9 @@ #include +typedef boost::chrono::nanoseconds ns; +typedef boost::chrono::milliseconds ms; + int value = 0; inline @@ -86,125 +91,6 @@ void wait_fn( ++value; } -void condition_test_waits( condition_test_data * data) -{ - boost::fibers::mutex::scoped_lock lock( data->mutex); - BOOST_CHECK( lock ? true : false); - - // Test wait. - while ( data->notified != 1) - { - data->condition.wait(lock); - } - BOOST_CHECK(lock ? true : false); - BOOST_CHECK_EQUAL(data->notified, 1); - data->awoken++; - data->condition.notify_one(); - - while ( data->notified != 2) - { - data->condition.wait(lock); - } - BOOST_CHECK(lock ? true : false); - BOOST_CHECK_EQUAL(data->notified, 2); - data->awoken++; - data->condition.notify_one(); - - // Test predicate wait. -// data->condition.wait(lock, cond_predicate(data->notified, 2)); -// BOOST_CHECK(lock ? true : false); -// BOOST_CHECK_EQUAL(data->notified, 2); -// data->awoken++; -// data->condition.notify_one(); -#if 0 - // Test timed_wait. - boost::chrono::system_clock::time_point xt = delay(10); - while (data->notified != 3) - data->condition.wait(lock); - //data->condition.timed_wait(lock, xt); - BOOST_CHECK(lock ? true : false); - BOOST_CHECK_EQUAL(data->notified, 3); - data->awoken++; - data->condition.notify_one(); - - // Test predicate timed_wait. - xt = delay(10); - cond_predicate pred(data->notified, 4); - BOOST_CHECK(data->condition.timed_wait(lock, xt, pred)); - BOOST_CHECK(lock ? true : false); - BOOST_CHECK(pred()); - BOOST_CHECK_EQUAL(data->notified, 4); - data->awoken++; - data->condition.notify_one(); - - // Test predicate timed_wait with relative timeout - cond_predicate pred_rel(data->notified, 5); - BOOST_CHECK(data->condition.timed_wait(lock, boost::chrono::seconds(10), pred_rel)); - BOOST_CHECK(lock ? true : false); - BOOST_CHECK(pred_rel()); - BOOST_CHECK_EQUAL(data->notified, 5); - data->awoken++; - data->condition.notify_one(); - - // Test timeout timed_wait. - BOOST_CHECK(!data->condition.timed_wait(lock, boost::chrono::seconds(2))); - BOOST_CHECK(lock ? true : false); -#endif -} - -void do_test_condition_waits() -{ - condition_test_data data; - - boost::fibers::fiber s( - boost::bind( & condition_test_waits, & data) ); - - { - boost::fibers::mutex::scoped_lock lock( data.mutex); - BOOST_CHECK(lock ? true : false); - - data.notified++; - data.condition.notify_one(); - while (data.awoken != 1) - data.condition.wait(lock); - BOOST_CHECK(lock ? true : false); - BOOST_CHECK_EQUAL(data.awoken, 1); - - data.notified++; - data.condition.notify_one(); - while (data.awoken != 2) - data.condition.wait(lock); - BOOST_CHECK(lock ? true : false); - BOOST_CHECK_EQUAL(data.awoken, 2); -#if 0 - data.notified++; - data.condition.notify_one(); - while (data.awoken != 3) - data.condition.wait(lock); - BOOST_CHECK(lock ? true : false); - BOOST_CHECK_EQUAL(data.awoken, 3); - - data.notified++; - data.condition.notify_one(); - while (data.awoken != 4) - data.condition.wait(lock); - BOOST_CHECK(lock ? true : false); - BOOST_CHECK_EQUAL(data.awoken, 4); - - data.notified++; - data.condition.notify_one(); - while (data.awoken != 5) - data.condition.wait(lock); - BOOST_CHECK(lock ? true : false); - BOOST_CHECK_EQUAL(data.awoken, 5); -#endif - } - - s.join(); - - BOOST_CHECK_EQUAL(data.awoken, 2); -} - void test_condition_wait_is_a_interruption_point() { boost::fibers::round_robin ds; @@ -346,12 +232,333 @@ void test_two_waiter_notify_all() BOOST_CHECK_EQUAL( 3, value); } -void test_condition_waits() +int test1 = 0; +int test2 = 0; + +int runs = 0; + +void fn1( boost::fibers::mutex & m, boost::fibers::condition_variable & cv) +{ + boost::unique_lock< boost::fibers::mutex > lk( m); + BOOST_CHECK(test2 == 0); + test1 = 1; + cv.notify_one(); + while (test2 == 0) { + cv.wait(lk); + } + BOOST_CHECK(test2 != 0); +} + +void fn2( boost::fibers::mutex & m, boost::fibers::condition_variable & cv) +{ + boost::unique_lock< boost::fibers::mutex > lk( m); + BOOST_CHECK(test2 == 0); + test1 = 1; + cv.notify_one(); + boost::fibers::clock_type::time_point t0 = boost::fibers::clock_type::now(); + boost::fibers::clock_type::time_point t = t0 + ms(250); + int count=0; + while (test2 == 0 && cv.wait_until(lk, t) == boost::fibers::cv_status::no_timeout) + count++; + boost::fibers::clock_type::time_point t1 = boost::fibers::clock_type::now(); + if (runs == 0) + { + BOOST_CHECK(t1 - t0 < ms(250)); + BOOST_CHECK(test2 != 0); + } + else + { + BOOST_CHECK(t1 - t0 - ms(250) < ms(count*250+5+1000)); + BOOST_CHECK(test2 == 0); + } + ++runs; +} + +class Pred +{ + int & i_; + +public: + explicit Pred(int& i) : + i_(i) + {} + + bool operator()() + { return i_ != 0; } +}; + +void fn3( boost::fibers::mutex & m, boost::fibers::condition_variable & cv) +{ + boost::unique_lock< boost::fibers::mutex > lk( m); + BOOST_CHECK(test2 == 0); + test1 = 1; + cv.notify_one(); + boost::fibers::clock_type::time_point t0 = boost::fibers::clock_type::now(); + boost::fibers::clock_type::time_point t = t0 + ms(250); + bool r = cv.wait_until(lk, t, Pred(test2)); + boost::fibers::clock_type::time_point t1 = boost::fibers::clock_type::now(); + if (runs == 0) + { + BOOST_CHECK(t1 - t0 < ms(250)); + BOOST_CHECK(test2 != 0); + BOOST_CHECK(r); + } + else + { + BOOST_CHECK(t1 - t0 - ms(250) < ms(250+2)); + BOOST_CHECK(test2 == 0); + BOOST_CHECK(!r); + } + ++runs; +} + +void fn4( boost::fibers::mutex & m, boost::fibers::condition_variable & cv) +{ + boost::unique_lock< boost::fibers::mutex > lk( m); + BOOST_CHECK(test2 == 0); + test1 = 1; + cv.notify_one(); + boost::fibers::clock_type::time_point t0 = boost::fibers::clock_type::now(); + int count=0; + while (test2 == 0 && cv.wait_for(lk, ms(250)) == boost::fibers::cv_status::no_timeout) + count++; + boost::fibers::clock_type::time_point t1 = boost::fibers::clock_type::now(); + if (runs == 0) + { + BOOST_CHECK(t1 - t0 < ms(250)); + BOOST_CHECK(test2 != 0); + } + else + { + BOOST_CHECK(t1 - t0 - ms(250) < ms(count*250+5+1000)); + BOOST_CHECK(test2 == 0); + } + ++runs; +} + +void fn5( boost::fibers::mutex & m, boost::fibers::condition_variable & cv) +{ + boost::unique_lock< boost::fibers::mutex > lk( m); + BOOST_CHECK(test2 == 0); + test1 = 1; + cv.notify_one(); + boost::fibers::clock_type::time_point t0 = boost::fibers::clock_type::now(); + int count=0; + cv.wait_for(lk, ms(250), Pred(test2)); + count++; + boost::fibers::clock_type::time_point t1 = boost::fibers::clock_type::now(); + if (runs == 0) + { + BOOST_CHECK(t1 - t0 < ms(250+1000)); + BOOST_CHECK(test2 != 0); + } + else + { + BOOST_CHECK(t1 - t0 - ms(250) < ms(count*250+2)); + BOOST_CHECK(test2 == 0); + } + ++runs; +} + +void do_test_condition_wait() +{ + test1 = 0; + test2 = 0; + runs = 0; + + boost::fibers::mutex m; + boost::fibers::condition_variable cv; + boost::unique_lock< boost::fibers::mutex > lk( m); + boost::fibers::fiber f( boost::bind( & fn1, boost::ref( m), boost::ref( cv) ) ); + BOOST_CHECK(test1 == 0); + while (test1 == 0) + cv.wait(lk); + BOOST_CHECK(test1 != 0); + test2 = 1; + lk.unlock(); + cv.notify_one(); + f.join(); +} + +void test_condition_wait() { boost::fibers::round_robin ds; boost::fibers::scheduling_algorithm( & ds); - do_test_condition_waits(); + boost::fibers::fiber( & do_test_condition_wait).join(); + do_test_condition_wait(); +} + +void do_test_condition_wait_until() +{ + test1 = 0; + test2 = 0; + runs = 0; + + boost::fibers::mutex m; + boost::fibers::condition_variable cv; + { + boost::unique_lock< boost::fibers::mutex > lk( m); + boost::fibers::fiber f( boost::bind( & fn2, boost::ref( m), boost::ref( cv) ) ); + BOOST_CHECK(test1 == 0); + while (test1 == 0) + cv.wait(lk); + BOOST_CHECK(test1 != 0); + test2 = 1; + lk.unlock(); + cv.notify_one(); + f.join(); + } + test1 = 0; + test2 = 0; + { + boost::unique_lock< boost::fibers::mutex > lk( m); + boost::fibers::fiber f( boost::bind( & fn2, boost::ref( m), boost::ref( cv) ) ); + BOOST_CHECK(test1 == 0); + while (test1 == 0) + cv.wait(lk); + BOOST_CHECK(test1 != 0); + lk.unlock(); + f.join(); + } +} + +void test_condition_wait_until() +{ + boost::fibers::round_robin ds; + boost::fibers::scheduling_algorithm( & ds); + + boost::fibers::fiber( & do_test_condition_wait_until).join(); + do_test_condition_wait_until(); +} + +void do_test_condition_wait_until_pred() +{ + test1 = 0; + test2 = 0; + runs = 0; + + boost::fibers::mutex m; + boost::fibers::condition_variable cv; + { + boost::unique_lock< boost::fibers::mutex > lk( m); + boost::fibers::fiber f( boost::bind( & fn3, boost::ref( m), boost::ref( cv) ) ); + BOOST_CHECK(test1 == 0); + while (test1 == 0) + cv.wait(lk); + BOOST_CHECK(test1 != 0); + test2 = 1; + lk.unlock(); + cv.notify_one(); + f.join(); + } + test1 = 0; + test2 = 0; + { + boost::unique_lock< boost::fibers::mutex > lk( m); + boost::fibers::fiber f( boost::bind( & fn3, boost::ref( m), boost::ref( cv) ) ); + BOOST_CHECK(test1 == 0); + while (test1 == 0) + cv.wait(lk); + BOOST_CHECK(test1 != 0); + lk.unlock(); + f.join(); + } +} + +void test_condition_wait_until_pred() +{ + boost::fibers::round_robin ds; + boost::fibers::scheduling_algorithm( & ds); + + boost::fibers::fiber( & do_test_condition_wait_until_pred).join(); + do_test_condition_wait_until_pred(); +} + +void do_test_condition_wait_for() +{ + test1 = 0; + test2 = 0; + runs = 0; + + boost::fibers::mutex m; + boost::fibers::condition_variable cv; + { + boost::unique_lock< boost::fibers::mutex > lk( m); + boost::fibers::fiber f( boost::bind( & fn4, boost::ref( m), boost::ref( cv) ) ); + BOOST_CHECK(test1 == 0); + while (test1 == 0) + cv.wait(lk); + BOOST_CHECK(test1 != 0); + test2 = 1; + lk.unlock(); + cv.notify_one(); + f.join(); + } + test1 = 0; + test2 = 0; + { + boost::unique_lock< boost::fibers::mutex > lk( m); + boost::fibers::fiber f( boost::bind( & fn4, boost::ref( m), boost::ref( cv) ) ); + BOOST_CHECK(test1 == 0); + while (test1 == 0) + cv.wait(lk); + BOOST_CHECK(test1 != 0); + lk.unlock(); + f.join(); + } +} + +void test_condition_wait_for() +{ + boost::fibers::round_robin ds; + boost::fibers::scheduling_algorithm( & ds); + + boost::fibers::fiber( & do_test_condition_wait_for).join(); + do_test_condition_wait_for(); +} + +void do_test_condition_wait_for_pred() +{ + test1 = 0; + test2 = 0; + runs = 0; + + boost::fibers::mutex m; + boost::fibers::condition_variable cv; + { + boost::unique_lock< boost::fibers::mutex > lk( m); + boost::fibers::fiber f( boost::bind( & fn5, boost::ref( m), boost::ref( cv) ) ); + BOOST_CHECK(test1 == 0); + while (test1 == 0) + cv.wait(lk); + BOOST_CHECK(test1 != 0); + test2 = 1; + lk.unlock(); + cv.notify_one(); + f.join(); + } + test1 = 0; + test2 = 0; + { + boost::unique_lock< boost::fibers::mutex > lk( m); + boost::fibers::fiber f( boost::bind( & fn5, boost::ref( m), boost::ref( cv) ) ); + BOOST_CHECK(test1 == 0); + while (test1 == 0) + cv.wait(lk); + BOOST_CHECK(test1 != 0); + lk.unlock(); + f.join(); + } +} + +void test_condition_wait_for_pred() +{ + boost::fibers::round_robin ds; + boost::fibers::scheduling_algorithm( & ds); + + boost::fibers::fiber( & do_test_condition_wait_for_pred).join(); + do_test_condition_wait_for_pred(); } boost::unit_test::test_suite * init_unit_test_suite( int, char* []) @@ -362,8 +569,12 @@ boost::unit_test::test_suite * init_unit_test_suite( int, char* []) test->add( BOOST_TEST_CASE( & test_one_waiter_notify_one) ); test->add( BOOST_TEST_CASE( & test_two_waiter_notify_one) ); test->add( BOOST_TEST_CASE( & test_two_waiter_notify_all) ); - test->add( BOOST_TEST_CASE( & test_condition_waits) ); - test->add( BOOST_TEST_CASE( & test_condition_wait_is_a_interruption_point) ); + test->add( BOOST_TEST_CASE( & test_condition_wait) ); + test->add( BOOST_TEST_CASE( & test_condition_wait_is_a_interruption_point) ); + test->add( BOOST_TEST_CASE( & test_condition_wait_until) ); + test->add( BOOST_TEST_CASE( & test_condition_wait_until_pred) ); + test->add( BOOST_TEST_CASE( & test_condition_wait_for) ); + test->add( BOOST_TEST_CASE( & test_condition_wait_for_pred) ); return test; } diff --git a/test/test_mutex.cpp b/test/test_mutex.cpp index 6f8b0b64..4d515ec2 100644 --- a/test/test_mutex.cpp +++ b/test/test_mutex.cpp @@ -13,13 +13,206 @@ #include #include +#include #include #include #include +#include #include #include +typedef boost::chrono::nanoseconds ns; +typedef boost::chrono::milliseconds ms; + +int value1 = 0; +int value2 = 0; + +template< typename M > +void fn1( M & mtx) +{ + typedef M mutex_type; + typename mutex_type::scoped_lock lk( mtx); + ++value1; + for ( int i = 0; i < 3; ++i) + boost::this_fiber::yield(); +} + +template< typename M > +void fn2( M & mtx) +{ + typedef M mutex_type; + ++value2; + typename mutex_type::scoped_lock lk( mtx); + ++value2; +} + +void fn3( boost::fibers::timed_mutex & m) +{ + boost::fibers::clock_type::time_point t0 = boost::fibers::clock_type::now(); + m.lock(); + boost::fibers::clock_type::time_point t1 = boost::fibers::clock_type::now(); + m.unlock(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ns(2500000)+ms(1000)); // within 2.5 ms +} + +void fn4( boost::fibers::timed_mutex & m) +{ + boost::fibers::clock_type::time_point t0 = boost::fibers::clock_type::now(); + while ( ! m.try_lock() ); + boost::fibers::clock_type::time_point t1 = boost::fibers::clock_type::now(); + m.unlock(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ns(50000000)+ms(2000)); // within 50 ms +} + +void fn5( boost::fibers::timed_mutex & m) +{ + boost::fibers::clock_type::time_point t0 = boost::fibers::clock_type::now(); + BOOST_CHECK( m.try_lock_for(ms(300)+ms(2000)) == true); + boost::fibers::clock_type::time_point t1 = boost::fibers::clock_type::now(); + m.unlock(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ns(5000000)+ms(2000)); // within 5 ms +} + +void fn6( boost::fibers::timed_mutex & m) +{ + boost::fibers::clock_type::time_point t0 = boost::fibers::clock_type::now(); + BOOST_CHECK(m.try_lock_for(ms(250)) == false); + boost::fibers::clock_type::time_point t1 = boost::fibers::clock_type::now(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ns(5000000)+ms(1000)); // within 5 ms +} + +void fn7( boost::fibers::timed_mutex & m) +{ + boost::fibers::clock_type::time_point t0 = boost::fibers::clock_type::now(); + BOOST_CHECK(m.try_lock_until(boost::fibers::clock_type::now() + ms(300) + ms(1000)) == true); + boost::fibers::clock_type::time_point t1 = boost::fibers::clock_type::now(); + m.unlock(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ns(5000000)+ms(1000)); // within 5ms +} + +void fn8( boost::fibers::timed_mutex & m) +{ + boost::fibers::clock_type::time_point t0 = boost::fibers::clock_type::now(); + BOOST_CHECK(m.try_lock_until(boost::fibers::clock_type::now() + ms(250)) == false); + boost::fibers::clock_type::time_point t1 = boost::fibers::clock_type::now(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ns(5000000)+ms(1000)); // within 5ms +} + +void fn9( boost::fibers::recursive_timed_mutex & m) +{ + boost::fibers::clock_type::time_point t0 = boost::fibers::clock_type::now(); + m.lock(); + boost::fibers::clock_type::time_point t1 = boost::fibers::clock_type::now(); + m.lock(); + m.unlock(); + m.unlock(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ns(2500000)+ms(1000)); // within 2.5 ms +} + +void fn10( boost::fibers::recursive_timed_mutex & m) +{ + boost::fibers::clock_type::time_point t0 = boost::fibers::clock_type::now(); + while (!m.try_lock()) ; + boost::fibers::clock_type::time_point t1 = boost::fibers::clock_type::now(); + BOOST_CHECK(m.try_lock()); + m.unlock(); + m.unlock(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ns(50000000)+ms(1000)); // within 50 ms +} + +void fn11( boost::fibers::recursive_timed_mutex & m) +{ + boost::fibers::clock_type::time_point t0 = boost::fibers::clock_type::now(); + BOOST_CHECK(m.try_lock_for(ms(300)+ms(1000)) == true); + boost::fibers::clock_type::time_point t1 = boost::fibers::clock_type::now(); + BOOST_CHECK(m.try_lock()); + m.unlock(); + m.unlock(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ns(5000000)+ms(1000)); // within 5 ms +} + +void fn12( boost::fibers::recursive_timed_mutex & m) +{ + boost::fibers::clock_type::time_point t0 = boost::fibers::clock_type::now(); + BOOST_CHECK(m.try_lock_for(ms(250)) == false); + boost::fibers::clock_type::time_point t1 = boost::fibers::clock_type::now(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ns(5000000)+ms(1000)); // within 5 ms +} + +void fn13( boost::fibers::recursive_timed_mutex & m) +{ + boost::fibers::clock_type::time_point t0 = boost::fibers::clock_type::now(); + BOOST_CHECK(m.try_lock_until(boost::fibers::clock_type::now() + ms(300) + ms(1000)) == true); + boost::fibers::clock_type::time_point t1 = boost::fibers::clock_type::now(); + m.unlock(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ns(5000000)+ms(1000)); // within 5 ms +} + +void fn14( boost::fibers::recursive_timed_mutex & m) +{ + boost::fibers::clock_type::time_point t0 = boost::fibers::clock_type::now(); + BOOST_CHECK(m.try_lock_until(boost::fibers::clock_type::now() + ms(250)) == false); + boost::fibers::clock_type::time_point t1 = boost::fibers::clock_type::now(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ns(5000000)+ms(1000)); // within 5 ms +} + +void fn15( boost::fibers::recursive_mutex & m) +{ + boost::fibers::clock_type::time_point t0 = boost::fibers::clock_type::now(); + m.lock(); + boost::fibers::clock_type::time_point t1 = boost::fibers::clock_type::now(); + m.lock(); + m.unlock(); + m.unlock(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ns(2500000)+ms(1000)); // within 2.5 ms +} + +void fn16( boost::fibers::recursive_mutex & m) +{ + boost::fibers::clock_type::time_point t0 = boost::fibers::clock_type::now(); + while (!m.try_lock()); + boost::fibers::clock_type::time_point t1 = boost::fibers::clock_type::now(); + BOOST_CHECK(m.try_lock()); + m.unlock(); + m.unlock(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ns(50000000)+ms(1000)); // within 50 ms +} + +void fn17( boost::fibers::mutex & m) +{ + boost::fibers::clock_type::time_point t0 = boost::fibers::clock_type::now(); + m.lock(); + boost::fibers::clock_type::time_point t1 = boost::fibers::clock_type::now(); + m.unlock(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ns(2500000)+ms(1000)); // within 2.5 ms +} + +void fn18( boost::fibers::mutex & m) +{ + boost::fibers::clock_type::time_point t0 = boost::fibers::clock_type::now(); + while (!m.try_lock()) ; + boost::fibers::clock_type::time_point t1 = boost::fibers::clock_type::now(); + m.unlock(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ns(50000000)+ms(1000)); // within 50 ms +} + template< typename M > struct test_lock { @@ -45,51 +238,36 @@ struct test_lock BOOST_CHECK(lk ? true : false); } }; -#if 0 + template< typename M > -struct test_trylock +struct test_exclusive { typedef M mutex_type; - typedef typename M::scoped_try_lock try_lock_type; + typedef typename M::scoped_lock lock_type; void operator()() { - mutex_type mutex; - boost::fibers::condition condition; + value1 = 0; + value2 = 0; + BOOST_CHECK_EQUAL( 0, value1); + BOOST_CHECK_EQUAL( 0, value2); - // Test the lock's constructors. - { - try_lock_type lock(mutex); - BOOST_CHECK(lock ? true : false); - } - { - try_lock_type lock(mutex, boost::defer_lock); - BOOST_CHECK(!lock); - } - try_lock_type lock(mutex); - BOOST_CHECK(lock ? true : false); + mutex_type mtx; + boost::fibers::fiber f1( + boost::bind( & fn1< mutex_type >, boost::ref( mtx) ) ); + boost::fibers::fiber f2( + boost::bind( & fn2< mutex_type >, boost::ref( mtx) ) ); + BOOST_ASSERT( f1); + BOOST_ASSERT( f2); + BOOST_CHECK_EQUAL( 1, value1); + BOOST_CHECK_EQUAL( 1, value2); - // Construct and initialize an xtime for a fast time out. - boost::xtime xt = delay(0, 100); - - // Test the lock and the mutex with condition variables. - // No one is going to notify this condition variable. We expect to - // time out. - BOOST_CHECK(!condition.timed_wait(lock, xt)); - BOOST_CHECK(lock ? true : false); - - // Test the lock, unlock and trylock methods. - lock.unlock(); - BOOST_CHECK(!lock); - lock.lock(); - BOOST_CHECK(lock ? true : false); - lock.unlock(); - BOOST_CHECK(!lock); - BOOST_CHECK(lock.try_lock()); - BOOST_CHECK(lock ? true : false); + f1.join(); + f2.join(); + BOOST_CHECK_EQUAL( 1, value1); + BOOST_CHECK_EQUAL( 2, value2); } }; -#endif template< typename M > struct test_recursive_lock @@ -108,66 +286,58 @@ struct test_recursive_lock void do_test_mutex() { test_lock< boost::fibers::mutex >()(); + test_exclusive< boost::fibers::mutex >()(); + + { + boost::fibers::mutex mtx; + mtx.lock(); + boost::fibers::fiber f( boost::bind( & fn17, boost::ref( mtx) ) ); + boost::this_fiber::sleep_for( ms(250) ); + mtx.unlock(); + f.join(); + } + + { + boost::fibers::mutex mtx; + mtx.lock(); + boost::fibers::fiber f( boost::bind( & fn18, boost::ref( mtx) ) ); + boost::this_fiber::sleep_for( ms(250) ); + mtx.unlock(); + f.join(); + } } -int value1 = 0; -int value2 = 0; - -void fn1( boost::fibers::mutex & mtx) -{ - boost::fibers::mutex::scoped_lock lk( mtx); - ++value1; - for ( int i = 0; i < 3; ++i) - boost::this_fiber::yield(); -} - -void fn2( boost::fibers::mutex & mtx) -{ - ++value2; - boost::fibers::mutex::scoped_lock lk( mtx); - ++value2; -} - -void test_locking() +void test_mutex() { boost::fibers::round_robin ds; boost::fibers::scheduling_algorithm( & ds); - boost::fibers::fiber s( & do_test_mutex); - s.join(); - BOOST_ASSERT( ! s); -} - -void test_exclusive() -{ - boost::fibers::round_robin ds; - boost::fibers::scheduling_algorithm( & ds); - - value1 = 0; - value2 = 0; - BOOST_CHECK_EQUAL( 0, value1); - BOOST_CHECK_EQUAL( 0, value2); - - boost::fibers::mutex mtx; - boost::fibers::fiber s1( - boost::bind( & fn1, boost::ref( mtx) ) ); - boost::fibers::fiber s2( - boost::bind( & fn2, boost::ref( mtx) ) ); - BOOST_ASSERT( s1); - BOOST_ASSERT( s2); - BOOST_CHECK_EQUAL( 1, value1); - BOOST_CHECK_EQUAL( 1, value2); - - s1.join(); - s2.join(); - BOOST_CHECK_EQUAL( 1, value1); - BOOST_CHECK_EQUAL( 2, value2); + boost::fibers::fiber( & do_test_mutex).join(); } void do_test_recursive_mutex() { test_lock< boost::fibers::recursive_mutex >()(); + test_exclusive< boost::fibers::recursive_mutex >()(); test_recursive_lock< boost::fibers::recursive_mutex >()(); + + { + boost::fibers::recursive_mutex mtx; + mtx.lock(); + boost::fibers::fiber f( boost::bind( & fn15, boost::ref( mtx) ) ); + boost::this_fiber::sleep_for( ms(250) ); + mtx.unlock(); + f.join(); + } + + { + boost::fibers::recursive_mutex mtx; + mtx.lock(); + boost::fibers::fiber f( boost::bind( & fn16, boost::ref( mtx) ) ); + boost::this_fiber::sleep_for( ms(250) ); + mtx.unlock(); + f.join(); + } } void test_recursive_mutex() @@ -178,19 +348,141 @@ void test_recursive_mutex() boost::fibers::fiber( do_test_recursive_mutex).join(); } -void do_test_recursive_try_mutex() +void do_test_timed_mutex() { - test_lock< boost::fibers::recursive_try_mutex >()(); - //test_trylock< boost::fibers::recursive_try_mutex >()(); - test_recursive_lock< boost::fibers::recursive_try_mutex >()(); + test_lock< boost::fibers::timed_mutex >()(); + test_exclusive< boost::fibers::timed_mutex >()(); + + { + boost::fibers::timed_mutex timed_mtx; + timed_mtx.lock(); + boost::fibers::fiber f( boost::bind( & fn3, boost::ref( timed_mtx) ) ); + boost::this_fiber::sleep_for( ms(250) ); + timed_mtx.unlock(); + f.join(); + } + + { + boost::fibers::timed_mutex timed_mtx; + timed_mtx.lock(); + boost::fibers::fiber f( boost::bind( & fn4, boost::ref( timed_mtx) ) ); + boost::this_fiber::sleep_for( ms(250) ); + timed_mtx.unlock(); + f.join(); + } + + { + boost::fibers::timed_mutex timed_mtx; + timed_mtx.lock(); + boost::fibers::fiber f( boost::bind( & fn5, boost::ref( timed_mtx) ) ); + boost::this_fiber::sleep_for( ms(250) ); + timed_mtx.unlock(); + f.join(); + } + + { + boost::fibers::timed_mutex timed_mtx; + timed_mtx.lock(); + boost::fibers::fiber f( boost::bind( & fn6, boost::ref( timed_mtx) ) ); + boost::this_fiber::sleep_for( ms(300) ); + timed_mtx.unlock(); + f.join(); + } + + { + boost::fibers::timed_mutex timed_mtx; + timed_mtx.lock(); + boost::fibers::fiber f( boost::bind( & fn7, boost::ref( timed_mtx) ) ); + boost::this_fiber::sleep_for( ms(250) ); + timed_mtx.unlock(); + f.join(); + } + + { + boost::fibers::timed_mutex timed_mtx; + timed_mtx.lock(); + boost::fibers::fiber f( boost::bind( & fn8, boost::ref( timed_mtx) ) ); + boost::this_fiber::sleep_for( ms(300) + ms(1000) ); + timed_mtx.unlock(); + f.join(); + } } -void test_recursive_try_mutex() +void test_timed_mutex() { boost::fibers::round_robin ds; boost::fibers::scheduling_algorithm( & ds); - boost::fibers::fiber( do_test_recursive_try_mutex).join(); + boost::fibers::fiber( & do_test_timed_mutex).join(); +} + +void do_test_recursive_timed_mutex() +{ + test_lock< boost::fibers::recursive_timed_mutex >()(); + test_exclusive< boost::fibers::recursive_timed_mutex >()(); + test_recursive_lock< boost::fibers::recursive_timed_mutex >()(); + + { + boost::fibers::recursive_timed_mutex timed_mtx; + timed_mtx.lock(); + boost::fibers::fiber f( boost::bind( & fn9, boost::ref( timed_mtx) ) ); + boost::this_fiber::sleep_for( ms(250) ); + timed_mtx.unlock(); + f.join(); + } + + { + boost::fibers::recursive_timed_mutex timed_mtx; + timed_mtx.lock(); + boost::fibers::fiber f( boost::bind( & fn10, boost::ref( timed_mtx) ) ); + boost::this_fiber::sleep_for( ms(250) ); + timed_mtx.unlock(); + f.join(); + } + + { + boost::fibers::recursive_timed_mutex timed_mtx; + timed_mtx.lock(); + boost::fibers::fiber f( boost::bind( & fn11, boost::ref( timed_mtx) ) ); + boost::this_fiber::sleep_for( ms(250) ); + timed_mtx.unlock(); + f.join(); + } + + { + boost::fibers::recursive_timed_mutex timed_mtx; + timed_mtx.lock(); + boost::fibers::fiber f( boost::bind( & fn12, boost::ref( timed_mtx) ) ); + boost::this_fiber::sleep_for( ms(400) ); + timed_mtx.unlock(); + f.join(); + } + + { + boost::fibers::recursive_timed_mutex timed_mtx; + timed_mtx.lock(); + boost::fibers::fiber f( boost::bind( & fn13, boost::ref( timed_mtx) ) ); + boost::this_fiber::sleep_for( ms(250) ); + timed_mtx.unlock(); + f.join(); + } + + { + boost::fibers::recursive_timed_mutex timed_mtx; + timed_mtx.lock(); + boost::fibers::fiber f( boost::bind( & fn14, boost::ref( timed_mtx) ) ); + boost::this_fiber::sleep_for( ms(300) ); + timed_mtx.unlock(); + f.join(); + } +} + +void test_recursive_timed_mutex() +{ + boost::fibers::round_robin ds; + boost::fibers::scheduling_algorithm( & ds); + + boost::fibers::fiber( & do_test_recursive_timed_mutex).join(); } boost::unit_test::test_suite * init_unit_test_suite( int, char* []) @@ -198,10 +490,10 @@ boost::unit_test::test_suite * init_unit_test_suite( int, char* []) boost::unit_test::test_suite * test = BOOST_TEST_SUITE("Boost.Fiber: mutex test suite"); - test->add( BOOST_TEST_CASE( & test_locking) ); - test->add( BOOST_TEST_CASE( & test_exclusive) ); + test->add( BOOST_TEST_CASE( & test_mutex) ); test->add( BOOST_TEST_CASE( & test_recursive_mutex) ); - test->add( BOOST_TEST_CASE( & test_recursive_try_mutex) ); + test->add( BOOST_TEST_CASE( & test_timed_mutex) ); + test->add( BOOST_TEST_CASE( & test_recursive_timed_mutex) ); return test; }