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_.dispatch(h_). If dispatch runs inline
41  
    The stop callback calls ex_.dispatch(h_). If dispatch runs inline
42  
    (same thread), the stop_callback is destroyed from within its own
42  
    (same thread), the stop_callback is destroyed from within its own
43  
    operator() via await_resume. This is safe: cancel_fn touches no
43  
    operator() via await_resume. This is safe: cancel_fn touches no
44  
    members after dispatch returns (same pattern as delete-this).
44  
    members after dispatch returns (same pattern as delete-this).
45  

45  

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

50  

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

54  

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

62  

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

69  

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

76  

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

89  

90  
namespace boost {
90  
namespace boost {
91  
namespace capy {
91  
namespace capy {
92  

92  

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

94  

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

99  

100  
    @par Cancellation
100  
    @par Cancellation
101  

101  

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

105  

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

110  

111  
    @par Zero Allocation
111  
    @par Zero Allocation
112  

112  

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

114  

115  
    @par Thread Safety
115  
    @par Thread Safety
116  

116  

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

119  

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

123  

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

131  

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

148  

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

152  

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

160  

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

164  

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

170  

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

174  

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

185  

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

188  

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

201  

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

207  

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

217  

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

222  

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

233  

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

237  

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

247  

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

267  

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

288  

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

290  

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

296  

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

303  

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

308  

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

313  

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

318  

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

329  

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

333  

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

340  

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

347  

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

352  

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

361  

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

370  

371  
    async_mutex() = default;
371  
    async_mutex() = default;
372  

372  

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

376  

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

378  

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

385  

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

387  

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

394  

395  
    /** Releases the mutex.
395  
    /** Releases the mutex.
396  

396  

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

420  

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

428  

429  
} // namespace capy
429  
} // namespace capy
430  
} // namespace boost
430  
} // namespace boost
431  

431  

432  
#endif
432  
#endif