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