25 constexpr static struct {
27 friend class mutex_lock;
29 constexpr mutex() noexcept : m_state{state_unlocked}, m_awaiters{
nullptr} {}
32 : m_state{state_locked_no_waiters}, m_awaiters{
nullptr} {}
39 [[maybe_unused]]
auto state = m_state.load(std::memory_order_relaxed);
40 assert(state == state_unlocked || state == state_locked_no_waiters);
41 assert(m_awaiters ==
nullptr);
45 mutex& operator=(const
mutex&) noexcept = delete;
55 auto old = state_unlocked;
56 return m_state.compare_exchange_strong(old, state_locked_no_waiters, std::memory_order::acquire,
57 std::memory_order::relaxed);
65 [[nodiscard]]
constexpr lock_awaiter
lock() noexcept;
72 [[nodiscard]] constexpr scoped_lock_awaiter
lock_scoped() noexcept;
85 return m_state.load(std::memory_order::relaxed) != state_unlocked;
89 static constexpr std::uintptr_t state_locked_no_waiters = 0;
90 static constexpr std::uintptr_t state_unlocked = 1;
91 std::atomic<uintptr_t> m_state;
92 lock_awaiter* m_awaiters;
97 [[nodiscard]]
constexpr bool await_ready()
const noexcept {
return false; }
98 [[nodiscard]]
bool await_suspend(coroutine_handle<> hndl)
noexcept {
100 auto old =
mutex->m_state.load(std::memory_order::acquire);
102 if (old == state_unlocked) {
103 if (
mutex->m_state.compare_exchange_weak(old, state_locked_no_waiters, std::memory_order::acquire,
104 std::memory_order::relaxed))
109 if (
mutex->m_state.compare_exchange_weak(old,
reinterpret_cast<std::uintptr_t
>(
this),
110 std::memory_order::release, std::memory_order::relaxed))
115 constexpr void await_resume()
const noexcept {}
119 coroutine_handle<> handle{};
131 mutex_lock(
class mutex& mtx, std::adopt_lock_t) noexcept : m_mtx(&mtx), m_locked{
true} {
132 assert(mtx.is_locked());
138 explicit constexpr mutex_lock(
class mutex& mtx) noexcept : m_mtx(&mtx), m_locked{
false} {}
141 constexpr mutex_lock(
mutex_lock&& other) noexcept : m_mtx(other.m_mtx), m_locked{other.m_locked} {
142 other.m_mtx =
nullptr;
143 other.m_locked =
false;
146 if (m_mtx !=
nullptr && m_locked) m_mtx->
unlock();
148 other.m_mtx =
nullptr;
149 m_locked = other.m_locked;
150 other.m_locked =
false;
154 if (m_mtx !=
nullptr && m_locked) m_mtx->
unlock();
184 [[nodiscard]]
auto lock() noexcept {
186 explicit awaiter(
mutex_lock* parent) : m_parent(parent), m_mutex_awaiter(parent->m_mtx) {}
187 [[nodiscard]]
bool await_ready()
const noexcept {
188 return m_parent->m_locked || m_mutex_awaiter.await_ready();
190 [[nodiscard]]
auto await_suspend(coroutine_handle<> hndl)
noexcept {
191 return m_mutex_awaiter.await_suspend(hndl);
193 void await_resume()
const noexcept {
194 m_mutex_awaiter.await_resume();
195 m_parent->m_locked =
true;
202 return awaiter{
this};
206 [[nodiscard]]
bool is_locked() const noexcept {
return m_locked; }
208 [[nodiscard]]
class mutex&
mutex() const noexcept {
return *m_mtx; }
217 [[nodiscard]]
constexpr bool await_ready()
const noexcept {
return awaiter.await_ready(); }
218 [[nodiscard]]
bool await_suspend(coroutine_handle<> hndl)
noexcept {
return awaiter.await_suspend(hndl); }
219 [[nodiscard]]
mutex_lock await_resume()
const noexcept {
220 awaiter.await_resume();
233 assert(m_state.load(std::memory_order::relaxed) != state_unlocked);
235 auto head = m_awaiters;
236 if (head ==
nullptr) {
237 auto old = state_locked_no_waiters;
238 const auto didrelease = m_state.compare_exchange_strong(old, state_unlocked, std::memory_order::release,
239 std::memory_order::relaxed);
240 if (didrelease)
return;
241 old = m_state.exchange(state_locked_no_waiters, std::memory_order::acquire);
242 assert(old != state_locked_no_waiters && old != state_unlocked);
246 auto temp = next->next;
250 }
while (next !=
nullptr);
252 assert(head !=
nullptr);
253 m_awaiters = head->next;
254 head->handle.resume();
RAII type to automatically unlock a mutex once it leaves scope.
Definition mutex.h:125
void unlock() noexcept
Unlock the lock See mutex::unlock() for details.
Definition mutex.h:161
bool is_locked() const noexcept
Check if the mutex is held by this lock.
Definition mutex.h:206
mutex_lock(class mutex &mtx, std::adopt_lock_t) noexcept
Construct a mutex_lock for the given mutex, adopting the lock.
Definition mutex.h:131
auto lock() noexcept
Asynchronously lock the contained mutex.
Definition mutex.h:184
class mutex & mutex() const noexcept
Get the wrapped mutex.
Definition mutex.h:208
constexpr mutex_lock(class mutex &mtx) noexcept
Construct an unlocked mutex_lock for the given mutex.
Definition mutex.h:138
bool try_lock() noexcept
Try locking the contained mutex.
Definition mutex.h:172
A mutex with an asynchronous lock() operation.
Definition mutex.h:20
constexpr mutex(decltype(construct_locked)) noexcept
Construct mutex in its locked state.
Definition mutex.h:31
bool is_locked() const noexcept
Query if the lock is currently locked.
Definition mutex.h:84
constexpr lock_awaiter lock() noexcept
Acquire the the mutex using co_await.
Definition mutex.h:228
constexpr mutex() noexcept
Construct mutex in its unlocked state.
Definition mutex.h:29
constexpr scoped_lock_awaiter lock_scoped() noexcept
Acquire the the mutex using co_await and wrap it in a mutex_lock.
Definition mutex.h:230
bool try_lock() noexcept
Attempt to acquire the mutex whitout blocking or yielding.
Definition mutex.h:54
void unlock() noexcept
Unlock the mutex.
Definition mutex.h:232
~mutex()
Destruct mutex.
Definition mutex.h:38
static constexpr struct asyncpp::mutex::@0 construct_locked
Tag value used to construct locked mutex.
Provides a consistent import interface for coroutine, experimental/coroutine or a best effort fallbac...