1 2 // Copyright Oliver Kowalke 2013. 3 // Distributed under the Boost Software License, Version 1.0. 4 // (See accompanying file LICENSE_1_0.txt or copy at 5 // http://www.boost.org/LICENSE_1_0.txt) 6 7 #include "boost/fiber/recursive_timed_mutex.hpp" 8 9 #include <algorithm> 10 #include <functional> 11 12 #include "boost/fiber/exceptions.hpp" 13 #include "boost/fiber/scheduler.hpp" 14 15 #ifdef BOOST_HAS_ABI_HEADERS 16 # include BOOST_ABI_PREFIX 17 #endif 18 19 namespace boost { 20 namespace fibers { 21 22 bool try_lock_until_(std::chrono::steady_clock::time_point const & timeout_time)23recursive_timed_mutex::try_lock_until_( std::chrono::steady_clock::time_point const& timeout_time) noexcept { 24 while ( true) { 25 if ( std::chrono::steady_clock::now() > timeout_time) { 26 return false; 27 } 28 context * active_ctx = context::active(); 29 // store this fiber in order to be notified later 30 detail::spinlock_lock lk{ wait_queue_splk_ }; 31 if ( active_ctx == owner_) { 32 ++count_; 33 return true; 34 } 35 if ( nullptr == owner_) { 36 owner_ = active_ctx; 37 count_ = 1; 38 return true; 39 } 40 BOOST_ASSERT( ! active_ctx->wait_is_linked() ); 41 active_ctx->wait_link( wait_queue_); 42 active_ctx->twstatus.store( reinterpret_cast< std::intptr_t >( this), std::memory_order_release); 43 // suspend this fiber until notified or timed-out 44 if ( ! active_ctx->wait_until( timeout_time, lk) ) { 45 // remove fiber from wait-queue 46 lk.lock(); 47 wait_queue_.remove( * active_ctx); 48 return false; 49 } 50 BOOST_ASSERT( ! active_ctx->wait_is_linked() ); 51 } 52 } 53 54 void lock()55recursive_timed_mutex::lock() { 56 while ( true) { 57 context * active_ctx = context::active(); 58 // store this fiber in order to be notified later 59 detail::spinlock_lock lk{ wait_queue_splk_ }; 60 if ( active_ctx == owner_) { 61 ++count_; 62 return; 63 } 64 if ( nullptr == owner_) { 65 owner_ = active_ctx; 66 count_ = 1; 67 return; 68 } 69 BOOST_ASSERT( ! active_ctx->wait_is_linked() ); 70 active_ctx->twstatus.store( static_cast< std::intptr_t >( 0), std::memory_order_release); 71 active_ctx->wait_link( wait_queue_); 72 // suspend this fiber 73 active_ctx->suspend( lk); 74 BOOST_ASSERT( ! active_ctx->wait_is_linked() ); 75 } 76 } 77 78 bool try_lock()79recursive_timed_mutex::try_lock() noexcept { 80 context * active_ctx = context::active(); 81 detail::spinlock_lock lk{ wait_queue_splk_ }; 82 if ( nullptr == owner_) { 83 owner_ = active_ctx; 84 count_ = 1; 85 } else if ( active_ctx == owner_) { 86 ++count_; 87 } 88 lk.unlock(); 89 // let other fiber release the lock 90 active_ctx->yield(); 91 return active_ctx == owner_; 92 } 93 94 void unlock()95recursive_timed_mutex::unlock() { 96 context * active_ctx = context::active(); 97 detail::spinlock_lock lk{ wait_queue_splk_ }; 98 if ( BOOST_UNLIKELY( active_ctx != owner_) ) { 99 throw lock_error{ 100 std::make_error_code( std::errc::operation_not_permitted), 101 "boost fiber: no privilege to perform the operation" }; 102 } 103 if ( 0 == --count_) { 104 owner_ = nullptr; 105 if ( ! wait_queue_.empty() ) { 106 context * ctx = & wait_queue_.front(); 107 wait_queue_.pop_front(); 108 auto expected = reinterpret_cast< std::intptr_t >( this); 109 if ( ctx->twstatus.compare_exchange_strong( expected, static_cast< std::intptr_t >( -1), std::memory_order_acq_rel) ) { 110 // notify context 111 active_ctx->schedule( ctx); 112 } else if ( static_cast< std::intptr_t >( 0) == expected) { 113 // no timed-wait op. 114 // notify context 115 active_ctx->schedule( ctx); 116 } 117 } 118 } 119 } 120 121 }} 122 123 #ifdef BOOST_HAS_ABI_HEADERS 124 # include BOOST_ABI_SUFFIX 125 #endif 126