From 74bc9f06e3de0089a1b726b4f0a0e0fc1725c5e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Teodor=20Sp=C3=A6ren?= Date: Mon, 8 Apr 2024 23:55:02 +0200 Subject: [PATCH 1/4] Add atomic options for integers --- libstdc++-v3/include/bits/atomic_base.h | 133 ++++++++++++++++++++++++ libstdc++-v3/include/std/atomic | 10 ++ 2 files changed, 143 insertions(+) diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h index dd360302f80..3e12cc917b5 100644 --- a/libstdc++-v3/include/bits/atomic_base.h +++ b/libstdc++-v3/include/bits/atomic_base.h @@ -40,6 +40,11 @@ #if __cplusplus > 201703L && _GLIBCXX_HOSTED #include + +#include +#include +#include +#include #endif #ifndef _GLIBCXX_ALWAYS_INLINE @@ -48,6 +53,7 @@ #include + namespace std _GLIBCXX_VISIBILITY(default) { _GLIBCXX_BEGIN_NAMESPACE_VERSION @@ -274,6 +280,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION notify_all() noexcept { std::__atomic_notify_address(&_M_i, true); } + _GLIBCXX_ALWAYS_INLINE bool + wait_value(bool __old, + memory_order __m = memory_order_seq_cst) const noexcept { + bool __ret; + std::__atomic_wait_address_v(&_M_i, __old, [__m, this, &__ret] { + __ret = __atomic_load_n(&_M_i, int(__m)); + return __ret; + }); + return __ret; + } + // TODO add const volatile overload #endif // __glibcxx_atomic_wait @@ -622,6 +639,111 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION notify_all() noexcept { std::__atomic_notify_address(&_M_i, true); } + _GLIBCXX_ALWAYS_INLINE __int_type + wait_value(__int_type __old, + memory_order __m = memory_order_seq_cst) const noexcept { + return this->wait_with_predicate([__old](const auto __cur) { + // TODO(rHermes): consider doing atomic comparisons here? + return __old != __cur; + }, __m); + } + + template requires std::predicate<__Pred, __int_type> + _GLIBCXX_ALWAYS_INLINE __int_type + wait_with_predicate(__Pred &&__stop_waiting, + memory_order __m = memory_order_seq_cst) const { + __int_type __ret; + std::__atomic_wait_address(&_M_i, [__m, this, &__stop_waiting, &__ret] { + __ret = this->load(__m); + return std::invoke(__stop_waiting, __ret); + }); + return __ret; + } + + template + _GLIBCXX_ALWAYS_INLINE std::optional<__int_type> + wait_for(__int_type __old, + const std::chrono::duration<__Rep, __Period> &__dur, + memory_order __m = memory_order_seq_cst) const { + // the reson for this, is that we need to ensure that the predicate + // is called, so we are sure the value has changed, and we are able to + // return it. + return this->wait_for_with_predicate([__old](__int_type __cur) { + // TODO(rHermes): consider doing atomic comparisons here? + return __old != __cur; + }, __dur, __m); + } + + template + _GLIBCXX_ALWAYS_INLINE std::optional<__int_type> + wait_until(__int_type __old, + const std::chrono::time_point<__Clock, __Duration> &__deadline, + memory_order __m = memory_order_seq_cst) const { + return this->wait_until_with_predicate([__old](const auto __cur) { + // TODO(rHermes): consider doing atomic comparisons here? + return __old != __cur; + }, __deadline, __m); + } + + _GLIBCXX_ALWAYS_INLINE std::optional<__int_type> + try_wait(__int_type __old, + memory_order __m = memory_order_seq_cst) const noexcept { + // TODO(rHermes): This is really not a good default, I need to + // discuss withsomeone smarter + return wait_for(__old, std::chrono::milliseconds(10), __m); + } + + template + requires std::predicate<__Pred, __int_type> + _GLIBCXX_ALWAYS_INLINE std::optional<__int_type> wait_for_with_predicate( + __Pred &&__stop_waiting, + const std::chrono::duration<__Rep, __Period> &__dur, + memory_order __m = memory_order_seq_cst) const { + __int_type __ret; + auto __succeeded = std::__atomic_wait_address_for( + &_M_i, + [__m, this, &__stop_waiting, &__ret] { + __ret = this->load(__m); + return std::invoke(__stop_waiting, __ret); + }, + __dur); + + if (!__succeeded) + return std::nullopt; + + return __ret; + } + + template requires std::predicate<__Pred, __int_type> + _GLIBCXX_ALWAYS_INLINE std::optional<__int_type> + try_wait_with_predicate(__Pred &&__stop_waiting, + memory_order __m = memory_order_seq_cst) const { + return wait_for_with_predicate(std::forward<__Pred>(__stop_waiting), + std::chrono::milliseconds(10), __m); + } + + template + requires std::predicate<__Pred, __int_type> + _GLIBCXX_ALWAYS_INLINE std::optional<__int_type> + wait_until_with_predicate( + __Pred &&__stop_waiting, + const std::chrono::time_point<__Clock, __Duration> &__deadline, + memory_order __m = memory_order_seq_cst) const { + __int_type __ret; + auto __succeeded = std::__atomic_wait_address_until( + &_M_i, + [__m, this, &__stop_waiting, &__ret] { + __ret = this->load(__m); + return std::invoke(__stop_waiting, __ret); + }, + __deadline); + + if (!__succeeded) + return std::nullopt; + + return __ret; + } + // TODO add const volatile overload #endif // __glibcxx_atomic_wait @@ -925,6 +1047,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION notify_all() const noexcept { std::__atomic_notify_address(&_M_p, true); } + _GLIBCXX_ALWAYS_INLINE __pointer_type + wait_value(__pointer_type __old, + memory_order __m = memory_order_seq_cst) const noexcept { + __pointer_type __ret; + std::__atomic_wait_address_v(&_M_p, __old, [__m, this, &__ret] { + __ret = this->load(__m); + return __ret; + }); + return __ret; + } + // TODO add const volatile overload #endif // __glibcxx_atomic_wait diff --git a/libstdc++-v3/include/std/atomic b/libstdc++-v3/include/std/atomic index 1462cf5ec23..3efcddc6938 100644 --- a/libstdc++-v3/include/std/atomic +++ b/libstdc++-v3/include/std/atomic @@ -183,6 +183,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION void notify_all() noexcept { _M_base.notify_all(); } + + bool + wait_value(bool __old, memory_order __m = memory_order_seq_cst) const noexcept { + return _M_base.wait_value(__old, __m); + } #endif // __cpp_lib_atomic_wait }; @@ -663,6 +668,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION void notify_all() noexcept { _M_b.notify_all(); } + + __pointer_type + wait_value(__pointer_type __old, memory_order __m = memory_order_seq_cst) const noexcept + { return _M_b.wait_value(__old, __m); } + #endif // __cpp_lib_atomic_wait __pointer_type -- 2.44.0