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

 
118  

115  

119  
    The mutex operations are designed for single-threaded use on one
116  
    The mutex operations are designed for single-threaded use on one
120  
    executor. The stop callback may fire from any thread.
117  
    executor. The stop callback may fire from any thread.
121 -
    This type is non-copyable and non-movable because suspended
 
122 -
    waiters hold intrusive pointers into the mutex's internal list.
 
123 -

 
124  

118  

125  
    @par Example
119  
    @par Example
126  
    @code
120  
    @code
127  
    async_mutex cm;
121  
    async_mutex cm;
128  

122  

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

130  

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

147  

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

151  

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

159  

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

163  

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

169  

176  
        struct cancel_fn
170  
        struct cancel_fn
177  
        {
171  
        {
178  
            lock_awaiter* self_;
172  
            lock_awaiter* self_;
179  

173  

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

184  

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

187  

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

200  

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

206  

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

216  

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

221  

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

232  

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

236  

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

246  

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

266  

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

287  

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

289  

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

295  

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

302  

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

307  

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

312  

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

317  

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

328  

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

332  

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

339  

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

346  

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

351  

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

360  

367  
        io_result<lock_guard> await_resume() noexcept
361  
        io_result<lock_guard> await_resume() noexcept
368  
        {
362  
        {
369  
            auto r = inner_.await_resume();
363  
            auto r = inner_.await_resume();
370  
            if(r.ec)
364  
            if(r.ec)
371  
                return {r.ec, {}};
365  
                return {r.ec, {}};
372  
            return {{}, lock_guard(m_)};
366  
            return {{}, lock_guard(m_)};
373  
        }
367  
        }
374  
    };
368  
    };
375 -
    /// Construct an unlocked mutex.
 
376  

369  

377  
    async_mutex() = default;
370  
    async_mutex() = default;
378  

371  

379 -
    /// Copy constructor (deleted).
372 +
    // Non-copyable, non-movable
380 -

 
381 -
    /// Copy assignment (deleted).
 
382  
    async_mutex(async_mutex const&) = delete;
373  
    async_mutex(async_mutex const&) = delete;
383 -

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

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

375  

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

377  

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

384  

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

386  

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

393  

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

395  

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

419  

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

427  

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

430  

446  
#endif
431  
#endif