]>
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/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 | 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 ( nullptr == owner_) { | |
32 | owner_ = active_ctx; | |
33 | return true; | |
34 | } | |
35 | BOOST_ASSERT( ! active_ctx->wait_is_linked() ); | |
36 | active_ctx->wait_link( wait_queue_); | |
37 | intrusive_ptr_add_ref( active_ctx); | |
38 | active_ctx->twstatus.store( reinterpret_cast< std::intptr_t >( this), std::memory_order_release); | |
39 | // suspend this fiber until notified or timed-out | |
40 | if ( ! active_ctx->wait_until( timeout_time, lk) ) { | |
41 | // remove fiber from wait-queue | |
42 | lk.lock(); | |
43 | wait_queue_.remove( * active_ctx); | |
44 | return false; | |
45 | } | |
46 | BOOST_ASSERT( ! active_ctx->wait_is_linked() ); | |
7c673cae | 47 | } |
7c673cae FG |
48 | } |
49 | ||
50 | void | |
51 | timed_mutex::lock() { | |
b32b8144 FG |
52 | while ( true) { |
53 | context * active_ctx = context::active(); | |
54 | // store this fiber in order to be notified later | |
55 | detail::spinlock_lock lk{ wait_queue_splk_ }; | |
56 | if ( BOOST_UNLIKELY( active_ctx == owner_) ) { | |
57 | throw lock_error{ | |
58 | std::make_error_code( std::errc::resource_deadlock_would_occur), | |
59 | "boost fiber: a deadlock is detected" }; | |
60 | } else if ( nullptr == owner_) { | |
61 | owner_ = active_ctx; | |
62 | return; | |
63 | } | |
64 | BOOST_ASSERT( ! active_ctx->wait_is_linked() ); | |
65 | active_ctx->wait_link( wait_queue_); | |
66 | active_ctx->twstatus.store( static_cast< std::intptr_t >( 0), std::memory_order_release); | |
67 | // suspend this fiber | |
68 | active_ctx->suspend( lk); | |
69 | BOOST_ASSERT( ! active_ctx->wait_is_linked() ); | |
7c673cae | 70 | } |
7c673cae FG |
71 | } |
72 | ||
73 | bool | |
74 | timed_mutex::try_lock() { | |
b32b8144 FG |
75 | context * active_ctx = context::active(); |
76 | detail::spinlock_lock lk{ wait_queue_splk_ }; | |
77 | if ( BOOST_UNLIKELY( active_ctx == owner_) ) { | |
78 | throw lock_error{ | |
7c673cae | 79 | std::make_error_code( std::errc::resource_deadlock_would_occur), |
b32b8144 | 80 | "boost fiber: a deadlock is detected" }; |
7c673cae | 81 | } else if ( nullptr == owner_) { |
b32b8144 | 82 | owner_ = active_ctx; |
7c673cae FG |
83 | } |
84 | lk.unlock(); | |
85 | // let other fiber release the lock | |
b32b8144 FG |
86 | active_ctx->yield(); |
87 | return active_ctx == owner_; | |
7c673cae FG |
88 | } |
89 | ||
90 | void | |
91 | timed_mutex::unlock() { | |
b32b8144 FG |
92 | context * active_ctx = context::active(); |
93 | detail::spinlock_lock lk{ wait_queue_splk_ }; | |
94 | if ( BOOST_UNLIKELY( active_ctx != owner_) ) { | |
95 | throw lock_error{ | |
7c673cae | 96 | std::make_error_code( std::errc::operation_not_permitted), |
b32b8144 | 97 | "boost fiber: no privilege to perform the operation" }; |
7c673cae | 98 | } |
b32b8144 | 99 | owner_ = nullptr; |
7c673cae FG |
100 | if ( ! wait_queue_.empty() ) { |
101 | context * ctx = & wait_queue_.front(); | |
102 | wait_queue_.pop_front(); | |
b32b8144 FG |
103 | std::intptr_t expected = reinterpret_cast< std::intptr_t >( this); |
104 | if ( ctx->twstatus.compare_exchange_strong( expected, static_cast< std::intptr_t >( -1), std::memory_order_acq_rel) ) { | |
105 | // notify before timeout | |
106 | intrusive_ptr_release( ctx); | |
107 | // notify context | |
108 | active_ctx->schedule( ctx); | |
109 | } else if ( static_cast< std::intptr_t >( 0) == expected) { | |
110 | // no timed-wait op. | |
111 | // notify context | |
112 | active_ctx->schedule( ctx); | |
113 | } else { | |
114 | // timed-wait op. | |
115 | // expected == -1: notify after timeout, same timed-wait op. | |
116 | // expected == <any>: notify after timeout, another timed-wait op. was already started | |
117 | intrusive_ptr_release( ctx); | |
118 | // re-schedule next | |
119 | } | |
7c673cae FG |
120 | } |
121 | } | |
122 | ||
123 | }} | |
124 | ||
125 | #ifdef BOOST_HAS_ABI_HEADERS | |
126 | # include BOOST_ABI_SUFFIX | |
127 | #endif |