[libcxx-commits] [libcxx] [libc++] Allow internal functions from atomic_sync.h to be used in C++17 (PR #224356)
via libcxx-commits
libcxx-commits at lists.llvm.org
Sun Sep 20 10:57:56 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-libcxx
Author: Michael G. Kazakov (mikekazakov)
<details>
<summary>Changes</summary>
Some Parallel STL algorithms, e.g. a single-pass scan, need a functionality for cheap waiting on address, which is publicly available only in C++20 mode.
This patch allows the internal atomic waiting functions to be used in C++17 mode:
- The header `atomic_sync.h` is currently gated behind `_LIBCPP_STD_VER >= 20`, but nothing inherently stops from exposing these internal functions in an earlier C++ standard.
- The official part defined by the Standard (`std::atomic::wait()` etc) remains gated by `C++20`.
- A few concepts in `atomic_waitable_traits.h` (`__atomic_waitable` and `__has_native_atomic_wait`) can become an `constexpr inline` boolean template in `C++17` mode, while remaining a concept in `C++20` and above.
---
Full diff: https://github.com/llvm/llvm-project/pull/224356.diff
4 Files Affected:
- (modified) libcxx/include/__atomic/atomic.h (+5-3)
- (modified) libcxx/include/__atomic/atomic_sync.h (+2-2)
- (modified) libcxx/include/__atomic/atomic_waitable_traits.h (+30-2)
- (added) libcxx/test/libcxx/atomics/atomics.syn/wait_on_cpp17.verify.cpp (+20)
``````````diff
diff --git a/libcxx/include/__atomic/atomic.h b/libcxx/include/__atomic/atomic.h
index c57b60fc2b4bb8..5099dea232c150 100644
--- a/libcxx/include/__atomic/atomic.h
+++ b/libcxx/include/__atomic/atomic.h
@@ -217,7 +217,7 @@ struct __atomic_base<_Tp, true> : public __atomic_base<_Tp, false> {
_LIBCPP_HIDE_FROM_ABI _Tp operator^=(_Tp __op) _NOEXCEPT { return fetch_xor(__op) ^ __op; }
};
-#if _LIBCPP_STD_VER >= 20
+#if _LIBCPP_STD_VER >= 17
// Here we need _IsIntegral because the default template argument is not enough
// e.g __atomic_base<int> is __atomic_base<int, true>, which inherits from
// __atomic_base<int, false> and the caller of the wait function is
@@ -246,7 +246,7 @@ struct __atomic_waitable_traits<__atomic_base<_Tp, _IsIntegral> > {
}
};
-#endif // _LIBCPP_STD_VER >= 20
+#endif // _LIBCPP_STD_VER >= 17
template <typename _Tp>
struct __check_atomic_mandates {
@@ -365,10 +365,12 @@ struct atomic<_Tp*> : public __atomic_base<_Tp*> {
atomic& operator=(const atomic&) volatile = delete;
};
-#if _LIBCPP_STD_VER >= 20
+#if _LIBCPP_STD_VER >= 17
template <class _Tp>
struct __atomic_waitable_traits<atomic<_Tp> > : __atomic_waitable_traits<__atomic_base<_Tp> > {};
+#endif // _LIBCPP_STD_VER >= 17
+#if _LIBCPP_STD_VER >= 20
template <class _Tp>
requires is_floating_point_v<_Tp>
struct atomic<_Tp> : __atomic_base<_Tp> {
diff --git a/libcxx/include/__atomic/atomic_sync.h b/libcxx/include/__atomic/atomic_sync.h
index 1aa6ffa21a8f71..16e8adb709e7bf 100644
--- a/libcxx/include/__atomic/atomic_sync.h
+++ b/libcxx/include/__atomic/atomic_sync.h
@@ -23,7 +23,7 @@
# pragma GCC system_header
#endif
-#if _LIBCPP_STD_VER >= 20
+#if _LIBCPP_STD_VER >= 17
_LIBCPP_BEGIN_NAMESPACE_STD
@@ -261,6 +261,6 @@ _LIBCPP_HIDE_FROM_ABI void __atomic_wait(_AtomicWaitable& __a, _Tp __val, memory
_LIBCPP_END_NAMESPACE_STD
-#endif // _LIBCPP_STD_VER >= 20
+#endif // _LIBCPP_STD_VER >= 17
#endif // _LIBCPP___ATOMIC_ATOMIC_SYNC_H
diff --git a/libcxx/include/__atomic/atomic_waitable_traits.h b/libcxx/include/__atomic/atomic_waitable_traits.h
index ed26a60df982dd..c671a30720f94a 100644
--- a/libcxx/include/__atomic/atomic_waitable_traits.h
+++ b/libcxx/include/__atomic/atomic_waitable_traits.h
@@ -16,13 +16,15 @@
#include <__type_traits/has_unique_object_representation.h>
#include <__type_traits/is_same.h>
#include <__type_traits/is_trivially_copyable.h>
+#include <__type_traits/void_t.h>
+#include <__utility/declval.h>
#include <cstring>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif
-#if _LIBCPP_STD_VER >= 20
+#if _LIBCPP_STD_VER >= 17
_LIBCPP_BEGIN_NAMESPACE_STD
@@ -42,12 +44,26 @@ struct __atomic_waitable_traits {
static void __atomic_contention_address(_AtomicWaitable&&) = delete;
};
+# if _LIBCPP_STD_VER >= 20
template <class _Tp>
concept __atomic_waitable = requires(const _Tp __t, memory_order __order) {
typename __atomic_waitable_traits<__decay_t<_Tp> >::__value_type;
{ __atomic_waitable_traits<__decay_t<_Tp> >::__atomic_load(__t, __order) };
{ __atomic_waitable_traits<__decay_t<_Tp> >::__atomic_contention_address(__t) };
};
+# else
+template <class _Tp, class = void>
+struct __atomic_waitable_impl : false_type {};
+template <class _Tp>
+struct __atomic_waitable_impl< _Tp,
+ __void_t<typename __atomic_waitable_traits<__decay_t<_Tp> >::__value_type,
+ decltype(__atomic_waitable_traits<__decay_t<_Tp> >::__atomic_load(
+ std::declval<const _Tp&>(), std::declval<memory_order>())),
+ decltype(__atomic_waitable_traits<__decay_t<_Tp> >::__atomic_contention_address(
+ std::declval<const _Tp&>())) > > : true_type {};
+template <class _Tp>
+constexpr inline bool __atomic_waitable = __atomic_waitable_impl<_Tp>::value;
+# endif // _LIBCPP_STD_VER >= 20
# ifdef __linux__
# define _LIBCPP_NATIVE_PLATFORM_WAIT_SIZES(_APPLY) _APPLY(4)
@@ -82,20 +98,32 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __has_native_atomic_wait_impl() {
};
}
+# if _LIBCPP_STD_VER >= 20
template <class _Tp>
concept __has_native_atomic_wait =
has_unique_object_representations_v<_Tp> && is_trivially_copyable_v<_Tp> &&
std::__has_native_atomic_wait_impl<_Tp>();
+# else
+template <class _Tp>
+constexpr inline bool __has_native_atomic_wait =
+ has_unique_object_representations_v<_Tp> && is_trivially_copyable_v<_Tp> &&
+ std::__has_native_atomic_wait_impl<_Tp>();
+# endif // _LIBCPP_STD_VER >= 20
# else // _LIBCPP_ABI_ATOMIC_WAIT_NATIVE_BY_SIZE
+# if _LIBCPP_STD_VER >= 20
template <class _Tp>
concept __has_native_atomic_wait = is_same_v<_Tp, __cxx_contention_t>;
+# else
+template <class _Tp>
+constexpr inline bool __has_native_atomic_wait = is_same_v<_Tp, __cxx_contention_t>;
+# endif // _LIBCPP_STD_VER >= 20
# endif // _LIBCPP_ABI_ATOMIC_WAIT_NATIVE_BY_SIZE
_LIBCPP_END_NAMESPACE_STD
-#endif // _LIBCPP_STD_VER >= 20
+#endif // _LIBCPP_STD_VER >= 17
#endif // _LIBCPP___ATOMIC_ATOMIC_WAITABLE_TRAITS_H
diff --git a/libcxx/test/libcxx/atomics/atomics.syn/wait_on_cpp17.verify.cpp b/libcxx/test/libcxx/atomics/atomics.syn/wait_on_cpp17.verify.cpp
new file mode 100644
index 00000000000000..ef754869c4eb55
--- /dev/null
+++ b/libcxx/test/libcxx/atomics/atomics.syn/wait_on_cpp17.verify.cpp
@@ -0,0 +1,20 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++17
+// UNSUPPORTED: no-threads
+
+#include <atomic>
+
+// This compile-only test checks that __atomic_wait, __atomic_notify_one, and __atomic_notify_all are available in C++17
+
+void test_atomic_wait(std::atomic<int>& val) {
+ std::__atomic_wait(val, 0, std::memory_order_seq_cst); // expected-no-diagnostics
+ std::__atomic_notify_one(val); // expected-no-diagnostics
+ std::__atomic_notify_all(val); // expected-no-diagnostics
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/224356
More information about the libcxx-commits
mailing list