aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/libcxx/include/__stop_token/atomic_unique_lock.h
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/libcxx/include/__stop_token/atomic_unique_lock.h')
-rw-r--r--contrib/llvm-project/libcxx/include/__stop_token/atomic_unique_lock.h139
1 files changed, 139 insertions, 0 deletions
diff --git a/contrib/llvm-project/libcxx/include/__stop_token/atomic_unique_lock.h b/contrib/llvm-project/libcxx/include/__stop_token/atomic_unique_lock.h
new file mode 100644
index 000000000000..6c63a254eab9
--- /dev/null
+++ b/contrib/llvm-project/libcxx/include/__stop_token/atomic_unique_lock.h
@@ -0,0 +1,139 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___STOP_TOKEN_ATOMIC_UNIQUE_GUARD_H
+#define _LIBCPP___STOP_TOKEN_ATOMIC_UNIQUE_GUARD_H
+
+#include <__bit/popcount.h>
+#include <__config>
+#include <atomic>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20
+
+// This class implements an RAII unique_lock without a mutex.
+// It uses std::atomic<State>,
+// where State contains a lock bit and might contain other data,
+// and LockedBit is the value of State when the lock bit is set, e.g 1 << 2
+template <class _State, _State _LockedBit>
+class _LIBCPP_AVAILABILITY_SYNC __atomic_unique_lock {
+ static_assert(std::popcount(_LockedBit) == 1, "LockedBit must be an integer where only one bit is set");
+
+ std::atomic<_State>& __state_;
+ bool __is_locked_;
+
+public:
+ _LIBCPP_HIDE_FROM_ABI explicit __atomic_unique_lock(std::atomic<_State>& __state) noexcept
+ : __state_(__state), __is_locked_(true) {
+ __lock();
+ }
+
+ template <class _Pred>
+ _LIBCPP_HIDE_FROM_ABI __atomic_unique_lock(std::atomic<_State>& __state, _Pred&& __give_up_locking) noexcept
+ : __state_(__state), __is_locked_(false) {
+ __is_locked_ = __lock_impl(__give_up_locking, __set_locked_bit, std::memory_order_acquire);
+ }
+
+ template <class _Pred, class _UnaryFunction>
+ _LIBCPP_HIDE_FROM_ABI __atomic_unique_lock(
+ std::atomic<_State>& __state,
+ _Pred&& __give_up_locking,
+ _UnaryFunction&& __state_after_lock,
+ std::memory_order __locked_ordering) noexcept
+ : __state_(__state), __is_locked_(false) {
+ __is_locked_ = __lock_impl(__give_up_locking, __state_after_lock, __locked_ordering);
+ }
+
+ __atomic_unique_lock(const __atomic_unique_lock&) = delete;
+ __atomic_unique_lock(__atomic_unique_lock&&) = delete;
+ __atomic_unique_lock& operator=(const __atomic_unique_lock&) = delete;
+ __atomic_unique_lock& operator=(__atomic_unique_lock&&) = delete;
+
+ _LIBCPP_HIDE_FROM_ABI ~__atomic_unique_lock() {
+ if (__is_locked_) {
+ __unlock();
+ }
+ }
+
+ _LIBCPP_HIDE_FROM_ABI bool __owns_lock() const noexcept { return __is_locked_; }
+
+ _LIBCPP_HIDE_FROM_ABI void __lock() noexcept {
+ const auto __never_give_up_locking = [](_State) { return false; };
+ // std::memory_order_acquire because we'd like to make sure that all the read operations after the lock can read the
+ // up-to-date values.
+ __lock_impl(__never_give_up_locking, __set_locked_bit, std::memory_order_acquire);
+ __is_locked_ = true;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI void __unlock() noexcept {
+ // unset the _LockedBit. `memory_order_release` because we need to make sure all the write operations before calling
+ // `__unlock` will be made visible to other threads
+ __state_.fetch_and(static_cast<_State>(~_LockedBit), std::memory_order_release);
+ __state_.notify_all();
+ __is_locked_ = false;
+ }
+
+private:
+ template <class _Pred, class _UnaryFunction>
+ _LIBCPP_HIDE_FROM_ABI bool
+ __lock_impl(_Pred&& __give_up_locking, // while trying to lock the state, if the predicate returns true, give up
+ // locking and return
+ _UnaryFunction&& __state_after_lock,
+ std::memory_order __locked_ordering) noexcept {
+ // At this stage, until we exit the inner while loop, other than the atomic state, we are not reading any order
+ // dependent values that is written on other threads, or writing anything that needs to be seen on other threads.
+ // Therefore `memory_order_relaxed` is enough.
+ _State __current_state = __state_.load(std::memory_order_relaxed);
+ do {
+ while (true) {
+ if (__give_up_locking(__current_state)) {
+ // user provided early return condition. fail to lock
+ return false;
+ } else if ((__current_state & _LockedBit) != 0) {
+ // another thread has locked the state, we need to wait
+ __state_.wait(__current_state, std::memory_order_relaxed);
+ // when it is woken up by notifyAll or spuriously, the __state_
+ // might have changed. reload the state
+ // Note that the new state's _LockedBit may or may not equal to 0
+ __current_state = __state_.load(std::memory_order_relaxed);
+ } else {
+ // at least for now, it is not locked. we can try `compare_exchange_weak` to lock it.
+ // Note that the variable `__current_state`'s lock bit has to be 0 at this point.
+ break;
+ }
+ }
+ } while (!__state_.compare_exchange_weak(
+ __current_state, // if __state_ has the same value of __current_state, lock bit must be zero before exchange and
+ // we are good to lock/exchange and return. If _state has a different value, because other
+ // threads locked it between the `break` statement above and this statement, exchange will fail
+ // and go back to the inner while loop above.
+ __state_after_lock(__current_state), // state after lock. Usually it should be __current_state | _LockedBit.
+ // Some use cases need to set other bits at the same time as an atomic
+ // operation therefore we accept a function
+ __locked_ordering, // sucessful exchange order. Usually it should be std::memory_order_acquire.
+ // Some use cases need more strict ordering therefore we accept it as a parameter
+ std::memory_order_relaxed // fail to exchange order. We don't need any ordering as we are going back to the
+ // inner while loop
+ ));
+ return true;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI static constexpr auto __set_locked_bit = [](_State __state) { return __state | _LockedBit; };
+};
+
+#endif // _LIBCPP_STD_VER >= 20
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___STOP_TOKEN_ATOMIC_UNIQUE_GUARD_H