]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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 | |
23 | recursive_timed_mutex::try_lock_until_( std::chrono::steady_clock::time_point const& timeout_time) noexcept { | |
b32b8144 FG |
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 | } else if ( nullptr == owner_) { | |
35 | owner_ = active_ctx; | |
36 | count_ = 1; | |
37 | return true; | |
38 | } | |
39 | BOOST_ASSERT( ! active_ctx->wait_is_linked() ); | |
40 | active_ctx->wait_link( wait_queue_); | |
41 | intrusive_ptr_add_ref( active_ctx); | |
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() ); | |
7c673cae | 51 | } |
7c673cae FG |
52 | } |
53 | ||
54 | void | |
55 | recursive_timed_mutex::lock() { | |
b32b8144 FG |
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 | } else if ( nullptr == owner_) { | |
64 | owner_ = active_ctx; | |
65 | count_ = 1; | |
66 | return; | |
67 | } | |
68 | BOOST_ASSERT( ! active_ctx->wait_is_linked() ); | |
69 | active_ctx->twstatus.store( static_cast< std::intptr_t >( 0), std::memory_order_release); | |
70 | active_ctx->wait_link( wait_queue_); | |
71 | // suspend this fiber | |
72 | active_ctx->suspend( lk); | |
73 | BOOST_ASSERT( ! active_ctx->wait_is_linked() ); | |
7c673cae | 74 | } |
7c673cae FG |
75 | } |
76 | ||
77 | bool | |
78 | recursive_timed_mutex::try_lock() noexcept { | |
b32b8144 FG |
79 | context * active_ctx = context::active(); |
80 | detail::spinlock_lock lk{ wait_queue_splk_ }; | |
7c673cae | 81 | if ( nullptr == owner_) { |
b32b8144 | 82 | owner_ = active_ctx; |
7c673cae | 83 | count_ = 1; |
b32b8144 | 84 | } else if ( active_ctx == owner_) { |
7c673cae FG |
85 | ++count_; |
86 | } | |
87 | lk.unlock(); | |
88 | // let other fiber release the lock | |
b32b8144 FG |
89 | active_ctx->yield(); |
90 | return active_ctx == owner_; | |
7c673cae FG |
91 | } |
92 | ||
93 | void | |
94 | recursive_timed_mutex::unlock() { | |
b32b8144 FG |
95 | context * active_ctx = context::active(); |
96 | detail::spinlock_lock lk{ wait_queue_splk_ }; | |
97 | if ( BOOST_UNLIKELY( active_ctx != owner_) ) { | |
98 | throw lock_error{ | |
7c673cae | 99 | std::make_error_code( std::errc::operation_not_permitted), |
b32b8144 | 100 | "boost fiber: no privilege to perform the operation" }; |
7c673cae FG |
101 | } |
102 | if ( 0 == --count_) { | |
b32b8144 | 103 | owner_ = nullptr; |
7c673cae FG |
104 | if ( ! wait_queue_.empty() ) { |
105 | context * ctx = & wait_queue_.front(); | |
106 | wait_queue_.pop_front(); | |
b32b8144 FG |
107 | std::intptr_t expected = reinterpret_cast< std::intptr_t >( this); |
108 | if ( ctx->twstatus.compare_exchange_strong( expected, static_cast< std::intptr_t >( -1), std::memory_order_acq_rel) ) { | |
109 | // notify before timeout | |
110 | intrusive_ptr_release( ctx); | |
111 | // notify context | |
112 | active_ctx->schedule( ctx); | |
113 | } else if ( static_cast< std::intptr_t >( 0) == expected) { | |
114 | // no timed-wait op. | |
115 | // notify context | |
116 | active_ctx->schedule( ctx); | |
117 | } else { | |
118 | // timed-wait op. | |
119 | // expected == -1: notify after timeout, same timed-wait op. | |
120 | // expected == <any>: notify after timeout, another timed-wait op. was already started | |
121 | intrusive_ptr_release( ctx); | |
122 | // re-schedule next | |
123 | } | |
7c673cae FG |
124 | } |
125 | } | |
126 | } | |
127 | ||
128 | }} | |
129 | ||
130 | #ifdef BOOST_HAS_ABI_HEADERS | |
131 | # include BOOST_ABI_SUFFIX | |
132 | #endif |