From c8a1c4e4df175e95473abc5f068b6f3624d11086 Mon Sep 17 00:00:00 2001 From: Oliver Kowalke Date: Sat, 19 Sep 2015 18:14:17 +0200 Subject: [PATCH] class recursive_timed_mutex added --- build/Jamfile.v2 | 1 + include/boost/fiber/all.hpp | 1 + include/boost/fiber/recursive_timed_mutex.hpp | 82 +++++++++ src/recursive_timed_mutex.cpp | 170 ++++++++++++++++++ test/test_mutex.cpp | 33 ++-- test/test_mutex_mt.cpp | 39 ++-- 6 files changed, 284 insertions(+), 42 deletions(-) create mode 100644 include/boost/fiber/recursive_timed_mutex.hpp create mode 100644 src/recursive_timed_mutex.cpp diff --git a/build/Jamfile.v2 b/build/Jamfile.v2 index b5a11cac..fd688d00 100644 --- a/build/Jamfile.v2 +++ b/build/Jamfile.v2 @@ -32,6 +32,7 @@ lib boost_fiber fiber.cpp mutex.cpp recursive_mutex.cpp + recursive_timed_mutex.cpp timed_mutex.cpp scheduler.cpp : shared:../../context/build//boost_context diff --git a/include/boost/fiber/all.hpp b/include/boost/fiber/all.hpp index 289f4983..2845cfad 100644 --- a/include/boost/fiber/all.hpp +++ b/include/boost/fiber/all.hpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include diff --git a/include/boost/fiber/recursive_timed_mutex.hpp b/include/boost/fiber/recursive_timed_mutex.hpp new file mode 100644 index 00000000..64b0fb05 --- /dev/null +++ b/include/boost/fiber/recursive_timed_mutex.hpp @@ -0,0 +1,82 @@ + +// 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 + +namespace boost { +namespace fibers { + +class BOOST_FIBERS_DECL recursive_timed_mutex { +private: + enum class mutex_status { + locked = 0, + unlocked + }; + + typedef context::wait_queue_t wait_queue_t; + + std::atomic< mutex_status > state_; + std::atomic< context * > owner_; + std::size_t count_; + wait_queue_t wait_queue_; + detail::spinlock wait_queue_splk_; + + bool lock_if_unlocked_(); + + bool try_lock_until_( std::chrono::steady_clock::time_point const& timeout_time); + +public: + recursive_timed_mutex(); + + ~recursive_timed_mutex(); + + recursive_timed_mutex( recursive_timed_mutex const&) = delete; + recursive_timed_mutex & operator=( recursive_timed_mutex const&) = delete; + + void lock(); + + bool try_lock(); + + template< typename Clock, typename Duration > + bool try_lock_until( std::chrono::time_point< Clock, Duration > const& timeout_time_) { + std::chrono::steady_clock::time_point timeout_time( + detail::convert( timeout_time_) ); + return try_lock_until_( timeout_time); + } + + template< typename Rep, typename Period > + bool try_lock_for( std::chrono::duration< Rep, Period > const& timeout_duration) { + return try_lock_until_( std::chrono::steady_clock::now() + timeout_duration); + } + + void unlock(); +}; + +}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif + +#endif // BOOST_FIBERS_RECURSIVE_TIMED_MUTEX_H diff --git a/src/recursive_timed_mutex.cpp b/src/recursive_timed_mutex.cpp new file mode 100644 index 00000000..83071712 --- /dev/null +++ b/src/recursive_timed_mutex.cpp @@ -0,0 +1,170 @@ + +// 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/scheduler.hpp" +#include "boost/fiber/interruption.hpp" + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_PREFIX +#endif + +namespace boost { +namespace fibers { + +bool +recursive_timed_mutex::lock_if_unlocked_() { + if ( mutex_status::locked == state_.load( std::memory_order_relaxed) ) { + if ( context::active() == owner_) { + ++count_; + return true; + } else { + return false; + } + } + + if ( mutex_status::unlocked != state_.exchange( mutex_status::locked, std::memory_order_acquire) ) { + if ( context::active() == owner_) { + ++count_; + return true; + } else { + return false; + } + } + + BOOST_ASSERT( nullptr == owner_); + owner_ = context::active(); + ++count_; + return true; +} + +recursive_timed_mutex::recursive_timed_mutex() : + state_( mutex_status::unlocked), + owner_( nullptr), + count_( 0), + wait_queue_(), + wait_queue_splk_() { +} + +recursive_timed_mutex::~recursive_timed_mutex() { + BOOST_ASSERT( nullptr == owner_); + BOOST_ASSERT( 0 == count_); + BOOST_ASSERT( wait_queue_.empty() ); +} + +void +recursive_timed_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 +recursive_timed_mutex::try_lock() { + if ( lock_if_unlocked_() ) { + return true; + } + + // let other fiber release the lock + context::active()->yield(); + return false; +} + +bool +recursive_timed_mutex::try_lock_until_( std::chrono::steady_clock::time_point const& timeout_time) { + context * ctx = context::active(); + for (;;) { + try { + if ( std::chrono::steady_clock::now() > timeout_time) { + return false; + } + + if ( lock_if_unlocked_() ) { + return true; + } + + // 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 until notified or timed-out + if ( ! ctx->wait_until( timeout_time) ) { + // remove fiber from wait-queue + lk.lock(); + ctx->wait_unlink(); + return false; + } + + // 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; + } + } +} + +void +recursive_timed_mutex::unlock() { + BOOST_ASSERT( mutex_status::locked == state_); + BOOST_ASSERT( context::active() == owner_); + + detail::spinlock_lock lk( wait_queue_splk_); + context * ctx( nullptr); + if ( 0 == --count_) { + if ( ! wait_queue_.empty() ) { + ctx = & wait_queue_.front(); + wait_queue_.pop_front(); + BOOST_ASSERT( nullptr != ctx); + } + lk.unlock(); + owner_ = nullptr; + state_.store( mutex_status::unlocked, std::memory_order_release); + + if ( nullptr != ctx) { + context::active()->set_ready( ctx); + } + } +} + +}} + +#ifdef BOOST_HAS_ABI_HEADERS +# include BOOST_ABI_SUFFIX +#endif diff --git a/test/test_mutex.cpp b/test/test_mutex.cpp index 381bda65..2f68dfa8 100644 --- a/test/test_mutex.cpp +++ b/test/test_mutex.cpp @@ -93,9 +93,8 @@ void fn8( boost::fibers::timed_mutex & m) { ns r = ns(5000000)+ms(1000); // within 6ms BOOST_CHECK(d < r); // within 6ms } -#if 0 -void fn9( boost::fibers::recursive_timed_mutex & m) -{ + +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(); @@ -106,8 +105,7 @@ void fn9( boost::fibers::recursive_timed_mutex & m) BOOST_CHECK(d < ns(2500000)+ms(1000)); // within 2.5 ms } -void fn10( boost::fibers::recursive_timed_mutex & m) -{ +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(); @@ -118,8 +116,7 @@ void fn10( boost::fibers::recursive_timed_mutex & m) BOOST_CHECK(d < ns(50000000)+ms(1000)); // within 50 ms } -void fn11( boost::fibers::recursive_timed_mutex & m) -{ +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(); @@ -130,8 +127,7 @@ void fn11( boost::fibers::recursive_timed_mutex & m) BOOST_CHECK(d < ns(5000000)+ms(1000)); // within 5 ms } -void fn12( boost::fibers::recursive_timed_mutex & m) -{ +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(); @@ -139,8 +135,7 @@ void fn12( boost::fibers::recursive_timed_mutex & m) BOOST_CHECK(d < ns(5000000)+ms(1000)); // within 5 ms } -void fn13( boost::fibers::recursive_timed_mutex & m) -{ +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(); @@ -149,15 +144,14 @@ void fn13( boost::fibers::recursive_timed_mutex & m) BOOST_CHECK(d < ns(5000000)+ms(1000)); // within 5 ms } -void fn14( boost::fibers::recursive_timed_mutex & m) -{ +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 } -#endif + void fn15( boost::fibers::recursive_mutex & m) { std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now(); m.lock(); @@ -375,9 +369,8 @@ void do_test_timed_mutex() { void test_timed_mutex() { boost::fibers::fiber( & do_test_timed_mutex).join(); } -#if 0 -void do_test_recursive_timed_mutex() -{ + +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 >()(); @@ -437,11 +430,9 @@ void do_test_recursive_timed_mutex() } } -void test_recursive_timed_mutex() -{ +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 = @@ -450,7 +441,7 @@ boost::unit_test::test_suite * init_unit_test_suite( int, char* []) { 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) ); + 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 index 195c01c7..f54bda5a 100644 --- a/test/test_mutex_mt.cpp +++ b/test/test_mutex_mt.cpp @@ -85,26 +85,6 @@ void test_recursive_mutex() { } } -#if 0 -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); - } -} -#endif void test_timed_mutex() { for ( int i = 0; i < 10; ++i) { boost::fibers::timed_mutex mtx; @@ -122,6 +102,23 @@ void test_timed_mutex() { } } +void test_recursive_timed_mutex() { + for ( int i = 0; i < 10; ++i) { + boost::fibers::recursive_timed_mutex mtx; + mtx.lock(); + boost::barrier b( 3); + boost::thread t1( fn1< boost::fibers::recursive_timed_mutex >, std::ref( b), std::ref( mtx) ); + boost::thread t2( fn2< boost::fibers::recursive_timed_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); + } +} + void test_dummy() { } @@ -133,7 +130,7 @@ boost::unit_test::test_suite * init_unit_test_suite( int, char* []) { 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) ); + test->add( BOOST_TEST_CASE( & test_recursive_timed_mutex) ); #else test->add( BOOST_TEST_CASE( & test_dummy) ); #endif