|
||||
File indexing completed on 2025-01-18 09:54:51
0001 /////////////////////////////////////////////////////////////////////////////// 0002 // Copyright (c) Lewis Baker 0003 // Licenced under MIT license. See LICENSE.txt for details. 0004 /////////////////////////////////////////////////////////////////////////////// 0005 #ifndef CPPCORO_ASYNC_MUTEX_HPP_INCLUDED 0006 #define CPPCORO_ASYNC_MUTEX_HPP_INCLUDED 0007 0008 #include <cppcoro/coroutine.hpp> 0009 #include <atomic> 0010 #include <cstdint> 0011 #include <mutex> // for std::adopt_lock_t 0012 0013 namespace cppcoro 0014 { 0015 class async_mutex_lock; 0016 class async_mutex_lock_operation; 0017 class async_mutex_scoped_lock_operation; 0018 0019 /// \brief 0020 /// A mutex that can be locked asynchronously using 'co_await'. 0021 /// 0022 /// Ownership of the mutex is not tied to any particular thread. 0023 /// This allows the coroutine owning the lock to transition from 0024 /// one thread to another while holding a lock. 0025 /// 0026 /// Implementation is lock-free, using only std::atomic values for 0027 /// synchronisation. Awaiting coroutines are suspended without blocking 0028 /// the current thread if the lock could not be acquired synchronously. 0029 class async_mutex 0030 { 0031 public: 0032 0033 /// \brief 0034 /// Construct to a mutex that is not currently locked. 0035 async_mutex() noexcept; 0036 0037 /// Destroys the mutex. 0038 /// 0039 /// Behaviour is undefined if there are any outstanding coroutines 0040 /// still waiting to acquire the lock. 0041 ~async_mutex(); 0042 0043 /// \brief 0044 /// Attempt to acquire a lock on the mutex without blocking. 0045 /// 0046 /// \return 0047 /// true if the lock was acquired, false if the mutex was already locked. 0048 /// The caller is responsible for ensuring unlock() is called on the mutex 0049 /// to release the lock if the lock was acquired by this call. 0050 bool try_lock() noexcept; 0051 0052 /// \brief 0053 /// Acquire a lock on the mutex asynchronously. 0054 /// 0055 /// If the lock could not be acquired synchronously then the awaiting 0056 /// coroutine will be suspended and later resumed when the lock becomes 0057 /// available. If suspended, the coroutine will be resumed inside the 0058 /// call to unlock() from the previous lock owner. 0059 /// 0060 /// \return 0061 /// An operation object that must be 'co_await'ed to wait until the 0062 /// lock is acquired. The result of the 'co_await m.lock_async()' 0063 /// expression has type 'void'. 0064 async_mutex_lock_operation lock_async() noexcept; 0065 0066 /// \brief 0067 /// Acquire a lock on the mutex asynchronously, returning an object that 0068 /// will call unlock() automatically when it goes out of scope. 0069 /// 0070 /// If the lock could not be acquired synchronously then the awaiting 0071 /// coroutine will be suspended and later resumed when the lock becomes 0072 /// available. If suspended, the coroutine will be resumed inside the 0073 /// call to unlock() from the previous lock owner. 0074 /// 0075 /// \return 0076 /// An operation object that must be 'co_await'ed to wait until the 0077 /// lock is acquired. The result of the 'co_await m.scoped_lock_async()' 0078 /// expression returns an 'async_mutex_lock' object that will call 0079 /// this->mutex() when it destructs. 0080 async_mutex_scoped_lock_operation scoped_lock_async() noexcept; 0081 0082 /// \brief 0083 /// Unlock the mutex. 0084 /// 0085 /// Must only be called by the current lock-holder. 0086 /// 0087 /// If there are lock operations waiting to acquire the 0088 /// mutex then the next lock operation in the queue will 0089 /// be resumed inside this call. 0090 void unlock(); 0091 0092 private: 0093 0094 friend class async_mutex_lock_operation; 0095 0096 static constexpr std::uintptr_t not_locked = 1; 0097 0098 // assume == reinterpret_cast<std::uintptr_t>(static_cast<void*>(nullptr)) 0099 static constexpr std::uintptr_t locked_no_waiters = 0; 0100 0101 // This field provides synchronisation for the mutex. 0102 // 0103 // It can have three kinds of values: 0104 // - not_locked 0105 // - locked_no_waiters 0106 // - a pointer to the head of a singly linked list of recently 0107 // queued async_mutex_lock_operation objects. This list is 0108 // in most-recently-queued order as new items are pushed onto 0109 // the front of the list. 0110 std::atomic<std::uintptr_t> m_state; 0111 0112 // Linked list of async lock operations that are waiting to acquire 0113 // the mutex. These operations will acquire the lock in the order 0114 // they appear in this list. Waiters in this list will acquire the 0115 // mutex before waiters added to the m_newWaiters list. 0116 async_mutex_lock_operation* m_waiters; 0117 0118 }; 0119 0120 /// \brief 0121 /// An object that holds onto a mutex lock for its lifetime and 0122 /// ensures that the mutex is unlocked when it is destructed. 0123 /// 0124 /// It is equivalent to a std::lock_guard object but requires 0125 /// that the result of co_await async_mutex::lock_async() is 0126 /// passed to the constructor rather than passing the async_mutex 0127 /// object itself. 0128 class async_mutex_lock 0129 { 0130 public: 0131 0132 explicit async_mutex_lock(async_mutex& mutex, std::adopt_lock_t) noexcept 0133 : m_mutex(&mutex) 0134 {} 0135 0136 async_mutex_lock(async_mutex_lock&& other) noexcept 0137 : m_mutex(other.m_mutex) 0138 { 0139 other.m_mutex = nullptr; 0140 } 0141 0142 async_mutex_lock(const async_mutex_lock& other) = delete; 0143 async_mutex_lock& operator=(const async_mutex_lock& other) = delete; 0144 0145 // Releases the lock. 0146 ~async_mutex_lock() 0147 { 0148 if (m_mutex != nullptr) 0149 { 0150 m_mutex->unlock(); 0151 } 0152 } 0153 0154 private: 0155 0156 async_mutex* m_mutex; 0157 0158 }; 0159 0160 class async_mutex_lock_operation 0161 { 0162 public: 0163 0164 explicit async_mutex_lock_operation(async_mutex& mutex) noexcept 0165 : m_mutex(mutex) 0166 {} 0167 0168 bool await_ready() const noexcept { return false; } 0169 bool await_suspend(cppcoro::coroutine_handle<> awaiter) noexcept; 0170 void await_resume() const noexcept {} 0171 0172 protected: 0173 0174 friend class async_mutex; 0175 0176 async_mutex& m_mutex; 0177 0178 private: 0179 0180 async_mutex_lock_operation* m_next; 0181 cppcoro::coroutine_handle<> m_awaiter; 0182 0183 }; 0184 0185 class async_mutex_scoped_lock_operation : public async_mutex_lock_operation 0186 { 0187 public: 0188 0189 using async_mutex_lock_operation::async_mutex_lock_operation; 0190 0191 [[nodiscard]] 0192 async_mutex_lock await_resume() const noexcept 0193 { 0194 return async_mutex_lock{ m_mutex, std::adopt_lock }; 0195 } 0196 0197 }; 0198 } 0199 0200 #endif
[ Source navigation ] | [ Diff markup ] | [ Identifier search ] | [ general search ] |
This page was automatically generated by the 2.3.7 LXR engine. The LXR team |