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  
    Distinct objects: Safe.@n
116  
    Distinct objects: Safe.@n
117  
    Shared objects: Unsafe.
117  
    Shared objects: Unsafe.
118  

118  

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

121  

122  
    This type is non-copyable and non-movable because suspended
122  
    This type is non-copyable and non-movable because suspended
123  
    waiters hold intrusive pointers into the mutex's internal list.
123  
    waiters hold intrusive pointers into the mutex's internal list.
124  

124  

125  
    @par Example
125  
    @par Example
126  
    @code
126  
    @code
127  
    async_mutex cm;
127  
    async_mutex cm;
128  

128  

129  
    task<> protected_operation() {
129  
    task<> protected_operation() {
130  
        auto [ec] = co_await cm.lock();
130  
        auto [ec] = co_await cm.lock();
131  
        if(ec)
131  
        if(ec)
132  
            co_return;
132  
            co_return;
133  
        // ... critical section ...
133  
        // ... critical section ...
134  
        cm.unlock();
134  
        cm.unlock();
135  
    }
135  
    }
136  

136  

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

153  

154  
private:
154  
private:
155  
    bool locked_ = false;
155  
    bool locked_ = false;
156  
    detail::intrusive_list<lock_awaiter> waiters_;
156  
    detail::intrusive_list<lock_awaiter> waiters_;
157  

157  

158  
public:
158  
public:
159  
    /** Awaiter returned by lock().
159  
    /** Awaiter returned by lock().
160  
    */
160  
    */
161  
    class lock_awaiter
161  
    class lock_awaiter
162  
        : public detail::intrusive_list<lock_awaiter>::node
162  
        : public detail::intrusive_list<lock_awaiter>::node
163  
    {
163  
    {
164  
        friend class async_mutex;
164  
        friend class async_mutex;
165  

165  

166  
        async_mutex* m_;
166  
        async_mutex* m_;
167  
        std::coroutine_handle<> h_;
167  
        std::coroutine_handle<> h_;
168  
        executor_ref ex_;
168  
        executor_ref ex_;
169  

169  

170  
        // These members must be declared before stop_cb_
170  
        // These members must be declared before stop_cb_
171  
        // (see comment on the union below).
171  
        // (see comment on the union below).
172  
        std::atomic<bool> claimed_{false};
172  
        std::atomic<bool> claimed_{false};
173  
        bool canceled_ = false;
173  
        bool canceled_ = false;
174  
        bool active_ = false;
174  
        bool active_ = false;
175  

175  

176  
        struct cancel_fn
176  
        struct cancel_fn
177  
        {
177  
        {
178  
            lock_awaiter* self_;
178  
            lock_awaiter* self_;
179  

179  

180  
            void operator()() const noexcept
180  
            void operator()() const noexcept
181  
            {
181  
            {
182  
                if(!self_->claimed_.exchange(
182  
                if(!self_->claimed_.exchange(
183  
                    true, std::memory_order_acq_rel))
183  
                    true, std::memory_order_acq_rel))
184  
                {
184  
                {
185  
                    self_->canceled_ = true;
185  
                    self_->canceled_ = true;
186  
                    self_->ex_.post(self_->h_);
186  
                    self_->ex_.post(self_->h_);
187  
                }
187  
                }
188  
            }
188  
            }
189  
        };
189  
        };
190  

190  

191  
        using stop_cb_t =
191  
        using stop_cb_t =
192  
            std::stop_callback<cancel_fn>;
192  
            std::stop_callback<cancel_fn>;
193  

193  

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

206  

207  
        stop_cb_t& stop_cb_() noexcept
207  
        stop_cb_t& stop_cb_() noexcept
208  
        {
208  
        {
209  
            return *reinterpret_cast<stop_cb_t*>(
209  
            return *reinterpret_cast<stop_cb_t*>(
210  
                stop_cb_buf_);
210  
                stop_cb_buf_);
211  
        }
211  
        }
212  

212  

213  
    public:
213  
    public:
214  
        ~lock_awaiter()
214  
        ~lock_awaiter()
215  
        {
215  
        {
216  
            if(active_)
216  
            if(active_)
217  
            {
217  
            {
218  
                stop_cb_().~stop_cb_t();
218  
                stop_cb_().~stop_cb_t();
219  
                m_->waiters_.remove(this);
219  
                m_->waiters_.remove(this);
220  
            }
220  
            }
221  
        }
221  
        }
222  

222  

223  
        explicit lock_awaiter(async_mutex* m) noexcept
223  
        explicit lock_awaiter(async_mutex* m) noexcept
224  
            : m_(m)
224  
            : m_(m)
225  
        {
225  
        {
226  
        }
226  
        }
227  

227  

228  
        lock_awaiter(lock_awaiter&& o) noexcept
228  
        lock_awaiter(lock_awaiter&& o) noexcept
229  
            : m_(o.m_)
229  
            : m_(o.m_)
230  
            , h_(o.h_)
230  
            , h_(o.h_)
231  
            , ex_(o.ex_)
231  
            , ex_(o.ex_)
232  
            , claimed_(o.claimed_.load(
232  
            , claimed_(o.claimed_.load(
233  
                std::memory_order_relaxed))
233  
                std::memory_order_relaxed))
234  
            , canceled_(o.canceled_)
234  
            , canceled_(o.canceled_)
235  
            , active_(std::exchange(o.active_, false))
235  
            , active_(std::exchange(o.active_, false))
236  
        {
236  
        {
237  
        }
237  
        }
238  

238  

239  
        lock_awaiter(lock_awaiter const&) = delete;
239  
        lock_awaiter(lock_awaiter const&) = delete;
240  
        lock_awaiter& operator=(lock_awaiter const&) = delete;
240  
        lock_awaiter& operator=(lock_awaiter const&) = delete;
241  
        lock_awaiter& operator=(lock_awaiter&&) = delete;
241  
        lock_awaiter& operator=(lock_awaiter&&) = delete;
242  

242  

243  
        bool await_ready() const noexcept
243  
        bool await_ready() const noexcept
244  
        {
244  
        {
245  
            if(!m_->locked_)
245  
            if(!m_->locked_)
246  
            {
246  
            {
247  
                m_->locked_ = true;
247  
                m_->locked_ = true;
248  
                return true;
248  
                return true;
249  
            }
249  
            }
250  
            return false;
250  
            return false;
251  
        }
251  
        }
252  

252  

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

272  

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

293  

294  
    /** RAII lock guard for async_mutex.
294  
    /** RAII lock guard for async_mutex.
295  

295  

296  
        Automatically unlocks the mutex when destroyed.
296  
        Automatically unlocks the mutex when destroyed.
297  
    */
297  
    */
298  
    class [[nodiscard]] lock_guard
298  
    class [[nodiscard]] lock_guard
299  
    {
299  
    {
300  
        async_mutex* m_;
300  
        async_mutex* m_;
301  

301  

302  
    public:
302  
    public:
303  
        ~lock_guard()
303  
        ~lock_guard()
304  
        {
304  
        {
305  
            if(m_)
305  
            if(m_)
306  
                m_->unlock();
306  
                m_->unlock();
307  
        }
307  
        }
308  

308  

309  
        lock_guard() noexcept
309  
        lock_guard() noexcept
310  
            : m_(nullptr)
310  
            : m_(nullptr)
311  
        {
311  
        {
312  
        }
312  
        }
313  

313  

314  
        explicit lock_guard(async_mutex* m) noexcept
314  
        explicit lock_guard(async_mutex* m) noexcept
315  
            : m_(m)
315  
            : m_(m)
316  
        {
316  
        {
317  
        }
317  
        }
318  

318  

319  
        lock_guard(lock_guard&& o) noexcept
319  
        lock_guard(lock_guard&& o) noexcept
320  
            : m_(std::exchange(o.m_, nullptr))
320  
            : m_(std::exchange(o.m_, nullptr))
321  
        {
321  
        {
322  
        }
322  
        }
323  

323  

324  
        lock_guard& operator=(lock_guard&& o) noexcept
324  
        lock_guard& operator=(lock_guard&& o) noexcept
325  
        {
325  
        {
326  
            if(this != &o)
326  
            if(this != &o)
327  
            {
327  
            {
328  
                if(m_)
328  
                if(m_)
329  
                    m_->unlock();
329  
                    m_->unlock();
330  
                m_ = std::exchange(o.m_, nullptr);
330  
                m_ = std::exchange(o.m_, nullptr);
331  
            }
331  
            }
332  
            return *this;
332  
            return *this;
333  
        }
333  
        }
334  

334  

335  
        lock_guard(lock_guard const&) = delete;
335  
        lock_guard(lock_guard const&) = delete;
336  
        lock_guard& operator=(lock_guard const&) = delete;
336  
        lock_guard& operator=(lock_guard const&) = delete;
337  
    };
337  
    };
338  

338  

339  
    /** Awaiter returned by scoped_lock() that returns a lock_guard on resume.
339  
    /** Awaiter returned by scoped_lock() that returns a lock_guard on resume.
340  
    */
340  
    */
341  
    class lock_guard_awaiter
341  
    class lock_guard_awaiter
342  
    {
342  
    {
343  
        async_mutex* m_;
343  
        async_mutex* m_;
344  
        lock_awaiter inner_;
344  
        lock_awaiter inner_;
345  

345  

346  
    public:
346  
    public:
347  
        explicit lock_guard_awaiter(async_mutex* m) noexcept
347  
        explicit lock_guard_awaiter(async_mutex* m) noexcept
348  
            : m_(m)
348  
            : m_(m)
349  
            , inner_(m)
349  
            , inner_(m)
350  
        {
350  
        {
351  
        }
351  
        }
352  

352  

353  
        bool await_ready() const noexcept
353  
        bool await_ready() const noexcept
354  
        {
354  
        {
355  
            return inner_.await_ready();
355  
            return inner_.await_ready();
356  
        }
356  
        }
357  

357  

358  
        /** IoAwaitable protocol overload. */
358  
        /** IoAwaitable protocol overload. */
359  
        std::coroutine_handle<>
359  
        std::coroutine_handle<>
360  
        await_suspend(
360  
        await_suspend(
361  
            std::coroutine_handle<> h,
361  
            std::coroutine_handle<> h,
362  
            io_env const* env) noexcept
362  
            io_env const* env) noexcept
363  
        {
363  
        {
364  
            return inner_.await_suspend(h, env);
364  
            return inner_.await_suspend(h, env);
365  
        }
365  
        }
366  

366  

367  
        io_result<lock_guard> await_resume() noexcept
367  
        io_result<lock_guard> await_resume() noexcept
368  
        {
368  
        {
369  
            auto r = inner_.await_resume();
369  
            auto r = inner_.await_resume();
370  
            if(r.ec)
370  
            if(r.ec)
371  
                return {r.ec, {}};
371  
                return {r.ec, {}};
372  
            return {{}, lock_guard(m_)};
372  
            return {{}, lock_guard(m_)};
373  
        }
373  
        }
374  
    };
374  
    };
375  

375  

376  
    /// Construct an unlocked mutex.
376  
    /// Construct an unlocked mutex.
377  
    async_mutex() = default;
377  
    async_mutex() = default;
378  

378  

379  
    /// Copy constructor (deleted).
379  
    /// Copy constructor (deleted).
380  
    async_mutex(async_mutex const&) = delete;
380  
    async_mutex(async_mutex const&) = delete;
381  

381  

382  
    /// Copy assignment (deleted).
382  
    /// Copy assignment (deleted).
383  
    async_mutex& operator=(async_mutex const&) = delete;
383  
    async_mutex& operator=(async_mutex const&) = delete;
384  

384  

385  
    /// Move constructor (deleted).
385  
    /// Move constructor (deleted).
386  
    async_mutex(async_mutex&&) = delete;
386  
    async_mutex(async_mutex&&) = delete;
387  

387  

388  
    /// Move assignment (deleted).
388  
    /// Move assignment (deleted).
389  
    async_mutex& operator=(async_mutex&&) = delete;
389  
    async_mutex& operator=(async_mutex&&) = delete;
390  

390  

391  
    /** Returns an awaiter that acquires the mutex.
391  
    /** Returns an awaiter that acquires the mutex.
392  

392  

393  
        @return An awaitable yielding `(error_code)`.
393  
        @return An awaitable yielding `(error_code)`.
394  
    */
394  
    */
395  
    lock_awaiter lock() noexcept
395  
    lock_awaiter lock() noexcept
396  
    {
396  
    {
397  
        return lock_awaiter{this};
397  
        return lock_awaiter{this};
398  
    }
398  
    }
399  

399  

400  
    /** Returns an awaiter that acquires the mutex with RAII.
400  
    /** Returns an awaiter that acquires the mutex with RAII.
401  

401  

402  
        @return An awaitable yielding `(error_code,lock_guard)`.
402  
        @return An awaitable yielding `(error_code,lock_guard)`.
403  
    */
403  
    */
404  
    lock_guard_awaiter scoped_lock() noexcept
404  
    lock_guard_awaiter scoped_lock() noexcept
405  
    {
405  
    {
406  
        return lock_guard_awaiter(this);
406  
        return lock_guard_awaiter(this);
407  
    }
407  
    }
408  

408  

409  
    /** Releases the mutex.
409  
    /** Releases the mutex.
410  

410  

411  
        If waiters are queued, the next eligible waiter is
411  
        If waiters are queued, the next eligible waiter is
412  
        resumed with the lock held. Canceled waiters are
412  
        resumed with the lock held. Canceled waiters are
413  
        skipped. If no eligible waiter remains, the mutex
413  
        skipped. If no eligible waiter remains, the mutex
414  
        becomes unlocked.
414  
        becomes unlocked.
415  
    */
415  
    */
416  
    void unlock() noexcept
416  
    void unlock() noexcept
417  
    {
417  
    {
418  
        for(;;)
418  
        for(;;)
419  
        {
419  
        {
420  
            auto* waiter = waiters_.pop_front();
420  
            auto* waiter = waiters_.pop_front();
421  
            if(!waiter)
421  
            if(!waiter)
422  
            {
422  
            {
423  
                locked_ = false;
423  
                locked_ = false;
424  
                return;
424  
                return;
425  
            }
425  
            }
426  
            if(!waiter->claimed_.exchange(
426  
            if(!waiter->claimed_.exchange(
427  
                true, std::memory_order_acq_rel))
427  
                true, std::memory_order_acq_rel))
428  
            {
428  
            {
429  
                waiter->ex_.post(waiter->h_);
429  
                waiter->ex_.post(waiter->h_);
430  
                return;
430  
                return;
431  
            }
431  
            }
432  
        }
432  
        }
433  
    }
433  
    }
434  

434  

435  
    /** Returns true if the mutex is currently locked.
435  
    /** Returns true if the mutex is currently locked.
436  
    */
436  
    */
437  
    bool is_locked() const noexcept
437  
    bool is_locked() const noexcept
438  
    {
438  
    {
439  
        return locked_;
439  
        return locked_;
440  
    }
440  
    }
441  
};
441  
};
442  

442  

443  
} // namespace capy
443  
} // namespace capy
444  
} // namespace boost
444  
} // namespace boost
445  

445  

446  
#endif
446  
#endif