* [PATCH] Want feedback for potential implementation of P2643R2 atomic features
@ 2024-04-14 20:15 Teodor Spæren
0 siblings, 0 replies; only message in thread
From: Teodor Spæren @ 2024-04-14 20:15 UTC (permalink / raw)
To: libstdc++
[-- Attachment #1: Type: text/plain, Size: 1416 bytes --]
Hello!
Earlier I sent an email about wanting to try to implement the new
features described in
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p2643r2.html
I've now gone and done that. I've added the support for all the commands
for floats, and integer values, as well as atomic_flag. I've added tests
for atomic_integer. I will of course finish all the types and add tests
for all the types, but I wanted to know if this is work that is wanted
or if it's to early to implement, and I also wanted some feedback.
I have a couple of questions:
1. How should I feature test for this to be enabled or not? For the
`wait` and `notify` functionality #if __glibcxx_atomic_wait is used,
should I guard this behind a new type of flag?
2. How is the formatting? Is there a command to format all the files?
3. What timeout values should I use for tests? I don't want the waiting
to take 2seconds, but I also don't want the tests to fail
intermittently. I've tried to use 60ms buffer now, but this seems like a
lot to me.
4. Why does "__atomic_wait_address_for_v" take the "old" parameter by
rvalue reference? Immediately we call a function which takes the
parameter by value. Is this just an oversight or is there some semantics
here that I'm not aware of?
I've attached the patches in what I hope is a good format, I hope that
I've not butchered this completely!
Best regards,
Teodor Spæren
[-- Attachment #2: 0004-Add-tests-for-atomic-integral.patch --]
[-- Type: text/x-patch, Size: 17853 bytes --]
From b0bfe970752e84493e2e79c7244372dad32c29cb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Teodor=20Sp=C3=A6ren?= <teodor@sparen.no>
Date: Sun, 14 Apr 2024 22:08:38 +0200
Subject: [PATCH 4/4] Add tests for atomic integral
---
.../atomic_integral/extended_wait.cc | 656 ++++++++++++++++++
1 file changed, 656 insertions(+)
create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic_integral/extended_wait.cc
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_integral/extended_wait.cc b/libstdc++-v3/testsuite/29_atomics/atomic_integral/extended_wait.cc
new file mode 100644
index 00000000000..6b91440f8bd
--- /dev/null
+++ b/libstdc++-v3/testsuite/29_atomics/atomic_integral/extended_wait.cc
@@ -0,0 +1,656 @@
+#include <atomic>
+#include <thread>
+#include <chrono>
+
+// { dg-do run { target c++20 } }
+// { dg-require-gthreads "" }
+// { dg-add-options libatomic }
+// { dg-additional-options "-pthread" { target pthread } }
+
+// Copyright (C) 2020-2024 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library. This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <testsuite_hooks.h>
+
+struct Timer
+{
+ std::chrono::steady_clock::time_point start{
+ std::chrono::steady_clock::now()
+ };
+
+ [[nodiscard]] auto
+ duration() const
+ {
+ return std::chrono::steady_clock::now() - start;
+ }
+};
+
+
+template<typename T>
+void check()
+{
+ using namespace std::chrono_literals;
+
+ inline constexpr auto ms_buffer = 60ms;
+
+ // Wait value should return immidiatly when it has the right value
+ {
+ const Timer t;
+ std::atomic<T> wow = 34;
+ const auto next = wow.wait_value(100);
+ VERIFY(next == 34);
+ VERIFY(t.duration() < 1ms);
+ }
+
+ // "Wait value should wait until notified atleast"
+ {
+ std::atomic<T> wow = 34;
+ const Timer t;
+ std::jthread outThere([&wow]() {
+ std::this_thread::sleep_for(50ms);
+ wow.store(102, std::memory_order::release);
+ wow.notify_all();
+ });
+
+ auto res = wow.wait_value(34);
+ VERIFY(res == 102);
+ VERIFY(50ms <= t.duration());
+ VERIFY(t.duration() < ms_buffer);
+ }
+
+ // "Wait value should not return on unchanged value."
+ {
+ const Timer t;
+ std::atomic<T> wow = 34;
+ std::jthread outThere([&wow]() {
+ std::this_thread::sleep_for(20ms);
+ wow.store(34);
+ wow.notify_all();
+ std::this_thread::sleep_for(40ms);
+ wow.store(102);
+ wow.notify_all();
+ });
+
+ auto res = wow.wait_value(34);
+ const auto dur = t.duration();
+ VERIFY(res == 102);
+ VERIFY(60ms <= dur);
+ VERIFY(dur < ms_buffer);
+ }
+
+ // "try wait should return right away when it's correct
+ {
+ const Timer t;
+ std::atomic<T> wow = 34;
+ auto res = wow.try_wait(100);
+ VERIFY(res);
+ VERIFY(*res == 34);
+ VERIFY(t.duration() < 1ms);
+ }
+
+ // "try wait should return after a little while when it's not correct
+ {
+ const Timer t;
+ std::atomic<T> wow = 34;
+ auto res = wow.try_wait(34);
+ VERIFY(!res);
+ VERIFY(t.duration() < 100ms);
+ }
+
+ // "WaitFor: No waiting if it's good
+ {
+ const Timer t;
+ std::atomic<T> wow = 34;
+ auto res = wow.wait_for(100, 10s);
+ VERIFY(res);
+ VERIFY(*res == 34);
+ VERIFY(t.duration() < 1ms);
+ }
+
+ // "WaitFor: Max waiting if it doesn't change
+ {
+ const Timer t;
+ std::atomic<T> wow = 34;
+ auto res = wow.wait_for(34, 50ms);
+ VERIFY(!res);
+ VERIFY(t.duration() < 60ms);
+ VERIFY(50ms <= t.duration());
+ }
+
+ // "WaitFor: Will wakeup if changed.
+ {
+ const Timer t;
+ std::atomic<T> wow = 34;
+
+ std::jthread outThere([&wow]() {
+ std::this_thread::sleep_for(50ms);
+ wow.store(102);
+ wow.notify_all();
+ });
+
+ auto res = wow.wait_for(34, 5s);
+ VERIFY(res);
+ VERIFY(*res == 102);
+ VERIFY(50ms <= t.duration());
+ VERIFY(t.duration() < 70ms);
+ }
+
+ // "WaitFor: Will not return if value is changed to same value.
+ {
+ const Timer t;
+ std::atomic<T> wow = 34;
+
+ std::jthread outThere([&wow]() {
+ for (int i = 0; i < 2; i++)
+ {
+ std::this_thread::sleep_for(20ms);
+ wow.store(34);
+ wow.notify_all();
+ }
+ });
+
+ auto res = wow.wait_for(34, 50ms);
+ auto taken = t.duration();
+ VERIFY(!res);
+ VERIFY(50ms <= taken);
+ VERIFY(taken < 100ms);
+ }
+
+ // "WaitUntil: Will return right away if the value is the same.
+ {
+ std::atomic<T> wow = 34;
+
+ const auto deadLine = std::chrono::system_clock::now() + 100ms;
+ auto res = wow.wait_until(102, deadLine);
+ const auto finished = std::chrono::system_clock::now();
+ VERIFY(res);
+ VERIFY(*res == 34);
+ VERIFY(finished <= deadLine);
+ }
+
+ // "WaitUntil: Will not return before deadline, if not hit
+ {
+ std::atomic<T> wow = 34;
+ const auto deadLine = std::chrono::system_clock::now() + 100ms;
+ auto res = wow.wait_until(34, deadLine);
+ const auto finished = std::chrono::system_clock::now();
+ VERIFY(!res);
+ VERIFY(deadLine <= finished);
+ VERIFY(finished < deadLine + 40ms);
+ }
+
+ // "WaitUntil: Will return if changed, if not hit
+ {
+ std::atomic<T> wow = 34;
+ const auto deadLine = std::chrono::system_clock::now() + 100ms;
+
+ std::jthread outThere([&wow]() {
+ std::this_thread::sleep_for(20ms);
+ wow.store(102);
+ wow.notify_all();
+ });
+ auto res = wow.wait_until(34, deadLine);
+ const auto finished = std::chrono::system_clock::now();
+ VERIFY(res);
+ VERIFY(*res == 102);
+ VERIFY(finished < deadLine);
+ }
+
+ // "WaitUntil: Will not return if not changed, if not hit
+ {
+ std::atomic<T> wow = 34;
+ const auto deadline = std::chrono::system_clock::now() + 100ms;
+
+ std::jthread outThere([&wow]() {
+ for (int i = 0; i < 2; i++)
+ {
+ std::this_thread::sleep_for(20ms);
+ wow.store(34);
+ wow.notify_all();
+ }
+ });
+
+ auto res = wow.wait_until(34, deadline);
+ const auto finished = std::chrono::system_clock::now();
+ VERIFY(!res);
+ VERIFY(deadline <= finished);
+ VERIFY(finished < deadline + 40ms);
+ }
+
+ // "Wait value should return immidiatly when it has the right value
+ {
+ std::atomic<T> wow = 34;
+ const Timer t;
+ const auto next = wow.wait_value(100);
+ VERIFY(next == 34);
+ VERIFY(t.duration() < 1ms);
+ }
+
+ // "Wait value should wait until notified atleast
+ {
+ std::atomic<T> wow = 34;
+ const Timer t;
+ std::jthread outThere([&wow]() {
+ std::this_thread::sleep_for(50ms);
+ wow.store(102, std::memory_order::release);
+ wow.notify_all();
+ });
+
+ auto res = wow.wait_value(34);
+ VERIFY(res == 102);
+ VERIFY(50ms <= t.duration());
+ VERIFY(t.duration() < 60ms);
+ }
+
+ // "Wait value should not return on unchanged value.
+ {
+ std::atomic<T> wow = 34;
+ const Timer t;
+ std::jthread outThere([&wow]() {
+ std::this_thread::sleep_for(20ms);
+ wow.store(34);
+ wow.notify_all();
+ std::this_thread::sleep_for(40ms);
+ wow.store(102);
+ wow.notify_all();
+ });
+
+ auto res = wow.wait_value(34);
+ const auto dur = t.duration();
+ VERIFY(res == 102);
+ VERIFY(60ms <= dur);
+ VERIFY(dur < 70ms);
+ }
+
+ // "try wait should return right away when it's correct
+ {
+ std::atomic<T> wow = 34;
+ const Timer t;
+ auto res = wow.try_wait(100);
+ VERIFY(res);
+ VERIFY(*res == 34);
+ VERIFY(t.duration() < 1ms);
+ }
+
+ // "try wait should return after a little while when it's not correct
+ {
+ std::atomic<T> wow = 34;
+ const Timer t;
+ auto res = wow.try_wait(34);
+ VERIFY(!res);
+ VERIFY(t.duration() < 100ms);
+ }
+
+ // "WaitFor: No waiting if it's good
+ {
+ std::atomic<T> wow = 34;
+ const Timer t;
+ auto res = wow.wait_for(100, 10s);
+ VERIFY(res);
+ VERIFY(*res == 34);
+ VERIFY(t.duration() < 1ms);
+ }
+
+ // "WaitFor: Max waiting if it doesn't change
+ {
+ std::atomic<T> wow = 34;
+ const Timer t;
+ auto res = wow.wait_for(34, 50ms);
+ VERIFY(!res);
+ VERIFY(t.duration() < 60ms);
+ VERIFY(50ms <= t.duration());
+ }
+
+ // "WaitFor: Will wakeup if changed.
+ {
+ std::atomic<T> wow = 34;
+ const Timer t;
+
+ std::jthread outThere([&wow]() {
+ std::this_thread::sleep_for(50ms);
+ wow.store(102);
+ wow.notify_all();
+ });
+
+ auto res = wow.wait_for(34, 5s);
+ VERIFY(res);
+ VERIFY(*res == 102);
+ VERIFY(50ms <= t.duration());
+ VERIFY(t.duration() < 70ms);
+ }
+
+ // "WaitFor: Will not return if value is changed to same value.
+ {
+ std::atomic<T> wow = 34;
+ const Timer t;
+
+ std::jthread outThere([&wow]() {
+ for (int i = 0; i < 2; i++)
+ {
+ std::this_thread::sleep_for(20ms);
+ wow.store(34);
+ wow.notify_all();
+ }
+ });
+
+ auto res = wow.wait_for(34, 50ms);
+ auto taken = t.duration();
+ VERIFY(!res);
+ VERIFY(50ms <= taken);
+ VERIFY(taken < 100ms);
+ }
+
+ // "WaitUntil: Will return right away if the value is the same.
+ {
+ std::atomic<T> wow = 34;
+ const auto deadLine = std::chrono::system_clock::now() + 100ms;
+ auto res = wow.wait_until(102, deadLine);
+ const auto finished = std::chrono::system_clock::now();
+ VERIFY(res);
+ VERIFY(*res == 34);
+ VERIFY(finished <= deadLine);
+ }
+
+ // "WaitUntil: Will not return before deadline, if not hit
+ {
+ std::atomic<T> wow = 34;
+ const auto deadLine = std::chrono::system_clock::now() + 100ms;
+ auto res = wow.wait_until(34, deadLine);
+ const auto finished = std::chrono::system_clock::now();
+ VERIFY(!res);
+ VERIFY(deadLine <= finished);
+ VERIFY(finished < deadLine + 40ms);
+ }
+
+ // "WaitUntil: Will return if changed, if not hit
+ {
+ std::atomic<T> wow = 34;
+ const auto deadLine = std::chrono::system_clock::now() + 100ms;
+
+ std::jthread outThere([&wow]() {
+ std::this_thread::sleep_for(20ms);
+ wow.store(102);
+ wow.notify_all();
+ });
+ auto res = wow.wait_until(34, deadLine);
+ const auto finished = std::chrono::system_clock::now();
+ VERIFY(res);
+ VERIFY(*res == 102);
+ VERIFY(finished < deadLine);
+ }
+
+ // "WaitUntil: Will not return if not changed, if not hit
+ {
+ std::atomic<T> wow = 34;
+ const auto deadline = std::chrono::system_clock::now() + 100ms;
+
+ std::jthread outThere([&wow]() {
+ for (int i = 0; i < 2; i++)
+ {
+ std::this_thread::sleep_for(20ms);
+ wow.store(34);
+ wow.notify_all();
+ }
+ });
+
+ auto res = wow.wait_until(34, deadline);
+ const auto finished = std::chrono::system_clock::now();
+ VERIFY(!res);
+ VERIFY(deadline <= finished);
+ VERIFY(finished < deadline + 40ms);
+ }
+
+ // Predicate api
+
+
+ // Wait predicate should return immidiatly when it has the right value
+ {
+ std::atomic<T> wow = 34;
+ const Timer t;
+ const auto next
+ = wow.wait_with_predicate([](const auto in) { return in < 40; });
+ VERIFY(next == 34);
+ VERIFY(t.duration() < 1ms);
+ }
+
+ // "Wait predicate should wait until notified atleast
+ {
+ std::atomic<T> wow = 34;
+ const Timer t;
+ std::jthread outThere([&wow]() {
+ std::this_thread::sleep_for(50ms);
+ wow.store(102, std::memory_order::release);
+ wow.notify_all();
+ });
+
+ auto res = wow.wait_with_predicate([](const auto in) { return 100 < in; });
+ VERIFY(res == 102);
+ VERIFY(50ms <= t.duration());
+ VERIFY(t.duration() < 60ms);
+ }
+
+ // "Wait predicate should not return on unchanged value.
+ {
+ std::atomic<T> wow = 34;
+
+ const Timer t;
+ std::jthread outThere([&wow]() {
+ std::this_thread::sleep_for(20ms);
+ wow.store(34);
+ wow.notify_all();
+ std::this_thread::sleep_for(40ms);
+ wow.store(102);
+ wow.notify_all();
+ });
+
+ auto res = wow.wait_with_predicate([](const auto in) { return in != 34; });
+ const auto dur = t.duration();
+ VERIFY(res == 102);
+ VERIFY(60ms <= dur);
+ VERIFY(dur < 70ms);
+ }
+
+ // "try wait predicate should return right away when it's correct
+ {
+ std::atomic<T> wow = 34;
+ const Timer t;
+ auto res
+ = wow.try_wait_with_predicate([](const auto in) { return in != 100; });
+ VERIFY(res);
+ VERIFY(*res == 34);
+ VERIFY(t.duration() < 1ms);
+ }
+
+ // "try wait predicate should return after a little while when it's not correct
+ {
+ std::atomic<T> wow = 34;
+ const Timer t;
+ auto res
+ = wow.try_wait_with_predicate([](const auto in) { return in != 34; });
+ VERIFY(!res);
+ VERIFY(t.duration() < 100ms);
+ }
+
+ // WaitFor predicate : No waiting if it's good
+ {
+ std::atomic<T> wow = 34;
+ const Timer t;
+ auto res = wow.wait_for_with_predicate(
+ [](const auto in) { return in != 100; }, 10s);
+ VERIFY(res);
+ VERIFY(*res == 34);
+ VERIFY(t.duration() < 1ms);
+ }
+
+ // WaitFor predicate : Max waiting if it doesn't change
+ {
+ std::atomic<T> wow = 34;
+ const Timer t;
+ auto res = wow.wait_for_with_predicate(
+ [](const auto in) { return 50 < in; }, 50ms);
+ VERIFY(!res);
+ VERIFY(t.duration() < 60ms);
+ VERIFY(50ms <= t.duration());
+ }
+
+ // WaitFor predicate : Will wakeup if changed.
+ {
+ std::atomic<T> wow = 34;
+ const Timer t;
+
+ std::jthread outThere([&wow]() {
+ std::this_thread::sleep_for(50ms);
+ wow.store(102);
+ wow.notify_all();
+ });
+
+ auto res = wow.wait_for_with_predicate(
+ [](const auto in) { return 100 < in; }, 5s);
+ VERIFY(res);
+ VERIFY(*res == 102);
+ VERIFY(50ms <= t.duration());
+ VERIFY(t.duration() < 70ms);
+ }
+
+ // WaitFor predicate : Will not return if value is changed to same value.
+ {
+ std::atomic<T> wow = 34;
+ const Timer t;
+
+ std::jthread outThere([&wow]() {
+ for (int i = 0; i < 2; i++)
+ {
+ std::this_thread::sleep_for(20ms);
+ wow.store(34);
+ wow.notify_all();
+ }
+ });
+
+ auto res = wow.wait_for_with_predicate(
+ [](const auto in) { return in < 30; }, 50ms);
+ auto taken = t.duration();
+ VERIFY(!res);
+ VERIFY(50ms <= taken);
+ VERIFY(taken < 100ms);
+ }
+
+ // WaitUntil predicate : Will return right away if the value is the same.
+ {
+ std::atomic<T> wow = 34;
+ const auto deadLine = std::chrono::system_clock::now() + 100ms;
+ auto res = wow.wait_until_with_predicate(
+ [](const auto in) { return in != 102; }, deadLine);
+ const auto finished = std::chrono::system_clock::now();
+ VERIFY(res);
+ VERIFY(*res == 34);
+ VERIFY(finished <= deadLine);
+ }
+
+ // WaitUntil predicate : Will not return before deadline, if not hit
+ {
+ std::atomic<T> wow = 34;
+ const auto deadLine = std::chrono::system_clock::now() + 100ms;
+ auto res = wow.wait_until_with_predicate([](const auto in)
+ {
+ return 40 < in;
+ }, deadLine);
+ const auto finished = std::chrono::system_clock::now();
+ VERIFY(!res);
+ VERIFY(deadLine <= finished);
+ VERIFY(finished < deadLine + 40ms);
+ }
+
+ // WaitUntil predicate: Will return if changed, if not hit
+ {
+ std::atomic<T> wow = 34;
+ const auto deadLine = std::chrono::system_clock::now() + 100ms;
+
+ std::jthread outThere([&wow]() {
+ std::this_thread::sleep_for(20ms);
+ wow.store(102);
+ wow.notify_all();
+ });
+
+ auto res = wow.wait_until_with_predicate([](const auto in)
+ {
+ return 100 < in;
+ }, deadLine);
+ const auto finished = std::chrono::system_clock::now();
+ VERIFY(res);
+ VERIFY(*res == 102);
+ VERIFY(finished < deadLine);
+ }
+
+ // WaitUntil predicate: Will not return if not changed, if not hit
+ {
+ std::atomic<T> wow = 34;
+ const auto deadline = std::chrono::system_clock::now() + 100ms;
+
+ std::jthread outThere([&wow]() {
+ for (int i = 0; i < 2; i++)
+ {
+ std::this_thread::sleep_for(20ms);
+ wow.store(34);
+ wow.notify_all();
+ }
+ });
+
+ auto res = wow.wait_until_with_predicate([](const auto in)
+ {
+ return 40 < in;
+ }, deadline);
+ const auto finished = std::chrono::system_clock::now();
+ VERIFY(!res);
+ VERIFY(deadline <= finished);
+ VERIFY(finished < deadline + ms_buffer);
+ }
+
+}
+
+
+int
+main ()
+{
+ // check<bool> bb;
+ check<char>();
+ check<signed char>();
+ check<unsigned char>();
+ check<short>();
+ check<unsigned short>();
+ check<int>();
+ check<unsigned int>();
+ check<long>();
+ check<unsigned long>();
+ check<long long>();
+ check<unsigned long long>();
+
+ check<wchar_t>();
+ check<char8_t>();
+ check<char16_t>();
+ check<char32_t>();
+
+ check<int8_t>();
+ check<int16_t>();
+ check<int32_t>();
+ check<int64_t>();
+
+ check<uint8_t>();
+ check<uint16_t>();
+ check<uint32_t>();
+ check<uint64_t>();
+ return 0;
+}
--
2.44.0
[-- Attachment #3: 0003-Add-atomic-wait-for-floating-points.patch --]
[-- Type: text/x-patch, Size: 4955 bytes --]
From 81e2424d093148d7ba05a471d39b51e5f390aafb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Teodor=20Sp=C3=A6ren?= <teodor@sparen.no>
Date: Wed, 10 Apr 2024 17:45:11 +0200
Subject: [PATCH 3/4] Add atomic wait for floating points.
---
libstdc++-v3/include/bits/atomic_base.h | 107 ++++++++++++++++++++++++
1 file changed, 107 insertions(+)
diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h
index 8a2ed65eaf2..50037a84ed2 100644
--- a/libstdc++-v3/include/bits/atomic_base.h
+++ b/libstdc++-v3/include/bits/atomic_base.h
@@ -1656,6 +1656,113 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
{ __atomic_impl::notify_all(&_M_fp); }
// TODO add const volatile overload
+
+ _GLIBCXX_ALWAYS_INLINE _Fp
+ wait_value(_Fp __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 <class __Pred> requires std::predicate<__Pred, _Fp>
+ _GLIBCXX_ALWAYS_INLINE _Fp
+ wait_with_predicate(__Pred &&__stop_waiting,
+ memory_order __m = memory_order_seq_cst) const {
+ _Fp __ret;
+ std::__atomic_wait_address(&_M_fp, [__m, this, &__stop_waiting, &__ret] {
+ __ret = this->load(__m);
+ return std::invoke(__stop_waiting, __ret);
+ });
+ return __ret;
+ }
+
+ template <class __Rep, class __Period>
+ _GLIBCXX_ALWAYS_INLINE std::optional<_Fp>
+ wait_for(_Fp __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](_Fp __cur) {
+ // TODO(rHermes): consider doing atomic comparisons here?
+ return __old != __cur;
+ }, __dur, __m);
+ }
+
+ template <class __Clock, class __Duration>
+ _GLIBCXX_ALWAYS_INLINE std::optional<_Fp>
+ wait_until(_Fp __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<_Fp>
+ try_wait(_Fp __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 <class __Pred, class __Rep, class __Period>
+ requires std::predicate<__Pred, _Fp>
+ _GLIBCXX_ALWAYS_INLINE std::optional<_Fp> wait_for_with_predicate(
+ __Pred &&__stop_waiting,
+ const std::chrono::duration<__Rep, __Period> &__dur,
+ memory_order __m = memory_order_seq_cst) const {
+ _Fp __ret;
+ auto __succeeded = std::__atomic_wait_address_for(
+ &_M_fp,
+ [__m, this, &__stop_waiting, &__ret] {
+ __ret = this->load(__m);
+ return std::invoke(__stop_waiting, __ret);
+ },
+ __dur);
+
+ if (!__succeeded)
+ return std::nullopt;
+
+ return __ret;
+ }
+
+ template <class __Pred>
+ requires std::predicate<__Pred, _Fp>
+ _GLIBCXX_ALWAYS_INLINE std::optional<_Fp>
+ 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 <class __Pred, class __Clock, class __Duration>
+ requires std::predicate<__Pred, _Fp>
+ _GLIBCXX_ALWAYS_INLINE std::optional<_Fp>
+ wait_until_with_predicate(
+ __Pred &&__stop_waiting,
+ const std::chrono::time_point<__Clock, __Duration> &__deadline,
+ memory_order __m = memory_order_seq_cst) const {
+ _Fp __ret;
+ auto __succeeded = std::__atomic_wait_address_until(
+ &_M_fp,
+ [__m, this, &__stop_waiting, &__ret] {
+ __ret = this->load(__m);
+ return std::invoke(__stop_waiting, __ret);
+ },
+ __deadline);
+
+ if (!__succeeded)
+ return std::nullopt;
+
+ return __ret;
+ }
+
#endif // __glibcxx_atomic_wait
value_type
--
2.44.0
[-- Attachment #4: 0002-Add-atomic-waits-to-atomic_flag.patch --]
[-- Type: text/x-patch, Size: 6187 bytes --]
From 184b9839440e63c4074634668fdea1809c6a29a9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Teodor=20Sp=C3=A6ren?= <teodor@sparen.no>
Date: Wed, 10 Apr 2024 12:11:50 +0200
Subject: [PATCH 2/4] Add atomic waits to atomic_flag
---
libstdc++-v3/include/bits/atomic_base.h | 111 +++++++++++++++++++++---
1 file changed, 100 insertions(+), 11 deletions(-)
diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h
index 3e12cc917b5..8a2ed65eaf2 100644
--- a/libstdc++-v3/include/bits/atomic_base.h
+++ b/libstdc++-v3/include/bits/atomic_base.h
@@ -280,16 +280,104 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
notify_all() noexcept
{ std::__atomic_notify_address(&_M_i, true); }
- _GLIBCXX_ALWAYS_INLINE bool
- wait_value(bool __old,
+ template <class __Pred> requires std::predicate<__Pred, bool>
+ _GLIBCXX_ALWAYS_INLINE bool
+ wait_with_predicate(__Pred &&__stop_waiting,
+ memory_order __m = memory_order_seq_cst) const {
+ bool __ret;
+ std::__atomic_wait_address(&_M_i, [__m, this, &__stop_waiting, &__ret] {
+ __ret = this->test(__m);
+ return std::invoke(__stop_waiting, __ret);
+ });
+ return __ret;
+ }
+
+ template <class __Rep, class __Period>
+ _GLIBCXX_ALWAYS_INLINE std::optional<bool>
+ wait_for(bool __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](const auto __cur) {
+ // TODO(rHermes): consider doing atomic comparisons here?
+ return __old != __cur;
+ }, __dur, __m);
+ }
+
+ template <class __Clock, class __Duration>
+ _GLIBCXX_ALWAYS_INLINE std::optional<bool>
+ wait_until(bool __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<bool>
+ try_wait(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));
+ // 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 <class __Pred, class __Rep, class __Period>
+ requires std::predicate<__Pred, bool>
+ _GLIBCXX_ALWAYS_INLINE std::optional<bool> wait_for_with_predicate(
+ __Pred &&__stop_waiting,
+ const std::chrono::duration<__Rep, __Period> &__dur,
+ memory_order __m = memory_order_seq_cst) const {
+ bool __ret;
+ auto __succeeded = std::__atomic_wait_address_for(
+ &_M_i,
+ [__m, this, &__stop_waiting, &__ret] {
+ __ret = this->test(__m);
+ return std::invoke(__stop_waiting, __ret);
+ },
+ __dur);
+
+ if (!__succeeded)
+ return std::nullopt;
+
return __ret;
- });
- return __ret;
- }
+ }
+
+ template <class __Pred>
+ requires std::predicate<__Pred, bool>
+ _GLIBCXX_ALWAYS_INLINE std::optional<bool>
+ try_wait_with_predicate(__Pred &&__stop_waiting,
+ memory_order __m = memory_order_seq_cst) const {
+
+ // TODO(rHermes): This needs to be changed out with an actual good value.
+ return wait_for_with_predicate(std::forward<__Pred>(__stop_waiting),
+ std::chrono::milliseconds(10), __m);
+ }
+
+ template <class __Pred, class __Clock, class __Duration>
+ requires std::predicate<__Pred, bool>
+ _GLIBCXX_ALWAYS_INLINE std::optional<bool>
+ wait_until_with_predicate(
+ __Pred &&__stop_waiting,
+ const std::chrono::time_point<__Clock, __Duration> &__deadline,
+ memory_order __m = memory_order_seq_cst) const {
+ bool __ret;
+ auto __succeeded = std::__atomic_wait_address_until(
+ &_M_i,
+ [__m, this, &__stop_waiting, &__ret] {
+ __ret = this->test(__m);
+ return std::invoke(__stop_waiting, __ret);
+ },
+ __deadline);
+
+ if (!__succeeded)
+ return std::nullopt;
+
+ return __ret;
+ }
// TODO add const volatile overload
#endif // __glibcxx_atomic_wait
@@ -694,7 +782,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
template <class __Pred, class __Rep, class __Period>
- requires std::predicate<__Pred, __int_type>
+ 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,
@@ -714,7 +802,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
return __ret;
}
- template <class __Pred> requires std::predicate<__Pred, __int_type>
+ template <class __Pred>
+ 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 {
@@ -723,7 +812,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
template <class __Pred, class __Clock, class __Duration>
- requires std::predicate<__Pred, __int_type>
+ requires std::predicate<__Pred, __int_type>
_GLIBCXX_ALWAYS_INLINE std::optional<__int_type>
wait_until_with_predicate(
__Pred &&__stop_waiting,
--
2.44.0
[-- Attachment #5: 0001-Add-atomic-options-for-integers.patch --]
[-- Type: text/x-patch, Size: 7508 bytes --]
From 74bc9f06e3de0089a1b726b4f0a0e0fc1725c5e9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Teodor=20Sp=C3=A6ren?= <teodor@sparen.no>
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 <bits/atomic_wait.h>
+
+#include <bits/atomic_timed_wait.h>
+#include <concepts>
+#include <functional>
+#include <optional>
#endif
#ifndef _GLIBCXX_ALWAYS_INLINE
@@ -48,6 +53,7 @@
#include <bits/version.h>
+
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 <class __Pred> 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 <class __Rep, class __Period>
+ _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 <class __Clock, class __Duration>
+ _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 <class __Pred, class __Rep, class __Period>
+ 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 <class __Pred> 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 <class __Pred, class __Clock, class __Duration>
+ 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
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2024-04-14 20:15 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-04-14 20:15 [PATCH] Want feedback for potential implementation of P2643R2 atomic features Teodor Spæren
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).