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 +
        BOOST_CAPY_MSVC_WARNING_PUSH
198 -
# pragma warning(push)
198 +
        BOOST_CAPY_MSVC_WARNING_DISABLE(4324) // padded due to alignas
199 -
# pragma warning(disable: 4324) // padded due to alignas
 
200 -
#endif
 
201  
        alignas(stop_cb_t)
199  
        alignas(stop_cb_t)
202  
            unsigned char stop_cb_buf_[sizeof(stop_cb_t)];
200  
            unsigned char stop_cb_buf_[sizeof(stop_cb_t)];
203 -
#ifdef _MSC_VER
201 +
        BOOST_CAPY_MSVC_WARNING_POP
204 -
# pragma warning(pop)
 
205 -
#endif
 
206  

202  

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

208  

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

218  

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

223  

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

234  

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

238  

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

248  

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

268  

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

289  

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

291  

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

297  

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

304  

309  
        lock_guard() noexcept
305  
        lock_guard() noexcept
310  
            : m_(nullptr)
306  
            : m_(nullptr)
311  
        {
307  
        {
312  
        }
308  
        }
313  

309  

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

314  

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

319  

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

330  

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

334  

339  
    /** 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.
340  
    */
336  
    */
341  
    class lock_guard_awaiter
337  
    class lock_guard_awaiter
342  
    {
338  
    {
343  
        async_mutex* m_;
339  
        async_mutex* m_;
344  
        lock_awaiter inner_;
340  
        lock_awaiter inner_;
345  

341  

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

348  

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

353  

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

362  

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

371  

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

374  

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

377  

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

380  

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

383  

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

386  

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

388  

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

395  

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

397  

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

404  

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

406  

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

430  

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

438  

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

441  

446  
#endif
442  
#endif