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  
        BOOST_CAPY_MSVC_WARNING_PUSH
197  
        BOOST_CAPY_MSVC_WARNING_PUSH
198  
        BOOST_CAPY_MSVC_WARNING_DISABLE(4324) // padded due to alignas
198  
        BOOST_CAPY_MSVC_WARNING_DISABLE(4324) // padded due to alignas
199  
        alignas(stop_cb_t)
199  
        alignas(stop_cb_t)
200  
            unsigned char stop_cb_buf_[sizeof(stop_cb_t)];
200  
            unsigned char stop_cb_buf_[sizeof(stop_cb_t)];
201  
        BOOST_CAPY_MSVC_WARNING_POP
201  
        BOOST_CAPY_MSVC_WARNING_POP
202  

202  

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

208  

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

218  

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

223  

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

234  

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

238  

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

248  

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

268  

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

289  

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

291  

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

297  

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

304  

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

309  

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

314  

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

319  

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

330  

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

334  

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

341  

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

348  

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

353  

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

362  

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

371  

372  
    /// Construct an unlocked mutex.
372  
    /// Construct an unlocked mutex.
373  
    async_mutex() = default;
373  
    async_mutex() = default;
374  

374  

375  
    /// Copy constructor (deleted).
375  
    /// Copy constructor (deleted).
376  
    async_mutex(async_mutex const&) = delete;
376  
    async_mutex(async_mutex const&) = delete;
377  

377  

378  
    /// Copy assignment (deleted).
378  
    /// Copy assignment (deleted).
379  
    async_mutex& operator=(async_mutex const&) = delete;
379  
    async_mutex& operator=(async_mutex const&) = delete;
380  

380  

381  
    /// Move constructor (deleted).
381  
    /// Move constructor (deleted).
382  
    async_mutex(async_mutex&&) = delete;
382  
    async_mutex(async_mutex&&) = delete;
383  

383  

384  
    /// Move assignment (deleted).
384  
    /// Move assignment (deleted).
385  
    async_mutex& operator=(async_mutex&&) = delete;
385  
    async_mutex& operator=(async_mutex&&) = delete;
386  

386  

387  
    /** Returns an awaiter that acquires the mutex.
387  
    /** Returns an awaiter that acquires the mutex.
388  

388  

389  
        @return An awaitable that await-returns `(error_code)`.
389  
        @return An awaitable that await-returns `(error_code)`.
390  
    */
390  
    */
391  
    lock_awaiter lock() noexcept
391  
    lock_awaiter lock() noexcept
392  
    {
392  
    {
393  
        return lock_awaiter{this};
393  
        return lock_awaiter{this};
394  
    }
394  
    }
395  

395  

396  
    /** Returns an awaiter that acquires the mutex with RAII.
396  
    /** Returns an awaiter that acquires the mutex with RAII.
397  

397  

398  
        @return An awaitable that await-returns `(error_code,lock_guard)`.
398  
        @return An awaitable that await-returns `(error_code,lock_guard)`.
399  
    */
399  
    */
400  
    lock_guard_awaiter scoped_lock() noexcept
400  
    lock_guard_awaiter scoped_lock() noexcept
401  
    {
401  
    {
402  
        return lock_guard_awaiter(this);
402  
        return lock_guard_awaiter(this);
403  
    }
403  
    }
404  

404  

405  
    /** Releases the mutex.
405  
    /** Releases the mutex.
406  

406  

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

430  

431  
    /** Returns true if the mutex is currently locked.
431  
    /** Returns true if the mutex is currently locked.
432  
    */
432  
    */
433  
    bool is_locked() const noexcept
433  
    bool is_locked() const noexcept
434  
    {
434  
    {
435  
        return locked_;
435  
        return locked_;
436  
    }
436  
    }
437  
};
437  
};
438  

438  

439  
} // namespace capy
439  
} // namespace capy
440  
} // namespace boost
440  
} // namespace boost
441  

441  

442  
#endif
442  
#endif