From 3f5e076f3a2294beb97976990148676a44c5459d Mon Sep 17 00:00:00 2001 From: Oliver Kowalke Date: Sat, 19 Sep 2015 17:01:20 +0200 Subject: [PATCH] class mutex added --- build/Jamfile.v2 | 1 + include/boost/fiber/all.hpp | 1 + include/boost/fiber/context.hpp | 6 +- include/boost/fiber/mutex.hpp | 62 +++++ src/context.cpp | 12 +- src/mutex.cpp | 116 ++++++++ test/Jamfile.v2 | 30 ++ test/test_mutex.cpp | 472 ++++++++++++++++++++++++++++++++ test/test_mutex_mt.cpp | 145 ++++++++++ 9 files changed, 836 insertions(+), 9 deletions(-) create mode 100644 include/boost/fiber/mutex.hpp create mode 100644 src/mutex.cpp create mode 100644 test/test_mutex.cpp create mode 100644 test/test_mutex_mt.cpp diff --git a/build/Jamfile.v2 b/build/Jamfile.v2 index 3ca6eac8..4040cc13 100644 --- a/build/Jamfile.v2 +++ b/build/Jamfile.v2 @@ -30,6 +30,7 @@ lib boost_fiber : context.cpp detail/spinlock.cpp fiber.cpp + mutex.cpp scheduler.cpp : shared:../../context/build//boost_context ; diff --git a/include/boost/fiber/all.hpp b/include/boost/fiber/all.hpp index 56e9bb3f..7b0c9490 100644 --- a/include/boost/fiber/all.hpp +++ b/include/boost/fiber/all.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/include/boost/fiber/context.hpp b/include/boost/fiber/context.hpp index b1504941..87cbffd0 100644 --- a/include/boost/fiber/context.hpp +++ b/include/boost/fiber/context.hpp @@ -134,8 +134,6 @@ private: void set_terminated_() noexcept; - void suspend_() noexcept; - protected: virtual void deallocate() { } @@ -231,7 +229,7 @@ public: // notify waiting (joining) fibers release(); // switch to another fiber - suspend_(); + suspend(); BOOST_ASSERT_MSG( false, "fiber already terminated"); }), wait_queue_() { @@ -247,6 +245,8 @@ public: void resume(); + void suspend() noexcept; + void release() noexcept; void join() noexcept; diff --git a/include/boost/fiber/mutex.hpp b/include/boost/fiber/mutex.hpp new file mode 100644 index 00000000..293e5009 --- /dev/null +++ b/include/boost/fiber/mutex.hpp @@ -0,0 +1,62 @@ + +// 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_MUTEX_H +#define BOOST_FIBERS_MUTEX_H + +#include + +#include + +#include +#include +#include + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace fibers { + +class BOOST_FIBERS_DECL mutex { +private: + enum class mutex_status { + locked = 0, + unlocked + }; + + typedef context::wait_queue_t wait_queue_t; + + std::atomic< mutex_status > state_; + context::id owner_; + wait_queue_t wait_queue_; + detail::spinlock wait_queue_splk_; + + bool lock_if_unlocked_(); + +public: + mutex(); + + ~mutex(); + + mutex( mutex const&) = delete; + mutex & operator=( mutex const&) = delete; + + void lock(); + + bool try_lock(); + + void unlock(); +}; + +}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_FIBERS_MUTEX_H diff --git a/src/context.cpp b/src/context.cpp index 29bf954c..1492464f 100644 --- a/src/context.cpp +++ b/src/context.cpp @@ -50,11 +50,6 @@ context::set_terminated_() noexcept { scheduler_->set_terminated( this); } -void -context::suspend_() noexcept { - scheduler_->re_schedule( this); -} - // main fiber context context::context( main_context_t) : ready_hook_(), @@ -117,6 +112,11 @@ context::resume() { ctx_(); } +void +context::suspend() noexcept { + scheduler_->re_schedule( this); +} + void context::release() noexcept { BOOST_ASSERT( is_terminated() ); @@ -183,7 +183,7 @@ context::set_ready( context * ctx) noexcept { scheduler_->set_ready( ctx); } else { // remote - scheduler_->set_remote_ready( ctx); + ctx->scheduler_->set_remote_ready( ctx); } } diff --git a/src/mutex.cpp b/src/mutex.cpp new file mode 100644 index 00000000..cf632e7a --- /dev/null +++ b/src/mutex.cpp @@ -0,0 +1,116 @@ + +// 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/mutex.hpp" + +#include + +#include + +#include "boost/fiber/scheduler.hpp" +#include "boost/fiber/interruption.hpp" + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace fibers { + +bool +mutex::lock_if_unlocked_() { + if ( mutex_status::locked == state_.load( std::memory_order_relaxed) ) { + return false; + } + + if ( mutex_status::unlocked != state_.exchange( mutex_status::locked, std::memory_order_acquire) ) { + return false; + } + + BOOST_ASSERT( ! owner_); + owner_ = context::active()->get_id(); + return true; +} + +mutex::mutex() : + state_( mutex_status::unlocked), + owner_(), + wait_queue_(), + wait_queue_splk_() { +} + +mutex::~mutex() { + BOOST_ASSERT( ! owner_); + BOOST_ASSERT( wait_queue_.empty() ); +} + +void +mutex::lock() { + context * ctx = context::active(); + for (;;) { + try { + if ( lock_if_unlocked_() ) { + return; + } + + // store this fiber in order to be notified later + detail::spinlock_lock lk( wait_queue_splk_); + BOOST_ASSERT( ! ctx->wait_is_linked() ); + wait_queue_.push_back( * ctx); + lk.unlock(); + + // suspend this fiber + ctx->suspend(); + + // remove fiber from wait-queue + lk.lock(); + ctx->wait_unlink(); + } catch (...) { + // remove fiber from wait-queue + detail::spinlock_lock lk( wait_queue_splk_); + ctx->wait_unlink(); + throw; + } + } +} + +bool +mutex::try_lock() { + if ( lock_if_unlocked_() ) { + return true; + } + + // let other fiber release the lock + context::active()->yield(); + return false; +} + +void +mutex::unlock() { + BOOST_ASSERT( mutex_status::locked == state_); + BOOST_ASSERT( context::active()->get_id() == owner_); + + detail::spinlock_lock lk( wait_queue_splk_); + context * ctx( nullptr); + if ( ! wait_queue_.empty() ) { + ctx = & wait_queue_.front(); + wait_queue_.pop_front(); + BOOST_ASSERT( nullptr != ctx); + } + lk.unlock(); + owner_ = context::id(); + state_ = mutex_status::unlocked; + + if ( nullptr != ctx) { + context::active()->set_ready( ctx); + } +} + +}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 4b919d5d..f3504ae9 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -41,3 +41,33 @@ run test_fiber.cpp : cxx11_variadic_macros cxx11_variadic_templates cxx14_initialized_lambda_captures ] ; + +run test_mutex.cpp : + : : + [ requires cxx11_constexpr + cxx11_decltype + cxx11_deleted_functions + cxx11_explicit_conversion_operators + cxx11_hdr_tuple cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_template_aliases + cxx11_rvalue_references + cxx11_variadic_macros + cxx11_variadic_templates + cxx14_initialized_lambda_captures ] ; + +run test_mutex_mt.cpp : + : : + [ requires cxx11_constexpr + cxx11_decltype + cxx11_deleted_functions + cxx11_explicit_conversion_operators + cxx11_hdr_tuple cxx11_lambdas + cxx11_noexcept + cxx11_nullptr + cxx11_template_aliases + cxx11_rvalue_references + cxx11_variadic_macros + cxx11_variadic_templates + cxx14_initialized_lambda_captures ] ; diff --git a/test/test_mutex.cpp b/test/test_mutex.cpp new file mode 100644 index 00000000..b6b17fe0 --- /dev/null +++ b/test/test_mutex.cpp @@ -0,0 +1,472 @@ + +// 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) +// +// This test is based on the tests of Boost.Thread + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +typedef std::chrono::nanoseconds ns; +typedef std::chrono::milliseconds ms; + +int value1 = 0; +int value2 = 0; + +template< typename M > +void fn1( M & mtx) { + typedef M mutex_type; + typename std::unique_lock< mutex_type > 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 std::unique_lock< mutex_type > lk( mtx); + ++value2; +} +#if 0 +void fn3( boost::fibers::timed_mutex & m) +{ + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + m.lock(); + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::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) +{ + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + while ( ! m.try_lock() ); + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::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) +{ + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + BOOST_CHECK( m.try_lock_for(ms(300)+ms(2000)) == true); + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::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) +{ + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + BOOST_CHECK(m.try_lock_for(ms(250)) == false); + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ns(5000000)+ms(1000)); // within 5 ms +} + +void fn7( boost::fibers::timed_mutex & m) +{ + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + BOOST_CHECK(m.try_lock_until(std::chrono::steady_clock::now() + ms(300) + ms(1000)) == true); + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::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) +{ + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + BOOST_CHECK(m.try_lock_until(std::chrono::steady_clock::now() + ms(250)) == false); + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + ns d = t1 - t0 - ms(250); + ns r = ns(5000000)+ms(1000); // within 6ms + BOOST_CHECK(d < r); // within 6ms +} + +void fn9( boost::fibers::recursive_timed_mutex & m) +{ + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + m.lock(); + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::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) +{ + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + while (!m.try_lock()) ; + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::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) +{ + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + BOOST_CHECK(m.try_lock_for(ms(300)+ms(1000)) == true); + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::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) +{ + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + BOOST_CHECK(m.try_lock_for(ms(250)) == false); + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::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) +{ + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + BOOST_CHECK(m.try_lock_until(std::chrono::steady_clock::now() + ms(300) + ms(1000)) == true); + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::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) +{ + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + BOOST_CHECK(m.try_lock_until(std::chrono::steady_clock::now() + ms(250)) == false); + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now(); + ns d = t1 - t0 - ms(250); + BOOST_CHECK(d < ns(5000000)+ms(1000)); // within 5 ms +} + +void fn15( boost::fibers::recursive_mutex & m) +{ + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + m.lock(); + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::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) +{ + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + while (!m.try_lock()); + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::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 +} +#endif + +void fn17( boost::fibers::mutex & m) { + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + m.lock(); + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::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) { + std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); + while (!m.try_lock()) ; + std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::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 { + typedef M mutex_type; + typedef typename std::unique_lock< M > lock_type; + + void operator()() { + mutex_type mtx; + + // Test the lock's constructors. + { + lock_type lk(mtx, std::defer_lock); + BOOST_CHECK(!lk); + } + lock_type lk(mtx); + BOOST_CHECK(lk ? true : false); + + // Test the lock and unlock methods. + lk.unlock(); + BOOST_CHECK(!lk); + lk.lock(); + BOOST_CHECK(lk ? true : false); + } +}; + +template< typename M > +struct test_exclusive { + typedef M mutex_type; + typedef typename std::unique_lock< M > lock_type; + + void operator()() { + value1 = 0; + value2 = 0; + BOOST_CHECK_EQUAL( 0, value1); + BOOST_CHECK_EQUAL( 0, value2); + + mutex_type mtx; + boost::fibers::fiber f1( & fn1< mutex_type >, std::ref( mtx) ); + boost::fibers::fiber f2( & fn2< mutex_type >, std::ref( mtx) ); + BOOST_ASSERT( f1); + BOOST_ASSERT( f2); + + f1.join(); + f2.join(); + BOOST_CHECK_EQUAL( 1, value1); + BOOST_CHECK_EQUAL( 2, value2); + } +}; + +template< typename M > +struct test_recursive_lock { + typedef M mutex_type; + typedef typename std::unique_lock< M > lock_type; + + void operator()() + { + mutex_type mx; + lock_type lock1(mx); + lock_type lock2(mx); + } +}; + +void do_test_mutex() { + test_lock< boost::fibers::mutex >()(); + test_exclusive< boost::fibers::mutex >()(); + + { + boost::fibers::mutex mtx; + mtx.lock(); + boost::fibers::fiber f( & fn17, std::ref( mtx) ); + boost::this_fiber::sleep_for( ms(250) ); + mtx.unlock(); + f.join(); + } + + { + boost::fibers::mutex mtx; + mtx.lock(); + boost::fibers::fiber f( & fn18, std::ref( mtx) ); + boost::this_fiber::sleep_for( ms(250) ); + mtx.unlock(); + f.join(); + } +} + +void test_mutex() { + boost::fibers::fiber( & do_test_mutex).join(); +} + +#if 0 +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( & fn15, std::ref( mtx) ); + boost::this_fiber::sleep_for( ms(250) ); + mtx.unlock(); + f.join(); + } + + { + boost::fibers::recursive_mutex mtx; + mtx.lock(); + boost::fibers::fiber f( & fn16, std::ref( mtx) ); + boost::this_fiber::sleep_for( ms(250) ); + mtx.unlock(); + f.join(); + } +} + +void test_recursive_mutex() +{ + boost::fibers::fiber( do_test_recursive_mutex).join(); +} + +void do_test_timed_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( & fn3, std::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( & fn4, std::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( & fn5, std::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( & fn6, std::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( & fn7, std::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( & fn8, std::ref( timed_mtx) ); + boost::this_fiber::sleep_for( ms(300) + ms(1000) ); + timed_mtx.unlock(); + f.join(); + } +} + +void test_timed_mutex() +{ + 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( & fn9, std::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( & fn10, std::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( & fn11, std::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( & fn12, std::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( & fn13, std::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( & fn14, std::ref( timed_mtx) ); + boost::this_fiber::sleep_for( ms(300) ); + timed_mtx.unlock(); + f.join(); + } +} + +void test_recursive_timed_mutex() +{ + boost::fibers::fiber( & do_test_recursive_timed_mutex).join(); +} +#endif + +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_mutex) ); + //test->add( BOOST_TEST_CASE( & test_recursive_mutex) ); + //test->add( BOOST_TEST_CASE( & test_timed_mutex) ); + //test->add( BOOST_TEST_CASE( & test_recursive_timed_mutex) ); + + return test; +} diff --git a/test/test_mutex_mt.cpp b/test/test_mutex_mt.cpp new file mode 100644 index 00000000..d96c0fcf --- /dev/null +++ b/test/test_mutex_mt.cpp @@ -0,0 +1,145 @@ + +// 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) +// +// This test is based on the tests of Boost.Thread + +#include +#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 Mtx > +void g( boost::barrier & b, Mtx & m) { + b.wait(); + m.lock(); + value1 = 3; + m.unlock(); +} + +template< typename Mtx > +void f( boost::barrier & b, Mtx & m) { + b.wait(); + m.lock(); + value2 = 7; + m.unlock(); +} + +template< typename Mtx > +void fn1( boost::barrier & b, Mtx & m) { + boost::fibers::fiber( std::bind( g< Mtx >, std::ref( b), std::ref( m) ) ).join(); +} + +template< typename Mtx > +void fn2( boost::barrier & b, Mtx & m) { + boost::fibers::fiber( std::bind( f< Mtx >, std::ref( b), std::ref( m) ) ).join(); +} + +void test_mutex() { + for ( int i = 0; i < 10; ++i) { + boost::fibers::mutex mtx; + mtx.lock(); + boost::barrier b( 3); + boost::thread t1( fn1< boost::fibers::mutex >, std::ref( b), std::ref( mtx) ); + boost::thread t2( fn2< boost::fibers::mutex >, std::ref( b), std::ref( mtx) ); + b.wait(); + boost::this_thread::sleep_for( ms( 250) ); + mtx.unlock(); + t1.join(); + t2.join(); + BOOST_CHECK( 3 == value1); + BOOST_CHECK( 7 == value2); + } +} +#if 0 +void test_recursive_mutex() +{ + for ( int i = 0; i < 10; ++i) + { + boost::fibers::recursive_mutex mtx; + mtx.lock(); + boost::barrier b( 3); + std::thread t1( fn1< boost::fibers::recursive_mutex >, std::ref( b), std::ref( mtx) ); + std::thread t2( fn2< boost::fibers::recursive_mutex >, std::ref( b), std::ref( mtx) ); + b.wait(); + std::this_thread::sleep_for( ms( 250) ); + mtx.unlock(); + t1.join(); + t2.join(); + BOOST_CHECK( 3 == value1); + BOOST_CHECK( 7 == value2); + } +} + +void test_recursive_timed_mutex() +{ + for ( int i = 0; i < 10; ++i) + { + boost::fibers::recursive_timed_mutex mtx; + mtx.lock(); + boost::barrier b( 3); + std::thread t1( fn1< boost::fibers::recursive_timed_mutex >, std::ref( b), std::ref( mtx) ); + std::thread t2( fn2< boost::fibers::recursive_timed_mutex >, std::ref( b), std::ref( mtx) ); + b.wait(); + std::this_thread::sleep_for( ms( 250) ); + mtx.unlock(); + t1.join(); + t2.join(); + BOOST_CHECK( 3 == value1); + BOOST_CHECK( 7 == value2); + } +} + +void test_timed_mutex() +{ + for ( int i = 0; i < 10; ++i) + { + boost::fibers::timed_mutex mtx; + mtx.lock(); + boost::barrier b( 3); + std::thread t1( fn1< boost::fibers::timed_mutex >, std::ref( b), std::ref( mtx) ); + std::thread t2( fn2< boost::fibers::timed_mutex >, std::ref( b), std::ref( mtx) ); + b.wait(); + std::this_thread::sleep_for( ms( 250) ); + mtx.unlock(); + t1.join(); + t2.join(); + BOOST_CHECK( 3 == value1); + BOOST_CHECK( 7 == value2); + } +} +#endif +void test_dummy() {} + +boost::unit_test::test_suite * init_unit_test_suite( int, char* []) +{ + boost::unit_test::test_suite * test = + BOOST_TEST_SUITE("Boost.Fiber: multithreaded mutex test suite"); + +#if ! defined(BOOST_FIBERS_NO_ATOMICS) + test->add( BOOST_TEST_CASE( & test_mutex) ); + //test->add( BOOST_TEST_CASE( & test_recursive_mutex) ); + //test->add( BOOST_TEST_CASE( & test_recursive_timed_mutex) ); + //test->add( BOOST_TEST_CASE( & test_timed_mutex) ); +#else + test->add( BOOST_TEST_CASE( & test_dummy) ); +#endif + + return test; +}