1  
//
1  
//
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3  
//
3  
//
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  
//
6  
//
7  
// Official repository: https://github.com/cppalliance/capy
7  
// Official repository: https://github.com/cppalliance/capy
8  
//
8  
//
9  

9  

10  
#ifndef BOOST_CAPY_ASYNC_MUTEX_HPP
10  
#ifndef BOOST_CAPY_ASYNC_MUTEX_HPP
11  
#define BOOST_CAPY_ASYNC_MUTEX_HPP
11  
#define BOOST_CAPY_ASYNC_MUTEX_HPP
12  

12  

13  
#include <boost/capy/detail/config.hpp>
13  
#include <boost/capy/detail/config.hpp>
14  
#include <boost/capy/detail/intrusive.hpp>
14  
#include <boost/capy/detail/intrusive.hpp>
15  
#include <boost/capy/concept/executor.hpp>
15  
#include <boost/capy/concept/executor.hpp>
16  
#include <boost/capy/error.hpp>
16  
#include <boost/capy/error.hpp>
17  
#include <boost/capy/ex/io_env.hpp>
17  
#include <boost/capy/ex/io_env.hpp>
18  
#include <boost/capy/io_result.hpp>
18  
#include <boost/capy/io_result.hpp>
19  

19  

20  
#include <stop_token>
20  
#include <stop_token>
21  

21  

22  
#include <atomic>
22  
#include <atomic>
23  
#include <coroutine>
23  
#include <coroutine>
24  
#include <new>
24  
#include <new>
25  
#include <utility>
25  
#include <utility>
26  

26  

27  
/*  async_mutex implementation notes
27  
/*  async_mutex implementation notes
28  
    ================================
28  
    ================================
29  

29  

30  
    Waiters form a doubly-linked intrusive list (fair FIFO). lock_awaiter
30  
    Waiters form a doubly-linked intrusive list (fair FIFO). lock_awaiter
31  
    inherits intrusive_list<lock_awaiter>::node; the list is owned by
31  
    inherits intrusive_list<lock_awaiter>::node; the list is owned by
32  
    async_mutex::waiters_.
32  
    async_mutex::waiters_.
33  

33  

34  
    Cancellation via stop_token
34  
    Cancellation via stop_token
35  
    ---------------------------
35  
    ---------------------------
36  
    A std::stop_callback is registered in await_suspend. Two actors can
36  
    A std::stop_callback is registered in await_suspend. Two actors can
37  
    race to resume the suspended coroutine: unlock() and the stop callback.
37  
    race to resume the suspended coroutine: unlock() and the stop callback.
38  
    An atomic bool `claimed_` resolves the race -- whoever does
38  
    An atomic bool `claimed_` resolves the race -- whoever does
39  
    claimed_.exchange(true) and reads false wins. The loser does nothing.
39  
    claimed_.exchange(true) and reads false wins. The loser does nothing.
40  

40  

41  
    The stop callback calls ex_.post(h_). The stop_callback is
41  
    The stop callback calls ex_.post(h_). The stop_callback is
42  
    destroyed later in await_resume. cancel_fn touches no members
42  
    destroyed later in await_resume. cancel_fn touches no members
43  
    after post returns (same pattern as delete-this).
43  
    after post returns (same pattern as delete-this).
44  

44  

45  
    unlock() pops waiters from the front. If the popped waiter was
45  
    unlock() pops waiters from the front. If the popped waiter was
46  
    already claimed by the stop callback, unlock() skips it and tries
46  
    already claimed by the stop callback, unlock() skips it and tries
47  
    the next. await_resume removes the (still-linked) canceled waiter
47  
    the next. await_resume removes the (still-linked) canceled waiter
48  
    via waiters_.remove(this).
48  
    via waiters_.remove(this).
49  

49  

50  
    The stop_callback lives in a union to suppress automatic
50  
    The stop_callback lives in a union to suppress automatic
51  
    construction/destruction. Placement new in await_suspend, explicit
51  
    construction/destruction. Placement new in await_suspend, explicit
52  
    destructor call in await_resume and ~lock_awaiter.
52  
    destructor call in await_resume and ~lock_awaiter.
53  

53  

54  
    Member ordering constraint
54  
    Member ordering constraint
55  
    --------------------------
55  
    --------------------------
56  
    The union containing stop_cb_ must be declared AFTER the members
56  
    The union containing stop_cb_ must be declared AFTER the members
57  
    the callback accesses (h_, ex_, claimed_, canceled_). If the
57  
    the callback accesses (h_, ex_, claimed_, canceled_). If the
58  
    stop_cb_ destructor blocks waiting for a concurrent callback, those
58  
    stop_cb_ destructor blocks waiting for a concurrent callback, those
59  
    members must still be alive (C++ destroys in reverse declaration
59  
    members must still be alive (C++ destroys in reverse declaration
60  
    order).
60  
    order).
61  

61  

62  
    active_ flag
62  
    active_ flag
63  
    ------------
63  
    ------------
64  
    Tracks both list membership and stop_cb_ lifetime (they are always
64  
    Tracks both list membership and stop_cb_ lifetime (they are always
65  
    set and cleared together). Used by the destructor to clean up if the
65  
    set and cleared together). Used by the destructor to clean up if the
66  
    coroutine is destroyed while suspended (e.g. execution_context
66  
    coroutine is destroyed while suspended (e.g. execution_context
67  
    shutdown).
67  
    shutdown).
68  

68  

69  
    Cancellation scope
69  
    Cancellation scope
70  
    ------------------
70  
    ------------------
71  
    Cancellation only takes effect while the coroutine is suspended in
71  
    Cancellation only takes effect while the coroutine is suspended in
72  
    the wait queue. If the mutex is unlocked, await_ready acquires it
72  
    the wait queue. If the mutex is unlocked, await_ready acquires it
73  
    immediately without checking the stop token. This is intentional:
73  
    immediately without checking the stop token. This is intentional:
74  
    the fast path has no token access and no overhead.
74  
    the fast path has no token access and no overhead.
75  

75  

76  
    Threading assumptions
76  
    Threading assumptions
77  
    ---------------------
77  
    ---------------------
78  
    - All list mutations happen on the executor thread (await_suspend,
78  
    - All list mutations happen on the executor thread (await_suspend,
79  
      await_resume, unlock, ~lock_awaiter).
79  
      await_resume, unlock, ~lock_awaiter).
80  
    - The stop callback may fire from any thread, but only touches
80  
    - The stop callback may fire from any thread, but only touches
81  
      claimed_ (atomic) and then calls post. It never touches the
81  
      claimed_ (atomic) and then calls post. It never touches the
82  
      list.
82  
      list.
83  
    - ~lock_awaiter must be called from the executor thread. This is
83  
    - ~lock_awaiter must be called from the executor thread. This is
84  
      guaranteed during normal shutdown but NOT if the coroutine frame
84  
      guaranteed during normal shutdown but NOT if the coroutine frame
85  
      is destroyed from another thread while a stop callback could
85  
      is destroyed from another thread while a stop callback could
86  
      fire (precondition violation, same as cppcoro/folly).
86  
      fire (precondition violation, same as cppcoro/folly).
87  
*/
87  
*/
88  

88  

89  
namespace boost {
89  
namespace boost {
90  
namespace capy {
90  
namespace capy {
91  

91  

92  
/** An asynchronous mutex for coroutines.
92  
/** An asynchronous mutex for coroutines.
93  

93  

94  
    This mutex provides mutual exclusion for coroutines without blocking.
94  
    This mutex provides mutual exclusion for coroutines without blocking.
95  
    When a coroutine attempts to acquire a locked mutex, it suspends and
95  
    When a coroutine attempts to acquire a locked mutex, it suspends and
96  
    is added to an intrusive wait queue. When the holder unlocks, the next
96  
    is added to an intrusive wait queue. When the holder unlocks, the next
97  
    waiter is resumed with the lock held.
97  
    waiter is resumed with the lock held.
98  

98  

99  
    @par Cancellation
99  
    @par Cancellation
100  

100  

101  
    When a coroutine is suspended waiting for the mutex and its stop
101  
    When a coroutine is suspended waiting for the mutex and its stop
102  
    token is triggered, the waiter completes with `error::canceled`
102  
    token is triggered, the waiter completes with `error::canceled`
103  
    instead of acquiring the lock.
103  
    instead of acquiring the lock.
104  

104  

105  
    Cancellation only applies while the coroutine is suspended in the
105  
    Cancellation only applies while the coroutine is suspended in the
106  
    wait queue. If the mutex is unlocked when `lock()` is called, the
106  
    wait queue. If the mutex is unlocked when `lock()` is called, the
107  
    lock is acquired immediately even if the stop token is already
107  
    lock is acquired immediately even if the stop token is already
108  
    signaled.
108  
    signaled.
109  

109  

110  
    @par Zero Allocation
110  
    @par Zero Allocation
111  

111  

112  
    No heap allocation occurs for lock operations.
112  
    No heap allocation occurs for lock operations.
113  

113  

114  
    @par Thread Safety
114  
    @par Thread Safety
115  

115  

116  
    The mutex operations are designed for single-threaded use on one
116  
    The mutex operations are designed for single-threaded use on one
117  
    executor. The stop callback may fire from any thread.
117  
    executor. The stop callback may fire from any thread.
118  

118  

119  
    @par Example
119  
    @par Example
120  
    @code
120  
    @code
121  
    async_mutex cm;
121  
    async_mutex cm;
122  

122  

123  
    task<> protected_operation() {
123  
    task<> protected_operation() {
124  
        auto [ec] = co_await cm.lock();
124  
        auto [ec] = co_await cm.lock();
125  
        if(ec)
125  
        if(ec)
126  
            co_return;
126  
            co_return;
127  
        // ... critical section ...
127  
        // ... critical section ...
128  
        cm.unlock();
128  
        cm.unlock();
129  
    }
129  
    }
130  

130  

131  
    // Or with RAII:
131  
    // Or with RAII:
132  
    task<> protected_operation() {
132  
    task<> protected_operation() {
133  
        auto [ec, guard] = co_await cm.scoped_lock();
133  
        auto [ec, guard] = co_await cm.scoped_lock();
134  
        if(ec)
134  
        if(ec)
135  
            co_return;
135  
            co_return;
136  
        // ... critical section ...
136  
        // ... critical section ...
137  
        // unlocks automatically
137  
        // unlocks automatically
138  
    }
138  
    }
139  
    @endcode
139  
    @endcode
140  
*/
140  
*/
141  
class async_mutex
141  
class async_mutex
142  
{
142  
{
143  
public:
143  
public:
144  
    class lock_awaiter;
144  
    class lock_awaiter;
145  
    class lock_guard;
145  
    class lock_guard;
146  
    class lock_guard_awaiter;
146  
    class lock_guard_awaiter;
147  

147  

148  
private:
148  
private:
149  
    bool locked_ = false;
149  
    bool locked_ = false;
150  
    detail::intrusive_list<lock_awaiter> waiters_;
150  
    detail::intrusive_list<lock_awaiter> waiters_;
151  

151  

152  
public:
152  
public:
153  
    /** Awaiter returned by lock().
153  
    /** Awaiter returned by lock().
154  
    */
154  
    */
155  
    class lock_awaiter
155  
    class lock_awaiter
156  
        : public detail::intrusive_list<lock_awaiter>::node
156  
        : public detail::intrusive_list<lock_awaiter>::node
157  
    {
157  
    {
158  
        friend class async_mutex;
158  
        friend class async_mutex;
159  

159  

160  
        async_mutex* m_;
160  
        async_mutex* m_;
161  
        std::coroutine_handle<> h_;
161  
        std::coroutine_handle<> h_;
162  
        executor_ref ex_;
162  
        executor_ref ex_;
163  

163  

164  
        // These members must be declared before stop_cb_
164  
        // These members must be declared before stop_cb_
165  
        // (see comment on the union below).
165  
        // (see comment on the union below).
166  
        std::atomic<bool> claimed_{false};
166  
        std::atomic<bool> claimed_{false};
167  
        bool canceled_ = false;
167  
        bool canceled_ = false;
168  
        bool active_ = false;
168  
        bool active_ = false;
169  

169  

170  
        struct cancel_fn
170  
        struct cancel_fn
171  
        {
171  
        {
172  
            lock_awaiter* self_;
172  
            lock_awaiter* self_;
173  

173  

174  
            void operator()() const noexcept
174  
            void operator()() const noexcept
175  
            {
175  
            {
176  
                if(!self_->claimed_.exchange(
176  
                if(!self_->claimed_.exchange(
177  
                    true, std::memory_order_acq_rel))
177  
                    true, std::memory_order_acq_rel))
178  
                {
178  
                {
179  
                    self_->canceled_ = true;
179  
                    self_->canceled_ = true;
180  
                    self_->ex_.post(self_->h_);
180  
                    self_->ex_.post(self_->h_);
181  
                }
181  
                }
182  
            }
182  
            }
183  
        };
183  
        };
184  

184  

185  
        using stop_cb_t =
185  
        using stop_cb_t =
186  
            std::stop_callback<cancel_fn>;
186  
            std::stop_callback<cancel_fn>;
187  

187  

188  
        // Aligned storage for stop_cb_t. Declared last:
188  
        // Aligned storage for stop_cb_t. Declared last:
189  
        // its destructor may block while the callback
189  
        // its destructor may block while the callback
190  
        // accesses the members above.
190  
        // accesses the members above.
191  
#ifdef _MSC_VER
191  
#ifdef _MSC_VER
192  
# pragma warning(push)
192  
# pragma warning(push)
193  
# pragma warning(disable: 4324) // padded due to alignas
193  
# pragma warning(disable: 4324) // padded due to alignas
194  
#endif
194  
#endif
195  
        alignas(stop_cb_t)
195  
        alignas(stop_cb_t)
196  
            unsigned char stop_cb_buf_[sizeof(stop_cb_t)];
196  
            unsigned char stop_cb_buf_[sizeof(stop_cb_t)];
197  
#ifdef _MSC_VER
197  
#ifdef _MSC_VER
198  
# pragma warning(pop)
198  
# pragma warning(pop)
199  
#endif
199  
#endif
200  

200  

201  
        stop_cb_t& stop_cb_() noexcept
201  
        stop_cb_t& stop_cb_() noexcept
202  
        {
202  
        {
203  
            return *reinterpret_cast<stop_cb_t*>(
203  
            return *reinterpret_cast<stop_cb_t*>(
204  
                stop_cb_buf_);
204  
                stop_cb_buf_);
205  
        }
205  
        }
206  

206  

207  
    public:
207  
    public:
208  
        ~lock_awaiter()
208  
        ~lock_awaiter()
209  
        {
209  
        {
210  
            if(active_)
210  
            if(active_)
211  
            {
211  
            {
212  
                stop_cb_().~stop_cb_t();
212  
                stop_cb_().~stop_cb_t();
213  
                m_->waiters_.remove(this);
213  
                m_->waiters_.remove(this);
214  
            }
214  
            }
215  
        }
215  
        }
216  

216  

217  
        explicit lock_awaiter(async_mutex* m) noexcept
217  
        explicit lock_awaiter(async_mutex* m) noexcept
218  
            : m_(m)
218  
            : m_(m)
219  
        {
219  
        {
220  
        }
220  
        }
221  

221  

222  
        lock_awaiter(lock_awaiter&& o) noexcept
222  
        lock_awaiter(lock_awaiter&& o) noexcept
223  
            : m_(o.m_)
223  
            : m_(o.m_)
224  
            , h_(o.h_)
224  
            , h_(o.h_)
225  
            , ex_(o.ex_)
225  
            , ex_(o.ex_)
226  
            , claimed_(o.claimed_.load(
226  
            , claimed_(o.claimed_.load(
227  
                std::memory_order_relaxed))
227  
                std::memory_order_relaxed))
228  
            , canceled_(o.canceled_)
228  
            , canceled_(o.canceled_)
229  
            , active_(std::exchange(o.active_, false))
229  
            , active_(std::exchange(o.active_, false))
230  
        {
230  
        {
231  
        }
231  
        }
232  

232  

233  
        lock_awaiter(lock_awaiter const&) = delete;
233  
        lock_awaiter(lock_awaiter const&) = delete;
234  
        lock_awaiter& operator=(lock_awaiter const&) = delete;
234  
        lock_awaiter& operator=(lock_awaiter const&) = delete;
235  
        lock_awaiter& operator=(lock_awaiter&&) = delete;
235  
        lock_awaiter& operator=(lock_awaiter&&) = delete;
236  

236  

237  
        bool await_ready() const noexcept
237  
        bool await_ready() const noexcept
238  
        {
238  
        {
239  
            if(!m_->locked_)
239  
            if(!m_->locked_)
240  
            {
240  
            {
241  
                m_->locked_ = true;
241  
                m_->locked_ = true;
242  
                return true;
242  
                return true;
243  
            }
243  
            }
244  
            return false;
244  
            return false;
245  
        }
245  
        }
246  

246  

247  
        /** IoAwaitable protocol overload. */
247  
        /** IoAwaitable protocol overload. */
248  
        std::coroutine_handle<>
248  
        std::coroutine_handle<>
249  
        await_suspend(
249  
        await_suspend(
250  
            std::coroutine_handle<> h,
250  
            std::coroutine_handle<> h,
251  
            io_env const* env) noexcept
251  
            io_env const* env) noexcept
252  
        {
252  
        {
253  
            if(env->stop_token.stop_requested())
253  
            if(env->stop_token.stop_requested())
254  
            {
254  
            {
255  
                canceled_ = true;
255  
                canceled_ = true;
256  
                return h;
256  
                return h;
257  
            }
257  
            }
258  
            h_ = h;
258  
            h_ = h;
259  
            ex_ = env->executor;
259  
            ex_ = env->executor;
260  
            m_->waiters_.push_back(this);
260  
            m_->waiters_.push_back(this);
261  
            ::new(stop_cb_buf_) stop_cb_t(
261  
            ::new(stop_cb_buf_) stop_cb_t(
262  
                env->stop_token, cancel_fn{this});
262  
                env->stop_token, cancel_fn{this});
263  
            active_ = true;
263  
            active_ = true;
264  
            return std::noop_coroutine();
264  
            return std::noop_coroutine();
265  
        }
265  
        }
266  

266  

267  
        io_result<> await_resume() noexcept
267  
        io_result<> await_resume() noexcept
268  
        {
268  
        {
269  
            if(active_)
269  
            if(active_)
270  
            {
270  
            {
271  
                stop_cb_().~stop_cb_t();
271  
                stop_cb_().~stop_cb_t();
272  
                if(canceled_)
272  
                if(canceled_)
273  
                {
273  
                {
274  
                    m_->waiters_.remove(this);
274  
                    m_->waiters_.remove(this);
275  
                    active_ = false;
275  
                    active_ = false;
276  
                    return {make_error_code(
276  
                    return {make_error_code(
277  
                        error::canceled)};
277  
                        error::canceled)};
278  
                }
278  
                }
279  
                active_ = false;
279  
                active_ = false;
280  
            }
280  
            }
281  
            if(canceled_)
281  
            if(canceled_)
282  
                return {make_error_code(
282  
                return {make_error_code(
283  
                    error::canceled)};
283  
                    error::canceled)};
284  
            return {{}};
284  
            return {{}};
285  
        }
285  
        }
286  
    };
286  
    };
287  

287  

288  
    /** RAII lock guard for async_mutex.
288  
    /** RAII lock guard for async_mutex.
289  

289  

290  
        Automatically unlocks the mutex when destroyed.
290  
        Automatically unlocks the mutex when destroyed.
291  
    */
291  
    */
292  
    class [[nodiscard]] lock_guard
292  
    class [[nodiscard]] lock_guard
293  
    {
293  
    {
294  
        async_mutex* m_;
294  
        async_mutex* m_;
295  

295  

296  
    public:
296  
    public:
297  
        ~lock_guard()
297  
        ~lock_guard()
298  
        {
298  
        {
299  
            if(m_)
299  
            if(m_)
300  
                m_->unlock();
300  
                m_->unlock();
301  
        }
301  
        }
302  

302  

303  
        lock_guard() noexcept
303  
        lock_guard() noexcept
304  
            : m_(nullptr)
304  
            : m_(nullptr)
305  
        {
305  
        {
306  
        }
306  
        }
307  

307  

308  
        explicit lock_guard(async_mutex* m) noexcept
308  
        explicit lock_guard(async_mutex* m) noexcept
309  
            : m_(m)
309  
            : m_(m)
310  
        {
310  
        {
311  
        }
311  
        }
312  

312  

313  
        lock_guard(lock_guard&& o) noexcept
313  
        lock_guard(lock_guard&& o) noexcept
314  
            : m_(std::exchange(o.m_, nullptr))
314  
            : m_(std::exchange(o.m_, nullptr))
315  
        {
315  
        {
316  
        }
316  
        }
317  

317  

318  
        lock_guard& operator=(lock_guard&& o) noexcept
318  
        lock_guard& operator=(lock_guard&& o) noexcept
319  
        {
319  
        {
320  
            if(this != &o)
320  
            if(this != &o)
321  
            {
321  
            {
322  
                if(m_)
322  
                if(m_)
323  
                    m_->unlock();
323  
                    m_->unlock();
324  
                m_ = std::exchange(o.m_, nullptr);
324  
                m_ = std::exchange(o.m_, nullptr);
325  
            }
325  
            }
326  
            return *this;
326  
            return *this;
327  
        }
327  
        }
328  

328  

329  
        lock_guard(lock_guard const&) = delete;
329  
        lock_guard(lock_guard const&) = delete;
330  
        lock_guard& operator=(lock_guard const&) = delete;
330  
        lock_guard& operator=(lock_guard const&) = delete;
331  
    };
331  
    };
332  

332  

333  
    /** Awaiter returned by scoped_lock() that returns a lock_guard on resume.
333  
    /** Awaiter returned by scoped_lock() that returns a lock_guard on resume.
334  
    */
334  
    */
335  
    class lock_guard_awaiter
335  
    class lock_guard_awaiter
336  
    {
336  
    {
337  
        async_mutex* m_;
337  
        async_mutex* m_;
338  
        lock_awaiter inner_;
338  
        lock_awaiter inner_;
339  

339  

340  
    public:
340  
    public:
341  
        explicit lock_guard_awaiter(async_mutex* m) noexcept
341  
        explicit lock_guard_awaiter(async_mutex* m) noexcept
342  
            : m_(m)
342  
            : m_(m)
343  
            , inner_(m)
343  
            , inner_(m)
344  
        {
344  
        {
345  
        }
345  
        }
346  

346  

347  
        bool await_ready() const noexcept
347  
        bool await_ready() const noexcept
348  
        {
348  
        {
349  
            return inner_.await_ready();
349  
            return inner_.await_ready();
350  
        }
350  
        }
351  

351  

352  
        /** IoAwaitable protocol overload. */
352  
        /** IoAwaitable protocol overload. */
353  
        std::coroutine_handle<>
353  
        std::coroutine_handle<>
354  
        await_suspend(
354  
        await_suspend(
355  
            std::coroutine_handle<> h,
355  
            std::coroutine_handle<> h,
356  
            io_env const* env) noexcept
356  
            io_env const* env) noexcept
357  
        {
357  
        {
358  
            return inner_.await_suspend(h, env);
358  
            return inner_.await_suspend(h, env);
359  
        }
359  
        }
360  

360  

361  
        io_result<lock_guard> await_resume() noexcept
361  
        io_result<lock_guard> await_resume() noexcept
362  
        {
362  
        {
363  
            auto r = inner_.await_resume();
363  
            auto r = inner_.await_resume();
364  
            if(r.ec)
364  
            if(r.ec)
365  
                return {r.ec, {}};
365  
                return {r.ec, {}};
366  
            return {{}, lock_guard(m_)};
366  
            return {{}, lock_guard(m_)};
367  
        }
367  
        }
368  
    };
368  
    };
369  

369  

370  
    async_mutex() = default;
370  
    async_mutex() = default;
371  

371  

372  
    // Non-copyable, non-movable
372  
    // Non-copyable, non-movable
373  
    async_mutex(async_mutex const&) = delete;
373  
    async_mutex(async_mutex const&) = delete;
374  
    async_mutex& operator=(async_mutex const&) = delete;
374  
    async_mutex& operator=(async_mutex const&) = delete;
375  

375  

376  
    /** Returns an awaiter that acquires the mutex.
376  
    /** Returns an awaiter that acquires the mutex.
377  

377  

378  
        @return An awaitable yielding `(error_code)`.
378  
        @return An awaitable yielding `(error_code)`.
379  
    */
379  
    */
380  
    lock_awaiter lock() noexcept
380  
    lock_awaiter lock() noexcept
381  
    {
381  
    {
382  
        return lock_awaiter{this};
382  
        return lock_awaiter{this};
383  
    }
383  
    }
384  

384  

385  
    /** Returns an awaiter that acquires the mutex with RAII.
385  
    /** Returns an awaiter that acquires the mutex with RAII.
386  

386  

387  
        @return An awaitable yielding `(error_code,lock_guard)`.
387  
        @return An awaitable yielding `(error_code,lock_guard)`.
388  
    */
388  
    */
389  
    lock_guard_awaiter scoped_lock() noexcept
389  
    lock_guard_awaiter scoped_lock() noexcept
390  
    {
390  
    {
391  
        return lock_guard_awaiter(this);
391  
        return lock_guard_awaiter(this);
392  
    }
392  
    }
393  

393  

394  
    /** Releases the mutex.
394  
    /** Releases the mutex.
395  

395  

396  
        If waiters are queued, the next eligible waiter is
396  
        If waiters are queued, the next eligible waiter is
397  
        resumed with the lock held. Canceled waiters are
397  
        resumed with the lock held. Canceled waiters are
398  
        skipped. If no eligible waiter remains, the mutex
398  
        skipped. If no eligible waiter remains, the mutex
399  
        becomes unlocked.
399  
        becomes unlocked.
400  
    */
400  
    */
401  
    void unlock() noexcept
401  
    void unlock() noexcept
402  
    {
402  
    {
403  
        for(;;)
403  
        for(;;)
404  
        {
404  
        {
405  
            auto* waiter = waiters_.pop_front();
405  
            auto* waiter = waiters_.pop_front();
406  
            if(!waiter)
406  
            if(!waiter)
407  
            {
407  
            {
408  
                locked_ = false;
408  
                locked_ = false;
409  
                return;
409  
                return;
410  
            }
410  
            }
411  
            if(!waiter->claimed_.exchange(
411  
            if(!waiter->claimed_.exchange(
412  
                true, std::memory_order_acq_rel))
412  
                true, std::memory_order_acq_rel))
413  
            {
413  
            {
414  
                waiter->ex_.post(waiter->h_);
414  
                waiter->ex_.post(waiter->h_);
415  
                return;
415  
                return;
416  
            }
416  
            }
417  
        }
417  
        }
418  
    }
418  
    }
419  

419  

420  
    /** Returns true if the mutex is currently locked.
420  
    /** Returns true if the mutex is currently locked.
421  
    */
421  
    */
422  
    bool is_locked() const noexcept
422  
    bool is_locked() const noexcept
423  
    {
423  
    {
424  
        return locked_;
424  
        return locked_;
425  
    }
425  
    }
426  
};
426  
};
427  

427  

428  
} // namespace capy
428  
} // namespace capy
429  
} // namespace boost
429  
} // namespace boost
430  

430  

431  
#endif
431  
#endif