[libcxx-commits] [libcxx] [libc++] P0718R2: Implementation of std::atomic<shared_ptr<T>> and std::atomic<weak_ptr<T>> (PR #194215)
Vladislav Semykin via libcxx-commits
libcxx-commits at lists.llvm.org
Sat May 30 08:40:53 PDT 2026
https://github.com/ViNN280801 updated https://github.com/llvm/llvm-project/pull/194215
>From 368b9e95212e00b93e291c7a6bd02d27c6f1ce42 Mon Sep 17 00:00:00 2001
From: ViNN280801 <vladislav.semykin at gmail.com>
Date: Sun, 26 Apr 2026 10:03:26 +0300
Subject: [PATCH 01/14] [libcxx] Implementation of non lock-free atomic
shared_ptr
Signed-off-by: ViNN280801 <vladislav.semykin at gmail.com>
---
libcxx/include/CMakeLists.txt | 2 +
libcxx/include/__atomic/atomic_sync_lite.h | 45 ++
libcxx/include/__memory/atomic_shared_ptr.h | 491 ++++++++++++++++++
libcxx/include/__memory/shared_ptr.h | 22 +-
libcxx/include/module.modulemap.in | 5 +
.../atomic_shared_ptr_aliasing.pass.cpp | 71 +++
.../atomic_shared_ptr_class.pass.cpp | 70 +++
.../atomic_shared_ptr_memory_order.verify.cpp | 58 +++
.../atomic_shared_ptr_nullptr.pass.cpp | 54 ++
.../atomic_shared_ptr_refcount.pass.cpp | 105 ++++
.../atomic_shared_ptr_stress.pass.cpp | 107 ++++
.../atomic_weak_ptr_class.pass.cpp | 91 ++++
12 files changed, 1119 insertions(+), 2 deletions(-)
create mode 100644 libcxx/include/__atomic/atomic_sync_lite.h
create mode 100644 libcxx/include/__memory/atomic_shared_ptr.h
create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_aliasing.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_class.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_memory_order.verify.cpp
create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_nullptr.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_refcount.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_stress.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_weak_ptr_class.pass.cpp
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 5e9040a62dd53..48968128e47bd 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -216,6 +216,7 @@ set(files
__atomic/atomic_lock_free.h
__atomic/atomic_ref.h
__atomic/atomic_sync.h
+ __atomic/atomic_sync_lite.h
__atomic/atomic_sync_timed.h
__atomic/atomic_waitable_traits.h
__atomic/check_memory_order.h
@@ -596,6 +597,7 @@ set(files
__memory/allocator_traits.h
__memory/array_cookie.h
__memory/assume_aligned.h
+ __memory/atomic_shared_ptr.h
__memory/auto_ptr.h
__memory/compressed_pair.h
__memory/concepts.h
diff --git a/libcxx/include/__atomic/atomic_sync_lite.h b/libcxx/include/__atomic/atomic_sync_lite.h
new file mode 100644
index 0000000000000..a0433e16b506a
--- /dev/null
+++ b/libcxx/include/__atomic/atomic_sync_lite.h
@@ -0,0 +1,45 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___ATOMIC_ATOMIC_SYNC_LITE_H
+#define _LIBCPP___ATOMIC_ATOMIC_SYNC_LITE_H
+
+#include <__atomic/contention_t.h>
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20 && _LIBCPP_HAS_THREADS
+
+# if !_LIBCPP_AVAILABILITY_HAS_NEW_SYNC
+// Old dylib interface kept for backwards compatibility.
+_LIBCPP_EXPORTED_FROM_ABI void __cxx_atomic_notify_one(void const volatile*) _NOEXCEPT;
+_LIBCPP_EXPORTED_FROM_ABI void __cxx_atomic_notify_all(void const volatile*) _NOEXCEPT;
+_LIBCPP_EXPORTED_FROM_ABI __cxx_contention_t __libcpp_atomic_monitor(void const volatile*) _NOEXCEPT;
+_LIBCPP_EXPORTED_FROM_ABI void __libcpp_atomic_wait(void const volatile*, __cxx_contention_t) _NOEXCEPT;
+# endif // !_LIBCPP_AVAILABILITY_HAS_NEW_SYNC
+
+// New dylib interface.
+_LIBCPP_AVAILABILITY_NEW_SYNC _LIBCPP_EXPORTED_FROM_ABI __cxx_contention_t
+__atomic_monitor_global(void const* __address) _NOEXCEPT;
+
+_LIBCPP_AVAILABILITY_NEW_SYNC _LIBCPP_EXPORTED_FROM_ABI void
+__atomic_wait_global_table(void const* __address, __cxx_contention_t __monitor_value) _NOEXCEPT;
+
+_LIBCPP_AVAILABILITY_NEW_SYNC _LIBCPP_EXPORTED_FROM_ABI void __atomic_notify_one_global_table(void const*) _NOEXCEPT;
+_LIBCPP_AVAILABILITY_NEW_SYNC _LIBCPP_EXPORTED_FROM_ABI void __atomic_notify_all_global_table(void const*) _NOEXCEPT;
+
+#endif // _LIBCPP_STD_VER >= 20 && _LIBCPP_HAS_THREADS
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___ATOMIC_ATOMIC_SYNC_LITE_H
diff --git a/libcxx/include/__memory/atomic_shared_ptr.h b/libcxx/include/__memory/atomic_shared_ptr.h
new file mode 100644
index 0000000000000..000b56e08bae6
--- /dev/null
+++ b/libcxx/include/__memory/atomic_shared_ptr.h
@@ -0,0 +1,491 @@
+// -*- 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___MEMORY_ATOMIC_SHARED_PTR_H
+#define _LIBCPP___MEMORY_ATOMIC_SHARED_PTR_H
+
+#include <__atomic/atomic_sync_lite.h>
+#include <__atomic/check_memory_order.h>
+#include <__atomic/memory_order.h>
+#include <__atomic/support.h>
+#include <__config>
+#include <__cstddef/nullptr_t.h>
+#include <__memory/shared_count.h>
+#include <__utility/move.h>
+
+#include <cstdint>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+#if defined(__SANITIZE_THREAD__) || (defined(__has_feature) && __has_feature(thread_sanitizer))
+# if __has_include(<sanitizer/tsan_interface.h>)
+# include <sanitizer/tsan_interface.h>
+# define _LIBCPP_ATOMIC_SHARED_PTR_TSAN 1
+# endif
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+// TSAN annotations model the lock-bit protocol on __ctrl_.
+#if defined(_LIBCPP_ATOMIC_SHARED_PTR_TSAN)
+# define _LIBCPP_ATOMIC_SP_TSAN_PRE_LOCK(addr) \
+ ::__tsan_mutex_pre_lock(reinterpret_cast<void*>(const_cast<__cxx_atomic_impl<uintptr_t>*>(addr)), 0)
+# define _LIBCPP_ATOMIC_SP_TSAN_POST_LOCK(addr) \
+ ::__tsan_mutex_post_lock(reinterpret_cast<void*>(const_cast<__cxx_atomic_impl<uintptr_t>*>(addr)), 0, 0)
+# define _LIBCPP_ATOMIC_SP_TSAN_PRE_UNLOCK(addr) \
+ ::__tsan_mutex_pre_unlock(reinterpret_cast<void*>(const_cast<__cxx_atomic_impl<uintptr_t>*>(addr)), 0)
+# define _LIBCPP_ATOMIC_SP_TSAN_POST_UNLOCK(addr) \
+ ::__tsan_mutex_post_unlock(reinterpret_cast<void*>(const_cast<__cxx_atomic_impl<uintptr_t>*>(addr)), 0)
+#else
+# define _LIBCPP_ATOMIC_SP_TSAN_PRE_LOCK(addr) ((void)(addr))
+# define _LIBCPP_ATOMIC_SP_TSAN_POST_LOCK(addr) ((void)(addr))
+# define _LIBCPP_ATOMIC_SP_TSAN_PRE_UNLOCK(addr) ((void)(addr))
+# define _LIBCPP_ATOMIC_SP_TSAN_POST_UNLOCK(addr) ((void)(addr))
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20 && _LIBCPP_HAS_THREADS && _LIBCPP_HAS_ATOMIC_HEADER
+
+template <class _Tp>
+class shared_ptr;
+
+template <class _Tp>
+class weak_ptr;
+
+template <class _Tp>
+struct atomic;
+
+// Split state into pointer word and control word.
+// The control word stores control-block pointer plus lock/notify bits.
+struct __atomic_smart_ptr_storage {
+ static constexpr uintptr_t __lock_bit_ = uintptr_t{1};
+ static constexpr uintptr_t __notify_bit_ = uintptr_t{2};
+ static constexpr uintptr_t __ptr_mask_ = ~(__lock_bit_ | __notify_bit_);
+
+ _LIBCPP_HIDE_FROM_ABI static uintptr_t __encode(__shared_weak_count* __ctrl, uintptr_t __bits) _NOEXCEPT {
+ return (reinterpret_cast<uintptr_t>(__ctrl) & __ptr_mask_) | (__bits & ~__ptr_mask_);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI static __shared_weak_count* __decode(uintptr_t __word) _NOEXCEPT {
+ return reinterpret_cast<__shared_weak_count*>(__word & __ptr_mask_);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI static bool __has_lock(uintptr_t __word) _NOEXCEPT { return (__word & __lock_bit_) != 0; }
+ _LIBCPP_HIDE_FROM_ABI static bool __has_notify(uintptr_t __word) _NOEXCEPT { return (__word & __notify_bit_) != 0; }
+};
+
+_LIBCPP_HIDE_FROM_ABI inline void __atomic_smart_ptr_notify_one(const void* __address) _NOEXCEPT {
+# if _LIBCPP_AVAILABILITY_HAS_NEW_SYNC
+ std::__atomic_notify_one_global_table(__address);
+# else
+ std::__cxx_atomic_notify_one(reinterpret_cast<void const volatile*>(__address));
+# endif
+}
+
+_LIBCPP_HIDE_FROM_ABI inline void __atomic_smart_ptr_notify_all(const void* __address) _NOEXCEPT {
+# if _LIBCPP_AVAILABILITY_HAS_NEW_SYNC
+ std::__atomic_notify_all_global_table(__address);
+# else
+ std::__cxx_atomic_notify_all(reinterpret_cast<void const volatile*>(__address));
+# endif
+}
+
+template <class _Poll>
+_LIBCPP_HIDE_FROM_ABI inline void __atomic_smart_ptr_wait_on_address(const void* __address, _Poll&& __poll) _NOEXCEPT {
+ while (!__poll()) {
+# if _LIBCPP_AVAILABILITY_HAS_NEW_SYNC
+ auto __monitor_value = std::__atomic_monitor_global(__address);
+ if (__poll())
+ return;
+ std::__atomic_wait_global_table(__address, __monitor_value);
+# else
+ void const volatile* __volatile_address = reinterpret_cast<void const volatile*>(__address);
+ auto __monitor_value = std::__libcpp_atomic_monitor(__volatile_address);
+ if (__poll())
+ return;
+ std::__libcpp_atomic_wait(__volatile_address, __monitor_value);
+# endif
+ }
+}
+
+template <class _Element>
+struct __atomic_smart_ptr_fields {
+ mutable __cxx_atomic_impl<_Element*> __ptr_;
+ mutable __cxx_atomic_impl<uintptr_t> __ctrl_;
+
+ _LIBCPP_HIDE_FROM_ABI __atomic_smart_ptr_fields(_Element* __p, __shared_weak_count* __c) _NOEXCEPT
+ : __ptr_(__p),
+ __ctrl_(__atomic_smart_ptr_storage::__encode(__c, 0)) {}
+
+ _LIBCPP_HIDE_FROM_ABI const void* __ctrl_address() const _NOEXCEPT {
+ return static_cast<const void*>(__builtin_addressof(__ctrl_));
+ }
+
+ // Acquire lock bit on __ctrl_. Contended path sets notify bit and waits.
+ _LIBCPP_HIDE_FROM_ABI void __lock() const _NOEXCEPT {
+ _LIBCPP_ATOMIC_SP_TSAN_PRE_LOCK(&__ctrl_);
+ uintptr_t __expected = std::__cxx_atomic_load(__builtin_addressof(__ctrl_), memory_order_relaxed);
+ for (;;) {
+ if (!__atomic_smart_ptr_storage::__has_lock(__expected)) {
+ uintptr_t __desired = __expected | __atomic_smart_ptr_storage::__lock_bit_;
+ if (std::__cxx_atomic_compare_exchange_weak(
+ __builtin_addressof(__ctrl_),
+ __builtin_addressof(__expected),
+ __desired,
+ memory_order_acquire,
+ memory_order_relaxed)) {
+ _LIBCPP_ATOMIC_SP_TSAN_POST_LOCK(&__ctrl_);
+ return;
+ }
+ continue;
+ }
+
+ uintptr_t __with_notify = __expected | __atomic_smart_ptr_storage::__notify_bit_;
+ if (!__atomic_smart_ptr_storage::__has_notify(__expected)) {
+ if (!std::__cxx_atomic_compare_exchange_weak(
+ __builtin_addressof(__ctrl_),
+ __builtin_addressof(__expected),
+ __with_notify,
+ memory_order_relaxed,
+ memory_order_relaxed))
+ continue;
+ __expected = __with_notify;
+ }
+
+ std::__atomic_smart_ptr_wait_on_address(__ctrl_address(), [&] {
+ __expected = std::__cxx_atomic_load(__builtin_addressof(__ctrl_), memory_order_relaxed);
+ return !__atomic_smart_ptr_storage::__has_lock(__expected);
+ });
+ }
+ }
+
+ // Publish new control pointer, clear bits, and notify waiters if needed.
+ _LIBCPP_HIDE_FROM_ABI void __unlock(__shared_weak_count* __ctrl_to_publish) const _NOEXCEPT {
+ _LIBCPP_ATOMIC_SP_TSAN_PRE_UNLOCK(&__ctrl_);
+ uintptr_t __new_word = __atomic_smart_ptr_storage::__encode(__ctrl_to_publish, 0);
+ uintptr_t __previous = std::__cxx_atomic_exchange(__builtin_addressof(__ctrl_), __new_word, memory_order_release);
+ if (__atomic_smart_ptr_storage::__has_notify(__previous))
+ std::__atomic_smart_ptr_notify_all(__ctrl_address());
+ _LIBCPP_ATOMIC_SP_TSAN_POST_UNLOCK(&__ctrl_);
+ }
+};
+
+// [util.smartptr.atomic.shared]: same stored pointer and same ownership, or both empty.
+template <class _Element>
+_LIBCPP_HIDE_FROM_ABI inline bool __atomic_smart_ptr_equivalent(
+ _Element* __ptr,
+ __shared_weak_count* __ctrl,
+ _Element* __expected_ptr,
+ __shared_weak_count* __expected_ctrl) _NOEXCEPT {
+ if (__ctrl == nullptr && __expected_ctrl == nullptr)
+ return true;
+ return __ptr == __expected_ptr && __ctrl == __expected_ctrl;
+}
+
+template <class _Tp>
+struct atomic<shared_ptr<_Tp>> {
+ using value_type = shared_ptr<_Tp>;
+
+ static constexpr bool is_always_lock_free = false;
+
+ _LIBCPP_HIDE_FROM_ABI atomic() _NOEXCEPT : __fields_(nullptr, nullptr) {}
+ _LIBCPP_HIDE_FROM_ABI constexpr atomic(nullptr_t) _NOEXCEPT : __fields_(nullptr, nullptr) {}
+ _LIBCPP_HIDE_FROM_ABI atomic(shared_ptr<_Tp> __desired) _NOEXCEPT : __fields_(__desired.__ptr_, __desired.__cntrl_) {
+ __desired.__ptr_ = nullptr;
+ __desired.__cntrl_ = nullptr;
+ }
+
+ atomic(const atomic&) = delete;
+ atomic& operator=(const atomic&) = delete;
+
+ _LIBCPP_HIDE_FROM_ABI ~atomic() {
+ if (auto* __c = __atomic_smart_ptr_storage::__decode(
+ std::__cxx_atomic_load(__builtin_addressof(__fields_.__ctrl_), memory_order_relaxed)))
+ __c->__release_shared();
+ }
+
+ _LIBCPP_HIDE_FROM_ABI bool is_lock_free() const _NOEXCEPT { return false; }
+
+ _LIBCPP_HIDE_FROM_ABI void operator=(shared_ptr<_Tp> __desired) _NOEXCEPT { store(std::move(__desired)); }
+ _LIBCPP_HIDE_FROM_ABI void operator=(nullptr_t) _NOEXCEPT { store(nullptr); }
+ _LIBCPP_HIDE_FROM_ABI operator shared_ptr<_Tp>() const _NOEXCEPT { return load(); }
+
+ _LIBCPP_HIDE_FROM_ABI void store(shared_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) _NOEXCEPT
+ _LIBCPP_CHECK_STORE_MEMORY_ORDER(__m) {
+ (void)__m;
+ _Tp* __desired_ptr = __desired.__ptr_;
+ __shared_weak_count* __desired_c = __desired.__cntrl_;
+ __desired.__ptr_ = nullptr;
+ __desired.__cntrl_ = nullptr;
+
+ __fields_.__lock();
+ __shared_weak_count* __old_c = __atomic_smart_ptr_storage::__decode(
+ std::__cxx_atomic_load(__builtin_addressof(__fields_.__ctrl_), memory_order_relaxed));
+ std::__cxx_atomic_store(__builtin_addressof(__fields_.__ptr_), __desired_ptr, memory_order_relaxed);
+ __fields_.__unlock(__desired_c);
+
+ if (__old_c)
+ __old_c->__release_shared();
+ }
+
+ _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> load(memory_order __m = memory_order_seq_cst) const _NOEXCEPT
+ _LIBCPP_CHECK_LOAD_MEMORY_ORDER(__m) {
+ (void)__m;
+ __fields_.__lock();
+ _Tp* __ptr = std::__cxx_atomic_load(__builtin_addressof(__fields_.__ptr_), memory_order_relaxed);
+ __shared_weak_count* __c = __atomic_smart_ptr_storage::__decode(
+ std::__cxx_atomic_load(__builtin_addressof(__fields_.__ctrl_), memory_order_relaxed));
+ if (__c)
+ __c->__add_shared();
+ __fields_.__unlock(__c);
+ return shared_ptr<_Tp>::__create_with_control_block(__ptr, __c);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp>
+ exchange(shared_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) _NOEXCEPT {
+ (void)__m;
+ _Tp* __desired_ptr = __desired.__ptr_;
+ __shared_weak_count* __desired_c = __desired.__cntrl_;
+ __desired.__ptr_ = nullptr;
+ __desired.__cntrl_ = nullptr;
+
+ __fields_.__lock();
+ _Tp* __old_ptr = std::__cxx_atomic_load(__builtin_addressof(__fields_.__ptr_), memory_order_relaxed);
+ __shared_weak_count* __old_c = __atomic_smart_ptr_storage::__decode(
+ std::__cxx_atomic_load(__builtin_addressof(__fields_.__ctrl_), memory_order_relaxed));
+ std::__cxx_atomic_store(__builtin_addressof(__fields_.__ptr_), __desired_ptr, memory_order_relaxed);
+ __fields_.__unlock(__desired_c);
+
+ return shared_ptr<_Tp>::__create_with_control_block(__old_ptr, __old_c);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI bool compare_exchange_strong(
+ shared_ptr<_Tp>& __expected, shared_ptr<_Tp> __desired, memory_order __success, memory_order __failure) _NOEXCEPT
+ _LIBCPP_CHECK_EXCHANGE_MEMORY_ORDER(__success, __failure) {
+ (void)__success;
+ (void)__failure;
+ __fields_.__lock();
+ _Tp* __cur_ptr = std::__cxx_atomic_load(__builtin_addressof(__fields_.__ptr_), memory_order_relaxed);
+ __shared_weak_count* __cur_c = __atomic_smart_ptr_storage::__decode(
+ std::__cxx_atomic_load(__builtin_addressof(__fields_.__ctrl_), memory_order_relaxed));
+
+ if (__atomic_smart_ptr_equivalent(__cur_ptr, __cur_c, __expected.__ptr_, __expected.__cntrl_)) {
+ _Tp* __desired_ptr = __desired.__ptr_;
+ __shared_weak_count* __desired_c = __desired.__cntrl_;
+ __desired.__ptr_ = nullptr;
+ __desired.__cntrl_ = nullptr;
+
+ std::__cxx_atomic_store(__builtin_addressof(__fields_.__ptr_), __desired_ptr, memory_order_relaxed);
+ __fields_.__unlock(__desired_c);
+ if (__cur_c)
+ __cur_c->__release_shared();
+ return true;
+ }
+
+ if (__cur_c)
+ __cur_c->__add_shared();
+ __fields_.__unlock(__cur_c);
+ __expected = shared_ptr<_Tp>::__create_with_control_block(__cur_ptr, __cur_c);
+ return false;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI bool compare_exchange_strong(
+ shared_ptr<_Tp>& __expected, shared_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) _NOEXCEPT {
+ return compare_exchange_strong(__expected, std::move(__desired), __m, std::__to_failure_order(__m));
+ }
+
+ _LIBCPP_HIDE_FROM_ABI bool compare_exchange_weak(
+ shared_ptr<_Tp>& __expected, shared_ptr<_Tp> __desired, memory_order __success, memory_order __failure) _NOEXCEPT
+ _LIBCPP_CHECK_EXCHANGE_MEMORY_ORDER(__success, __failure) {
+ return compare_exchange_strong(__expected, std::move(__desired), __success, __failure);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI bool compare_exchange_weak(
+ shared_ptr<_Tp>& __expected, shared_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) _NOEXCEPT {
+ return compare_exchange_strong(__expected, std::move(__desired), __m, std::__to_failure_order(__m));
+ }
+
+ // Wait until the stored value is not equivalent to __old.
+ // __ctrl_ is the wait address; pointer changes are published with control updates.
+ _LIBCPP_HIDE_FROM_ABI void wait(shared_ptr<_Tp> __old, memory_order __m = memory_order_seq_cst) const _NOEXCEPT
+ _LIBCPP_CHECK_WAIT_MEMORY_ORDER(__m) {
+ _Tp* __old_ptr = __old.__ptr_;
+ __shared_weak_count* __old_c = __old.__cntrl_;
+
+ std::__atomic_smart_ptr_wait_on_address(__fields_.__ctrl_address(), [&] {
+ uintptr_t __word = std::__cxx_atomic_load(__builtin_addressof(__fields_.__ctrl_), __m);
+ __shared_weak_count* __cur_c = __atomic_smart_ptr_storage::__decode(__word);
+ if (__cur_c != __old_c)
+ return true;
+ _Tp* __cur_ptr = std::__cxx_atomic_load(__builtin_addressof(__fields_.__ptr_), __m);
+ return !__atomic_smart_ptr_equivalent(__cur_ptr, __cur_c, __old_ptr, __old_c);
+ });
+ }
+
+ _LIBCPP_HIDE_FROM_ABI void notify_one() _NOEXCEPT { std::__atomic_smart_ptr_notify_one(__fields_.__ctrl_address()); }
+ _LIBCPP_HIDE_FROM_ABI void notify_all() _NOEXCEPT { std::__atomic_smart_ptr_notify_all(__fields_.__ctrl_address()); }
+
+private:
+ __atomic_smart_ptr_fields<_Tp> __fields_;
+};
+
+template <class _Tp>
+struct atomic<weak_ptr<_Tp>> {
+ using value_type = weak_ptr<_Tp>;
+
+ static constexpr bool is_always_lock_free = false;
+
+ _LIBCPP_HIDE_FROM_ABI atomic() _NOEXCEPT : __fields_(nullptr, nullptr) {}
+ _LIBCPP_HIDE_FROM_ABI atomic(weak_ptr<_Tp> __desired) _NOEXCEPT : __fields_(__desired.__ptr_, __desired.__cntrl_) {
+ __desired.__ptr_ = nullptr;
+ __desired.__cntrl_ = nullptr;
+ }
+
+ atomic(const atomic&) = delete;
+ atomic& operator=(const atomic&) = delete;
+
+ _LIBCPP_HIDE_FROM_ABI ~atomic() {
+ if (auto* __c = __atomic_smart_ptr_storage::__decode(
+ std::__cxx_atomic_load(__builtin_addressof(__fields_.__ctrl_), memory_order_relaxed)))
+ __c->__release_weak();
+ }
+
+ _LIBCPP_HIDE_FROM_ABI bool is_lock_free() const _NOEXCEPT { return false; }
+
+ _LIBCPP_HIDE_FROM_ABI void operator=(weak_ptr<_Tp> __desired) _NOEXCEPT { store(std::move(__desired)); }
+ _LIBCPP_HIDE_FROM_ABI operator weak_ptr<_Tp>() const _NOEXCEPT { return load(); }
+
+ _LIBCPP_HIDE_FROM_ABI void store(weak_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) _NOEXCEPT
+ _LIBCPP_CHECK_STORE_MEMORY_ORDER(__m) {
+ (void)__m;
+ _Tp* __desired_ptr = __desired.__ptr_;
+ __shared_weak_count* __desired_c = __desired.__cntrl_;
+ __desired.__ptr_ = nullptr;
+ __desired.__cntrl_ = nullptr;
+
+ __fields_.__lock();
+ __shared_weak_count* __old_c = __atomic_smart_ptr_storage::__decode(
+ std::__cxx_atomic_load(__builtin_addressof(__fields_.__ctrl_), memory_order_relaxed));
+ std::__cxx_atomic_store(__builtin_addressof(__fields_.__ptr_), __desired_ptr, memory_order_relaxed);
+ __fields_.__unlock(__desired_c);
+
+ if (__old_c)
+ __old_c->__release_weak();
+ }
+
+ _LIBCPP_HIDE_FROM_ABI weak_ptr<_Tp> load(memory_order __m = memory_order_seq_cst) const _NOEXCEPT
+ _LIBCPP_CHECK_LOAD_MEMORY_ORDER(__m) {
+ (void)__m;
+ __fields_.__lock();
+ _Tp* __ptr = std::__cxx_atomic_load(__builtin_addressof(__fields_.__ptr_), memory_order_relaxed);
+ __shared_weak_count* __c = __atomic_smart_ptr_storage::__decode(
+ std::__cxx_atomic_load(__builtin_addressof(__fields_.__ctrl_), memory_order_relaxed));
+ if (__c)
+ __c->__add_weak();
+ __fields_.__unlock(__c);
+ return weak_ptr<_Tp>::__create_with_control_block(__ptr, __c);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI weak_ptr<_Tp>
+ exchange(weak_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) _NOEXCEPT {
+ (void)__m;
+ _Tp* __desired_ptr = __desired.__ptr_;
+ __shared_weak_count* __desired_c = __desired.__cntrl_;
+ __desired.__ptr_ = nullptr;
+ __desired.__cntrl_ = nullptr;
+
+ __fields_.__lock();
+ _Tp* __old_ptr = std::__cxx_atomic_load(__builtin_addressof(__fields_.__ptr_), memory_order_relaxed);
+ __shared_weak_count* __old_c = __atomic_smart_ptr_storage::__decode(
+ std::__cxx_atomic_load(__builtin_addressof(__fields_.__ctrl_), memory_order_relaxed));
+ std::__cxx_atomic_store(__builtin_addressof(__fields_.__ptr_), __desired_ptr, memory_order_relaxed);
+ __fields_.__unlock(__desired_c);
+
+ return weak_ptr<_Tp>::__create_with_control_block(__old_ptr, __old_c);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI bool compare_exchange_strong(
+ weak_ptr<_Tp>& __expected, weak_ptr<_Tp> __desired, memory_order __success, memory_order __failure) _NOEXCEPT
+ _LIBCPP_CHECK_EXCHANGE_MEMORY_ORDER(__success, __failure) {
+ (void)__success;
+ (void)__failure;
+ __fields_.__lock();
+ _Tp* __cur_ptr = std::__cxx_atomic_load(__builtin_addressof(__fields_.__ptr_), memory_order_relaxed);
+ __shared_weak_count* __cur_c = __atomic_smart_ptr_storage::__decode(
+ std::__cxx_atomic_load(__builtin_addressof(__fields_.__ctrl_), memory_order_relaxed));
+
+ if (__atomic_smart_ptr_equivalent(__cur_ptr, __cur_c, __expected.__ptr_, __expected.__cntrl_)) {
+ _Tp* __desired_ptr = __desired.__ptr_;
+ __shared_weak_count* __desired_c = __desired.__cntrl_;
+ __desired.__ptr_ = nullptr;
+ __desired.__cntrl_ = nullptr;
+
+ std::__cxx_atomic_store(__builtin_addressof(__fields_.__ptr_), __desired_ptr, memory_order_relaxed);
+ __fields_.__unlock(__desired_c);
+ if (__cur_c)
+ __cur_c->__release_weak();
+ return true;
+ }
+
+ if (__cur_c)
+ __cur_c->__add_weak();
+ __fields_.__unlock(__cur_c);
+ __expected = weak_ptr<_Tp>::__create_with_control_block(__cur_ptr, __cur_c);
+ return false;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI bool compare_exchange_strong(
+ weak_ptr<_Tp>& __expected, weak_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) _NOEXCEPT {
+ return compare_exchange_strong(__expected, std::move(__desired), __m, std::__to_failure_order(__m));
+ }
+
+ _LIBCPP_HIDE_FROM_ABI bool compare_exchange_weak(
+ weak_ptr<_Tp>& __expected, weak_ptr<_Tp> __desired, memory_order __success, memory_order __failure) _NOEXCEPT
+ _LIBCPP_CHECK_EXCHANGE_MEMORY_ORDER(__success, __failure) {
+ return compare_exchange_strong(__expected, std::move(__desired), __success, __failure);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI bool compare_exchange_weak(
+ weak_ptr<_Tp>& __expected, weak_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) _NOEXCEPT {
+ return compare_exchange_strong(__expected, std::move(__desired), __m, std::__to_failure_order(__m));
+ }
+
+ _LIBCPP_HIDE_FROM_ABI void wait(weak_ptr<_Tp> __old, memory_order __m = memory_order_seq_cst) const _NOEXCEPT
+ _LIBCPP_CHECK_WAIT_MEMORY_ORDER(__m) {
+ _Tp* __old_ptr = __old.__ptr_;
+ __shared_weak_count* __old_c = __old.__cntrl_;
+
+ std::__atomic_smart_ptr_wait_on_address(__fields_.__ctrl_address(), [&] {
+ uintptr_t __word = std::__cxx_atomic_load(__builtin_addressof(__fields_.__ctrl_), __m);
+ __shared_weak_count* __cur_c = __atomic_smart_ptr_storage::__decode(__word);
+ if (__cur_c != __old_c)
+ return true;
+ _Tp* __cur_ptr = std::__cxx_atomic_load(__builtin_addressof(__fields_.__ptr_), __m);
+ return !__atomic_smart_ptr_equivalent(__cur_ptr, __cur_c, __old_ptr, __old_c);
+ });
+ }
+
+ _LIBCPP_HIDE_FROM_ABI void notify_one() _NOEXCEPT { std::__atomic_smart_ptr_notify_one(__fields_.__ctrl_address()); }
+ _LIBCPP_HIDE_FROM_ABI void notify_all() _NOEXCEPT { std::__atomic_smart_ptr_notify_all(__fields_.__ctrl_address()); }
+
+private:
+ __atomic_smart_ptr_fields<_Tp> __fields_;
+};
+
+#endif // _LIBCPP_STD_VER >= 20 && _LIBCPP_HAS_THREADS && _LIBCPP_HAS_ATOMIC_HEADER
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___MEMORY_ATOMIC_SHARED_PTR_H
diff --git a/libcxx/include/__memory/shared_ptr.h b/libcxx/include/__memory/shared_ptr.h
index 66442c8868005..45a82abebbb15 100644
--- a/libcxx/include/__memory/shared_ptr.h
+++ b/libcxx/include/__memory/shared_ptr.h
@@ -606,6 +606,9 @@ class _LIBCPP_SHARED_PTR_TRIVIAL_ABI shared_ptr {
return __r;
}
+ template <class>
+ friend struct atomic;
+
private:
template <class _Yp, bool = is_function<_Yp>::value>
struct __shared_ptr_default_allocator {
@@ -1078,8 +1081,8 @@ template <class _Tp, class _Up>
#endif
template <class _Tp, class _Up>
-[[__nodiscard__]] inline
- _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> dynamic_pointer_cast(const shared_ptr<_Up>& __r) _NOEXCEPT {
+[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp>
+dynamic_pointer_cast(const shared_ptr<_Up>& __r) _NOEXCEPT {
typedef typename shared_ptr<_Tp>::element_type _ET;
_ET* __p = dynamic_cast<_ET*>(__r.get());
return __p ? shared_ptr<_Tp>(__r, __p) : shared_ptr<_Tp>();
@@ -1251,6 +1254,17 @@ class _LIBCPP_SHARED_PTR_TRIVIAL_ABI weak_ptr {
friend class weak_ptr;
template <class _Up>
friend class shared_ptr;
+
+ template <class>
+ friend struct atomic;
+
+ template <class _Yp, class _CntrlBlk>
+ _LIBCPP_HIDE_FROM_ABI static weak_ptr<_Tp> __create_with_control_block(_Yp* __p, _CntrlBlk* __cntrl) _NOEXCEPT {
+ weak_ptr<_Tp> __r;
+ __r.__ptr_ = __p;
+ __r.__cntrl_ = __cntrl;
+ return __r;
+ }
};
#if _LIBCPP_STD_VER >= 17
@@ -1472,6 +1486,10 @@ inline _LIBCPP_HIDE_FROM_ABI bool atomic_compare_exchange_weak_explicit(
_LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
_LIBCPP_END_NAMESPACE_STD
+#if _LIBCPP_STD_VER >= 20 && _LIBCPP_HAS_THREADS && _LIBCPP_HAS_ATOMIC_HEADER
+# include <__memory/atomic_shared_ptr.h>
+#endif
+
_LIBCPP_POP_MACROS
#endif // _LIBCPP___MEMORY_SHARED_PTR_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 51a2b77a9ba45..704af8af9a5e2 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -874,6 +874,7 @@ module std [system] {
module atomic_lock_free { header "__atomic/atomic_lock_free.h" }
module atomic_ref { header "__atomic/atomic_ref.h" }
module atomic_sync { header "__atomic/atomic_sync.h" }
+ module atomic_sync_lite { header "__atomic/atomic_sync_lite.h" }
module atomic_sync_timed { header "__atomic/atomic_sync_timed.h" }
module atomic_waitable_traits { header "__atomic/atomic_waitable_traits.h" }
module atomic {
@@ -1688,6 +1689,10 @@ module std [system] {
header "__memory/shared_ptr.h"
export std.functional.hash
}
+ module atomic_shared_ptr {
+ header "__memory/atomic_shared_ptr.h"
+ export std.memory.shared_ptr
+ }
module swap_allocator { header "__memory/swap_allocator.h" }
module temp_value { header "__memory/temp_value.h" }
module temporary_buffer {
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_aliasing.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_aliasing.pass.cpp
new file mode 100644
index 0000000000000..8c70c371475bd
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_aliasing.pass.cpp
@@ -0,0 +1,71 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: no-threads
+
+// [util.smartptr.atomic.shared] equivalence checks with aliasing shared_ptr.
+
+#include <atomic>
+#include <cassert>
+#include <memory>
+
+struct Pair {
+ int a;
+ int b;
+};
+
+int main(int, char**) {
+ auto owner = std::make_shared<Pair>(Pair{10, 20});
+
+ std::shared_ptr<int> view_a(owner, &owner->a);
+ std::shared_ptr<int> view_b(owner, &owner->b);
+ assert(view_a.use_count() == view_b.use_count());
+ assert(view_a.get() != view_b.get());
+
+ std::atomic<std::shared_ptr<int>> atom(view_a);
+
+ // Same control block, different stored pointer -> CAS must fail.
+ {
+ std::shared_ptr<int> expected = view_b;
+ auto fresh = std::make_shared<int>(99);
+ bool ok = atom.compare_exchange_strong(expected, fresh);
+ assert(!ok);
+ assert(expected.get() == view_a.get());
+ }
+
+ // Same stored pointer with the matching control block -> CAS must succeed.
+ {
+ std::shared_ptr<int> expected = view_a;
+ bool ok = atom.compare_exchange_strong(expected, view_b);
+ assert(ok);
+ auto got = atom.load();
+ assert(got.get() == view_b.get());
+ }
+
+ // Both empty are equivalent.
+ {
+ std::atomic<std::shared_ptr<int>> empty_atom;
+ std::shared_ptr<int> expected;
+ bool ok = empty_atom.compare_exchange_strong(expected, view_a);
+ assert(ok);
+ auto got = empty_atom.load();
+ assert(got.get() == view_a.get());
+ }
+
+ // Empty vs non-empty are not equivalent.
+ {
+ std::atomic<std::shared_ptr<int>> non_empty(view_a);
+ std::shared_ptr<int> expected;
+ bool ok = non_empty.compare_exchange_strong(expected, view_b);
+ assert(!ok);
+ assert(expected.get() == view_a.get());
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_class.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_class.pass.cpp
new file mode 100644
index 0000000000000..6eb3ab65fc2e1
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_class.pass.cpp
@@ -0,0 +1,70 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: no-threads
+
+#include <atomic>
+#include <cassert>
+#include <memory>
+#include <thread>
+
+int main(int, char**) {
+ std::atomic<std::shared_ptr<int>> a;
+
+ auto p1 = std::make_shared<int>(1);
+ auto p2 = std::make_shared<int>(2);
+
+ a.store(p1);
+ {
+ auto got = a.load();
+ assert(got && *got == 1);
+ }
+
+ {
+ auto old = a.exchange(p2);
+ assert(old && *old == 1);
+ auto got = a.load();
+ assert(got && *got == 2);
+ }
+
+ {
+ auto expected = p2;
+ bool ok = a.compare_exchange_strong(expected, p1);
+ assert(ok);
+ auto got = a.load();
+ assert(got && *got == 1);
+ }
+
+ {
+ auto expected = p2;
+ bool ok = a.compare_exchange_strong(expected, p2);
+ assert(!ok);
+ assert(expected && *expected == 1);
+ }
+
+#if __cpp_lib_atomic_wait >= 201907L
+ {
+ std::atomic<bool> started{false};
+ std::thread t([&] {
+ auto old = a.load();
+ started.store(true, std::memory_order_release);
+ a.wait(old);
+ });
+
+ while (!started.load(std::memory_order_acquire)) {
+ }
+
+ a.store(p2);
+ a.notify_all();
+ t.join();
+ }
+#endif
+
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_memory_order.verify.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_memory_order.verify.cpp
new file mode 100644
index 0000000000000..047c2785cab6e
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_memory_order.verify.cpp
@@ -0,0 +1,58 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: diagnose-if-support
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: no-threads
+
+#include <atomic>
+#include <memory>
+
+void test() {
+ std::atomic<std::shared_ptr<int>> atom;
+ std::shared_ptr<int> expected;
+ std::shared_ptr<int> desired;
+
+ atom.store(desired,
+ std::memory_order_consume); // expected-warning {{memory order argument to atomic operation is invalid}}
+ atom.store(desired,
+ std::memory_order_acquire); // expected-warning {{memory order argument to atomic operation is invalid}}
+ atom.store(desired,
+ std::memory_order_acq_rel); // expected-warning {{memory order argument to atomic operation is invalid}}
+
+ (void)atom.load(
+ std::memory_order_release); // expected-warning {{memory order argument to atomic operation is invalid}}
+ (void)atom.load(
+ std::memory_order_acq_rel); // expected-warning {{memory order argument to atomic operation is invalid}}
+
+ atom.wait(expected,
+ std::memory_order_release); // expected-warning {{memory order argument to atomic operation is invalid}}
+ atom.wait(expected,
+ std::memory_order_acq_rel); // expected-warning {{memory order argument to atomic operation is invalid}}
+
+ atom.compare_exchange_strong(
+ expected,
+ desired,
+ std::memory_order_seq_cst,
+ std::memory_order_release); // expected-warning {{memory order argument to atomic operation is invalid}}
+ atom.compare_exchange_strong(
+ expected,
+ desired,
+ std::memory_order_seq_cst,
+ std::memory_order_acq_rel); // expected-warning {{memory order argument to atomic operation is invalid}}
+ atom.compare_exchange_weak(
+ expected,
+ desired,
+ std::memory_order_seq_cst,
+ std::memory_order_release); // expected-warning {{memory order argument to atomic operation is invalid}}
+ atom.compare_exchange_weak(
+ expected,
+ desired,
+ std::memory_order_seq_cst,
+ std::memory_order_acq_rel); // expected-warning {{memory order argument to atomic operation is invalid}}
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_nullptr.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_nullptr.pass.cpp
new file mode 100644
index 0000000000000..3662c491f72ee
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_nullptr.pass.cpp
@@ -0,0 +1,54 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: no-threads
+
+// LWG 3661 and LWG 3893 coverage.
+
+#include <atomic>
+#include <cassert>
+#include <memory>
+#include <type_traits>
+
+int main(int, char**) {
+ static_assert(std::is_constructible_v<std::atomic<std::shared_ptr<int>>, std::nullptr_t>);
+ static_assert(std::is_constructible_v<std::atomic<std::weak_ptr<int>>>);
+ static_assert(std::is_assignable_v<std::atomic<std::shared_ptr<int>>&, std::nullptr_t>);
+ static_assert(std::is_assignable_v<std::atomic<std::shared_ptr<int>>&, std::shared_ptr<int>>);
+
+ {
+ std::atomic<std::shared_ptr<int>> a(nullptr);
+ auto loaded = a.load();
+ assert(!loaded);
+ }
+
+ {
+ auto p = std::make_shared<int>(7);
+ std::atomic<std::shared_ptr<int>> a(p);
+ a = nullptr;
+ auto loaded = a.load();
+ assert(!loaded);
+ }
+
+ {
+ auto p = std::make_shared<int>(13);
+ std::atomic<std::shared_ptr<int>> a;
+ a = p;
+ auto loaded = a.load();
+ assert(loaded && *loaded == 13);
+ }
+
+ {
+ std::atomic<std::shared_ptr<int>> a;
+ std::shared_ptr<int> got = a;
+ assert(!got);
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_refcount.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_refcount.pass.cpp
new file mode 100644
index 0000000000000..197506cd5b0ac
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_refcount.pass.cpp
@@ -0,0 +1,105 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: no-threads
+
+#include <atomic>
+#include <cassert>
+#include <memory>
+
+namespace {
+struct Tracker {
+ static int alive;
+ Tracker() { ++alive; }
+ ~Tracker() { --alive; }
+};
+int Tracker::alive = 0;
+
+struct ReentrantDeleter {
+ std::atomic<std::shared_ptr<int>>* watch;
+ void operator()(int* p) const {
+ if (watch) {
+ auto current = watch->load();
+ (void)current;
+ }
+ delete p;
+ }
+};
+} // namespace
+
+int main(int, char**) {
+ // store releases the previous value.
+ {
+ std::atomic<std::shared_ptr<Tracker>> a;
+ a.store(std::make_shared<Tracker>());
+ assert(Tracker::alive == 1);
+ a.store(std::make_shared<Tracker>());
+ assert(Tracker::alive == 1);
+ a.store(nullptr);
+ assert(Tracker::alive == 0);
+ }
+
+ // exchange returns old ownership and preserves refcounts.
+ {
+ auto first = std::make_shared<Tracker>();
+ std::atomic<std::shared_ptr<Tracker>> a(first);
+ assert(first.use_count() == 2);
+ auto second = std::make_shared<Tracker>();
+ auto old = a.exchange(second);
+ assert(old.get() == first.get());
+ assert(first.use_count() == 2);
+ assert(second.use_count() == 2);
+ assert(Tracker::alive == 2);
+ }
+
+ // compare_exchange success/failure paths preserve ownership.
+ {
+ auto p1 = std::make_shared<Tracker>();
+ auto p2 = std::make_shared<Tracker>();
+ std::atomic<std::shared_ptr<Tracker>> a(p1);
+ {
+ std::shared_ptr<Tracker> expected = p2;
+ bool ok = a.compare_exchange_strong(expected, p1);
+ assert(!ok);
+ assert(expected.get() == p1.get());
+ }
+ {
+ std::shared_ptr<Tracker> expected = p1;
+ bool ok = a.compare_exchange_strong(expected, p2);
+ assert(ok);
+ }
+ assert(p1.use_count() == 1);
+ assert(p2.use_count() == 2);
+ }
+ assert(Tracker::alive == 0);
+
+ // atomic destruction releases held shared ownership.
+ {
+ auto p = std::make_shared<Tracker>();
+ {
+ std::atomic<std::shared_ptr<Tracker>> a(p);
+ assert(p.use_count() == 2);
+ }
+ assert(p.use_count() == 1);
+ }
+ assert(Tracker::alive == 0);
+
+ // Deleter must run after unlock; reentrant load must succeed.
+ {
+ std::atomic<std::shared_ptr<int>> a;
+ {
+ ReentrantDeleter d{&a};
+ std::shared_ptr<int> p(new int(7), d);
+ a.store(std::move(p));
+ }
+ a.store(std::shared_ptr<int>{});
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_stress.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_stress.pass.cpp
new file mode 100644
index 0000000000000..e02d74931fce6
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_stress.pass.cpp
@@ -0,0 +1,107 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: no-threads
+
+#include <array>
+#include <atomic>
+#include <cassert>
+#include <memory>
+#include <thread>
+#include <vector>
+
+namespace {
+constexpr int kCandidateCount = 4;
+constexpr int kWriters = 4;
+constexpr int kReaders = 4;
+constexpr int kIterations = 4000;
+
+template <class Atomic, class Make>
+void run(Atomic& a, Make make_value, const std::array<int, kCandidateCount>& expected) {
+ std::atomic<bool> stop{false};
+ std::vector<std::thread> threads;
+
+ for (int w = 0; w < kWriters; ++w) {
+ threads.emplace_back([&, w] {
+ for (int i = 0; i < kIterations; ++i) {
+ a.store(make_value(expected[(w + i) % kCandidateCount]));
+ }
+ });
+ }
+
+ for (int r = 0; r < kReaders; ++r) {
+ threads.emplace_back([&, r] {
+ while (!stop.load(std::memory_order_relaxed)) {
+ auto val = a.load();
+ std::shared_ptr<int> sp;
+ if constexpr (requires { val.lock(); }) {
+ sp = val.lock();
+ } else {
+ sp = val;
+ }
+ if (sp) {
+ int observed = *sp;
+ bool ok = false;
+ for (int candidate : expected) {
+ if (candidate == observed) {
+ ok = true;
+ break;
+ }
+ }
+ assert(ok);
+ }
+ (void)r;
+ }
+ });
+ }
+
+ for (int w = 0; w < kWriters; ++w) {
+ threads[w].join();
+ }
+ stop.store(true, std::memory_order_relaxed);
+ for (int r = 0; r < kReaders; ++r) {
+ threads[kWriters + r].join();
+ }
+}
+} // namespace
+
+int main(int, char**) {
+ const std::array<int, kCandidateCount> expected = {1, 2, 3, 4};
+ std::array<std::shared_ptr<int>, kCandidateCount> pool;
+ for (int i = 0; i < kCandidateCount; ++i) {
+ pool[i] = std::make_shared<int>(expected[i]);
+ }
+
+ {
+ std::atomic<std::shared_ptr<int>> a(pool[0]);
+ auto make_shared_value = [&](int v) {
+ for (int i = 0; i < kCandidateCount; ++i) {
+ if (expected[i] == v)
+ return pool[i];
+ }
+ return pool[0];
+ };
+ run(a, make_shared_value, expected);
+ }
+
+ {
+ std::atomic<std::weak_ptr<int>> a;
+ a.store(std::weak_ptr<int>(pool[0]));
+ auto make_weak_value = [&](int v) -> std::weak_ptr<int> {
+ for (int i = 0; i < kCandidateCount; ++i) {
+ if (expected[i] == v)
+ return std::weak_ptr<int>(pool[i]);
+ }
+ return std::weak_ptr<int>(pool[0]);
+ };
+ run(a, make_weak_value, expected);
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_weak_ptr_class.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_weak_ptr_class.pass.cpp
new file mode 100644
index 0000000000000..2b0c68234d3f0
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_weak_ptr_class.pass.cpp
@@ -0,0 +1,91 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: no-threads
+
+#include <atomic>
+#include <cassert>
+#include <memory>
+#include <thread>
+
+int main(int, char**) {
+ std::atomic<std::weak_ptr<int>> a;
+
+ auto sp1 = std::make_shared<int>(1);
+ auto sp2 = std::make_shared<int>(2);
+ std::weak_ptr<int> wp1 = sp1;
+ std::weak_ptr<int> wp2 = sp2;
+
+ a.store(wp1);
+ {
+ std::weak_ptr<int> got = a.load();
+ auto locked = got.lock();
+ assert(locked && *locked == 1);
+ }
+
+ {
+ std::weak_ptr<int> old = a.exchange(wp2);
+ auto locked = old.lock();
+ assert(locked && *locked == 1);
+ std::weak_ptr<int> got = a.load();
+ auto locked2 = got.lock();
+ assert(locked2 && *locked2 == 2);
+ }
+
+ {
+ std::weak_ptr<int> expected = wp2;
+ bool ok = a.compare_exchange_strong(expected, wp1);
+ assert(ok);
+ std::weak_ptr<int> got = a.load();
+ auto locked = got.lock();
+ assert(locked && *locked == 1);
+ }
+
+ {
+ std::weak_ptr<int> expected = wp2;
+ bool ok = a.compare_exchange_strong(expected, wp2);
+ assert(!ok);
+ auto locked = expected.lock();
+ assert(locked && *locked == 1);
+ }
+
+ // Expired weak pointer remains representable and loadable.
+ {
+ auto sp3 = std::make_shared<int>(3);
+ std::weak_ptr<int> wp3 = sp3;
+ a.store(wp3);
+ sp3.reset();
+ std::weak_ptr<int> got = a.load();
+ assert(got.expired());
+ }
+
+#if __cpp_lib_atomic_wait >= 201907L
+ {
+ auto sp_for_wait = std::make_shared<int>(42);
+ std::weak_ptr<int> wp_for_wait = sp_for_wait;
+ a.store(wp_for_wait);
+
+ std::atomic<bool> started{false};
+ std::thread t([&] {
+ std::weak_ptr<int> old = a.load();
+ started.store(true, std::memory_order_release);
+ a.wait(old);
+ });
+
+ while (!started.load(std::memory_order_acquire)) {
+ }
+
+ a.store(wp1);
+ a.notify_all();
+ t.join();
+ }
+#endif
+
+ return 0;
+}
>From 5d0484bdbd2e33ae90a03bcf2187d6da3ea7c343 Mon Sep 17 00:00:00 2001
From: ViNN280801 <vladislav.semykin at gmail.com>
Date: Sun, 26 Apr 2026 15:51:41 +0300
Subject: [PATCH 02/14] [libcxx] Updated docs; replaced old-style _NOEXCEPT
with noexcept; added necessary headers for arm platforms
Signed-off-by: ViNN280801 <vladislav.semykin at gmail.com>
---
libcxx/docs/FeatureTestMacroTable.rst | 2 +-
libcxx/docs/Status/Cxx20Papers.csv | 2 +-
libcxx/docs/Status/Cxx23Issues.csv | 2 +-
libcxx/docs/Status/Cxx2cIssues.csv | 2 +-
libcxx/include/__memory/atomic_shared_ptr.h | 91 +++++++++----------
libcxx/include/version | 2 +-
.../atomic.version.compile.pass.cpp | 48 +++-------
.../version.version.compile.pass.cpp | 48 +++-------
.../atomic_shared_ptr_aliasing.pass.cpp | 2 +-
.../atomic_shared_ptr_class.pass.cpp | 2 +-
.../atomic_shared_ptr_memory_order.verify.cpp | 2 +-
.../atomic_shared_ptr_nullptr.pass.cpp | 3 +-
.../atomic_shared_ptr_refcount.pass.cpp | 3 +-
.../atomic_shared_ptr_stress.pass.cpp | 2 +-
.../atomic_weak_ptr_class.pass.cpp | 2 +-
.../generate_feature_test_macro_components.py | 1 -
16 files changed, 89 insertions(+), 125 deletions(-)
diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index ae48eaed1f46b..902e9df4e1df2 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -180,7 +180,7 @@ Status
---------------------------------------------------------- -----------------
``__cpp_lib_atomic_ref`` ``201806L``
---------------------------------------------------------- -----------------
- ``__cpp_lib_atomic_shared_ptr`` *unimplemented*
+ ``__cpp_lib_atomic_shared_ptr`` ``201711L``
---------------------------------------------------------- -----------------
``__cpp_lib_atomic_value_initialization`` ``201911L``
---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv
index 7fbccde688009..3b1dc43a7fd5b 100644
--- a/libcxx/docs/Status/Cxx20Papers.csv
+++ b/libcxx/docs/Status/Cxx20Papers.csv
@@ -12,7 +12,7 @@
"`P0600R1 <https://wg21.link/P0600R1>`__","nodiscard in the Library","2017-11 (Albuquerque)","|Complete|","16","`#104010 <https://github.com/llvm/llvm-project/issues/104010>`__",""
"`P0616R0 <https://wg21.link/P0616R0>`__","de-pessimize legacy <numeric> algorithms with std::move","2017-11 (Albuquerque)","|Complete|","12","`#104011 <https://github.com/llvm/llvm-project/issues/104011>`__",""
"`P0653R2 <https://wg21.link/P0653R2>`__","Utility to convert a pointer to a raw pointer","2017-11 (Albuquerque)","|Complete|","6","`#104012 <https://github.com/llvm/llvm-project/issues/104012>`__",""
-"`P0718R2 <https://wg21.link/P0718R2>`__","Atomic shared_ptr","2017-11 (Albuquerque)","","","`#99980 <https://github.com/llvm/llvm-project/issues/99980>`__",""
+"`P0718R2 <https://wg21.link/P0718R2>`__","Atomic shared_ptr","2017-11 (Albuquerque)","|Complete|","22","`#99980 <https://github.com/llvm/llvm-project/issues/99980>`__",""
"`P0767R1 <https://wg21.link/P0767R1>`__","Deprecate POD","2017-11 (Albuquerque)","|Complete|","21","`#104013 <https://github.com/llvm/llvm-project/issues/104013>`__","It was previously erroneously marked as complete in LLVM 7."
"`P0768R1 <https://wg21.link/P0768R1>`__","Library Support for the Spaceship (Comparison) Operator","2017-11 (Albuquerque)","|Complete|","","`#104014 <https://github.com/llvm/llvm-project/issues/104014>`__",""
"`P0777R1 <https://wg21.link/P0777R1>`__","Treating Unnecessary ``decay``\ ","2017-11 (Albuquerque)","|Complete|","7","`#104016 <https://github.com/llvm/llvm-project/issues/104016>`__",""
diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index 0a2f0d59484de..7788cd285d997 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -157,7 +157,7 @@
"`LWG3654 <https://wg21.link/LWG3654>`__","``basic_format_context::arg(size_t)`` should be ``noexcept`` ","2022-02 (Virtual)","|Complete|","15","`#104959 <https://github.com/llvm/llvm-project/issues/104959>`__",""
"`LWG3657 <https://wg21.link/LWG3657>`__","``std::hash<std::filesystem::path>`` is not enabled","2022-02 (Virtual)","|Complete|","17","`#104960 <https://github.com/llvm/llvm-project/issues/104960>`__",""
"`LWG3660 <https://wg21.link/LWG3660>`__","``iterator_traits<common_iterator>::pointer`` should conform to ยง[iterator.traits]","2022-02 (Virtual)","|Complete|","14","`#104961 <https://github.com/llvm/llvm-project/issues/104961>`__",""
-"`LWG3661 <https://wg21.link/LWG3661>`__","``constinit atomic<shared_ptr<T>> a(nullptr);`` should work","2022-02 (Virtual)","","","`#104962 <https://github.com/llvm/llvm-project/issues/104962>`__",""
+"`LWG3661 <https://wg21.link/LWG3661>`__","``constinit atomic<shared_ptr<T>> a(nullptr);`` should work","2022-02 (Virtual)","|Complete|","22","`#104962 <https://github.com/llvm/llvm-project/issues/104962>`__",""
"","","","","","",""
"`LWG3564 <https://wg21.link/LWG3564>`__","``transform_view::iterator<true>::value_type`` and ``iterator_category`` should use ``const F&``","2022-07 (Virtual)","|Complete|","20","`#104963 <https://github.com/llvm/llvm-project/issues/104963>`__",""
"`LWG3617 <https://wg21.link/LWG3617>`__","``function``/``packaged_task`` deduction guides and deducing ``this``","2022-07 (Virtual)","","","`#104965 <https://github.com/llvm/llvm-project/issues/104965>`__",""
diff --git a/libcxx/docs/Status/Cxx2cIssues.csv b/libcxx/docs/Status/Cxx2cIssues.csv
index e329e14f70ab3..0a868e89eba6b 100644
--- a/libcxx/docs/Status/Cxx2cIssues.csv
+++ b/libcxx/docs/Status/Cxx2cIssues.csv
@@ -3,7 +3,7 @@
"`LWG3884 <https://wg21.link/LWG3884>`__","``flat_foo`` is missing allocator-extended copy/move constructors","2023-06 (Varna)","|Complete|","21","`#105269 <https://github.com/llvm/llvm-project/issues/105269>`__",""
"`LWG3885 <https://wg21.link/LWG3885>`__","``op`` should be in [zombie.names]","2023-06 (Varna)","|Nothing To Do|","","`#105270 <https://github.com/llvm/llvm-project/issues/105270>`__",""
"`LWG3887 <https://wg21.link/LWG3887>`__","Version macro for ``allocate_at_least``","2023-06 (Varna)","|Complete|","19","`#105271 <https://github.com/llvm/llvm-project/issues/105271>`__",""
-"`LWG3893 <https://wg21.link/LWG3893>`__","LWG 3661 broke ``atomic<shared_ptr<T>> a; a = nullptr;``","2023-06 (Varna)","","","`#105273 <https://github.com/llvm/llvm-project/issues/105273>`__",""
+"`LWG3893 <https://wg21.link/LWG3893>`__","LWG 3661 broke ``atomic<shared_ptr<T>> a; a = nullptr;``","2023-06 (Varna)","|Complete|","22","`#105273 <https://github.com/llvm/llvm-project/issues/105273>`__",""
"`LWG3894 <https://wg21.link/LWG3894>`__","``generator::promise_type::yield_value(ranges::elements_of<Rng, Alloc>)`` should not be ``noexcept``","2023-06 (Varna)","","","`#105274 <https://github.com/llvm/llvm-project/issues/105274>`__",""
"`LWG3903 <https://wg21.link/LWG3903>`__","span destructor is redundantly noexcept","2023-06 (Varna)","|Complete|","7","`#105275 <https://github.com/llvm/llvm-project/issues/105275>`__",""
"`LWG3904 <https://wg21.link/LWG3904>`__","``lazy_split_view::outer-iterator``'s const-converting constructor isn't setting ``trailing_empty_``","2023-06 (Varna)","","","`#105276 <https://github.com/llvm/llvm-project/issues/105276>`__",""
diff --git a/libcxx/include/__memory/atomic_shared_ptr.h b/libcxx/include/__memory/atomic_shared_ptr.h
index 000b56e08bae6..a4875b9687f45 100644
--- a/libcxx/include/__memory/atomic_shared_ptr.h
+++ b/libcxx/include/__memory/atomic_shared_ptr.h
@@ -72,19 +72,19 @@ struct __atomic_smart_ptr_storage {
static constexpr uintptr_t __notify_bit_ = uintptr_t{2};
static constexpr uintptr_t __ptr_mask_ = ~(__lock_bit_ | __notify_bit_);
- _LIBCPP_HIDE_FROM_ABI static uintptr_t __encode(__shared_weak_count* __ctrl, uintptr_t __bits) _NOEXCEPT {
+ _LIBCPP_HIDE_FROM_ABI static uintptr_t __encode(__shared_weak_count* __ctrl, uintptr_t __bits) noexcept {
return (reinterpret_cast<uintptr_t>(__ctrl) & __ptr_mask_) | (__bits & ~__ptr_mask_);
}
- _LIBCPP_HIDE_FROM_ABI static __shared_weak_count* __decode(uintptr_t __word) _NOEXCEPT {
+ _LIBCPP_HIDE_FROM_ABI static __shared_weak_count* __decode(uintptr_t __word) noexcept {
return reinterpret_cast<__shared_weak_count*>(__word & __ptr_mask_);
}
- _LIBCPP_HIDE_FROM_ABI static bool __has_lock(uintptr_t __word) _NOEXCEPT { return (__word & __lock_bit_) != 0; }
- _LIBCPP_HIDE_FROM_ABI static bool __has_notify(uintptr_t __word) _NOEXCEPT { return (__word & __notify_bit_) != 0; }
+ _LIBCPP_HIDE_FROM_ABI static bool __has_lock(uintptr_t __word) noexcept { return (__word & __lock_bit_) != 0; }
+ _LIBCPP_HIDE_FROM_ABI static bool __has_notify(uintptr_t __word) noexcept { return (__word & __notify_bit_) != 0; }
};
-_LIBCPP_HIDE_FROM_ABI inline void __atomic_smart_ptr_notify_one(const void* __address) _NOEXCEPT {
+_LIBCPP_HIDE_FROM_ABI inline void __atomic_smart_ptr_notify_one(const void* __address) noexcept {
# if _LIBCPP_AVAILABILITY_HAS_NEW_SYNC
std::__atomic_notify_one_global_table(__address);
# else
@@ -92,7 +92,7 @@ _LIBCPP_HIDE_FROM_ABI inline void __atomic_smart_ptr_notify_one(const void* __ad
# endif
}
-_LIBCPP_HIDE_FROM_ABI inline void __atomic_smart_ptr_notify_all(const void* __address) _NOEXCEPT {
+_LIBCPP_HIDE_FROM_ABI inline void __atomic_smart_ptr_notify_all(const void* __address) noexcept {
# if _LIBCPP_AVAILABILITY_HAS_NEW_SYNC
std::__atomic_notify_all_global_table(__address);
# else
@@ -101,7 +101,7 @@ _LIBCPP_HIDE_FROM_ABI inline void __atomic_smart_ptr_notify_all(const void* __ad
}
template <class _Poll>
-_LIBCPP_HIDE_FROM_ABI inline void __atomic_smart_ptr_wait_on_address(const void* __address, _Poll&& __poll) _NOEXCEPT {
+_LIBCPP_HIDE_FROM_ABI inline void __atomic_smart_ptr_wait_on_address(const void* __address, _Poll&& __poll) noexcept {
while (!__poll()) {
# if _LIBCPP_AVAILABILITY_HAS_NEW_SYNC
auto __monitor_value = std::__atomic_monitor_global(__address);
@@ -123,16 +123,15 @@ struct __atomic_smart_ptr_fields {
mutable __cxx_atomic_impl<_Element*> __ptr_;
mutable __cxx_atomic_impl<uintptr_t> __ctrl_;
- _LIBCPP_HIDE_FROM_ABI __atomic_smart_ptr_fields(_Element* __p, __shared_weak_count* __c) _NOEXCEPT
- : __ptr_(__p),
- __ctrl_(__atomic_smart_ptr_storage::__encode(__c, 0)) {}
+ _LIBCPP_HIDE_FROM_ABI __atomic_smart_ptr_fields(_Element* __p, __shared_weak_count* __c) noexcept
+ : __ptr_(__p), __ctrl_(__atomic_smart_ptr_storage::__encode(__c, 0)) {}
- _LIBCPP_HIDE_FROM_ABI const void* __ctrl_address() const _NOEXCEPT {
+ _LIBCPP_HIDE_FROM_ABI const void* __ctrl_address() const noexcept {
return static_cast<const void*>(__builtin_addressof(__ctrl_));
}
// Acquire lock bit on __ctrl_. Contended path sets notify bit and waits.
- _LIBCPP_HIDE_FROM_ABI void __lock() const _NOEXCEPT {
+ _LIBCPP_HIDE_FROM_ABI void __lock() const noexcept {
_LIBCPP_ATOMIC_SP_TSAN_PRE_LOCK(&__ctrl_);
uintptr_t __expected = std::__cxx_atomic_load(__builtin_addressof(__ctrl_), memory_order_relaxed);
for (;;) {
@@ -170,7 +169,7 @@ struct __atomic_smart_ptr_fields {
}
// Publish new control pointer, clear bits, and notify waiters if needed.
- _LIBCPP_HIDE_FROM_ABI void __unlock(__shared_weak_count* __ctrl_to_publish) const _NOEXCEPT {
+ _LIBCPP_HIDE_FROM_ABI void __unlock(__shared_weak_count* __ctrl_to_publish) const noexcept {
_LIBCPP_ATOMIC_SP_TSAN_PRE_UNLOCK(&__ctrl_);
uintptr_t __new_word = __atomic_smart_ptr_storage::__encode(__ctrl_to_publish, 0);
uintptr_t __previous = std::__cxx_atomic_exchange(__builtin_addressof(__ctrl_), __new_word, memory_order_release);
@@ -186,7 +185,7 @@ _LIBCPP_HIDE_FROM_ABI inline bool __atomic_smart_ptr_equivalent(
_Element* __ptr,
__shared_weak_count* __ctrl,
_Element* __expected_ptr,
- __shared_weak_count* __expected_ctrl) _NOEXCEPT {
+ __shared_weak_count* __expected_ctrl) noexcept {
if (__ctrl == nullptr && __expected_ctrl == nullptr)
return true;
return __ptr == __expected_ptr && __ctrl == __expected_ctrl;
@@ -198,9 +197,9 @@ struct atomic<shared_ptr<_Tp>> {
static constexpr bool is_always_lock_free = false;
- _LIBCPP_HIDE_FROM_ABI atomic() _NOEXCEPT : __fields_(nullptr, nullptr) {}
- _LIBCPP_HIDE_FROM_ABI constexpr atomic(nullptr_t) _NOEXCEPT : __fields_(nullptr, nullptr) {}
- _LIBCPP_HIDE_FROM_ABI atomic(shared_ptr<_Tp> __desired) _NOEXCEPT : __fields_(__desired.__ptr_, __desired.__cntrl_) {
+ _LIBCPP_HIDE_FROM_ABI atomic() noexcept : __fields_(nullptr, nullptr) {}
+ _LIBCPP_HIDE_FROM_ABI constexpr atomic(nullptr_t) noexcept : __fields_(nullptr, nullptr) {}
+ _LIBCPP_HIDE_FROM_ABI atomic(shared_ptr<_Tp> __desired) noexcept : __fields_(__desired.__ptr_, __desired.__cntrl_) {
__desired.__ptr_ = nullptr;
__desired.__cntrl_ = nullptr;
}
@@ -214,13 +213,13 @@ struct atomic<shared_ptr<_Tp>> {
__c->__release_shared();
}
- _LIBCPP_HIDE_FROM_ABI bool is_lock_free() const _NOEXCEPT { return false; }
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool is_lock_free() const noexcept { return false; }
- _LIBCPP_HIDE_FROM_ABI void operator=(shared_ptr<_Tp> __desired) _NOEXCEPT { store(std::move(__desired)); }
- _LIBCPP_HIDE_FROM_ABI void operator=(nullptr_t) _NOEXCEPT { store(nullptr); }
- _LIBCPP_HIDE_FROM_ABI operator shared_ptr<_Tp>() const _NOEXCEPT { return load(); }
+ _LIBCPP_HIDE_FROM_ABI void operator=(shared_ptr<_Tp> __desired) noexcept { store(std::move(__desired)); }
+ _LIBCPP_HIDE_FROM_ABI void operator=(nullptr_t) noexcept { store(nullptr); }
+ _LIBCPP_HIDE_FROM_ABI operator shared_ptr<_Tp>() const noexcept { return load(); }
- _LIBCPP_HIDE_FROM_ABI void store(shared_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) _NOEXCEPT
+ _LIBCPP_HIDE_FROM_ABI void store(shared_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) noexcept
_LIBCPP_CHECK_STORE_MEMORY_ORDER(__m) {
(void)__m;
_Tp* __desired_ptr = __desired.__ptr_;
@@ -238,7 +237,7 @@ struct atomic<shared_ptr<_Tp>> {
__old_c->__release_shared();
}
- _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> load(memory_order __m = memory_order_seq_cst) const _NOEXCEPT
+ _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> load(memory_order __m = memory_order_seq_cst) const noexcept
_LIBCPP_CHECK_LOAD_MEMORY_ORDER(__m) {
(void)__m;
__fields_.__lock();
@@ -252,7 +251,7 @@ struct atomic<shared_ptr<_Tp>> {
}
_LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp>
- exchange(shared_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) _NOEXCEPT {
+ exchange(shared_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) noexcept {
(void)__m;
_Tp* __desired_ptr = __desired.__ptr_;
__shared_weak_count* __desired_c = __desired.__cntrl_;
@@ -270,7 +269,7 @@ struct atomic<shared_ptr<_Tp>> {
}
_LIBCPP_HIDE_FROM_ABI bool compare_exchange_strong(
- shared_ptr<_Tp>& __expected, shared_ptr<_Tp> __desired, memory_order __success, memory_order __failure) _NOEXCEPT
+ shared_ptr<_Tp>& __expected, shared_ptr<_Tp> __desired, memory_order __success, memory_order __failure) noexcept
_LIBCPP_CHECK_EXCHANGE_MEMORY_ORDER(__success, __failure) {
(void)__success;
(void)__failure;
@@ -300,24 +299,24 @@ struct atomic<shared_ptr<_Tp>> {
}
_LIBCPP_HIDE_FROM_ABI bool compare_exchange_strong(
- shared_ptr<_Tp>& __expected, shared_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) _NOEXCEPT {
+ shared_ptr<_Tp>& __expected, shared_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) noexcept {
return compare_exchange_strong(__expected, std::move(__desired), __m, std::__to_failure_order(__m));
}
_LIBCPP_HIDE_FROM_ABI bool compare_exchange_weak(
- shared_ptr<_Tp>& __expected, shared_ptr<_Tp> __desired, memory_order __success, memory_order __failure) _NOEXCEPT
+ shared_ptr<_Tp>& __expected, shared_ptr<_Tp> __desired, memory_order __success, memory_order __failure) noexcept
_LIBCPP_CHECK_EXCHANGE_MEMORY_ORDER(__success, __failure) {
return compare_exchange_strong(__expected, std::move(__desired), __success, __failure);
}
_LIBCPP_HIDE_FROM_ABI bool compare_exchange_weak(
- shared_ptr<_Tp>& __expected, shared_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) _NOEXCEPT {
+ shared_ptr<_Tp>& __expected, shared_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) noexcept {
return compare_exchange_strong(__expected, std::move(__desired), __m, std::__to_failure_order(__m));
}
// Wait until the stored value is not equivalent to __old.
// __ctrl_ is the wait address; pointer changes are published with control updates.
- _LIBCPP_HIDE_FROM_ABI void wait(shared_ptr<_Tp> __old, memory_order __m = memory_order_seq_cst) const _NOEXCEPT
+ _LIBCPP_HIDE_FROM_ABI void wait(shared_ptr<_Tp> __old, memory_order __m = memory_order_seq_cst) const noexcept
_LIBCPP_CHECK_WAIT_MEMORY_ORDER(__m) {
_Tp* __old_ptr = __old.__ptr_;
__shared_weak_count* __old_c = __old.__cntrl_;
@@ -332,8 +331,8 @@ struct atomic<shared_ptr<_Tp>> {
});
}
- _LIBCPP_HIDE_FROM_ABI void notify_one() _NOEXCEPT { std::__atomic_smart_ptr_notify_one(__fields_.__ctrl_address()); }
- _LIBCPP_HIDE_FROM_ABI void notify_all() _NOEXCEPT { std::__atomic_smart_ptr_notify_all(__fields_.__ctrl_address()); }
+ _LIBCPP_HIDE_FROM_ABI void notify_one() noexcept { std::__atomic_smart_ptr_notify_one(__fields_.__ctrl_address()); }
+ _LIBCPP_HIDE_FROM_ABI void notify_all() noexcept { std::__atomic_smart_ptr_notify_all(__fields_.__ctrl_address()); }
private:
__atomic_smart_ptr_fields<_Tp> __fields_;
@@ -345,8 +344,8 @@ struct atomic<weak_ptr<_Tp>> {
static constexpr bool is_always_lock_free = false;
- _LIBCPP_HIDE_FROM_ABI atomic() _NOEXCEPT : __fields_(nullptr, nullptr) {}
- _LIBCPP_HIDE_FROM_ABI atomic(weak_ptr<_Tp> __desired) _NOEXCEPT : __fields_(__desired.__ptr_, __desired.__cntrl_) {
+ _LIBCPP_HIDE_FROM_ABI atomic() noexcept : __fields_(nullptr, nullptr) {}
+ _LIBCPP_HIDE_FROM_ABI atomic(weak_ptr<_Tp> __desired) noexcept : __fields_(__desired.__ptr_, __desired.__cntrl_) {
__desired.__ptr_ = nullptr;
__desired.__cntrl_ = nullptr;
}
@@ -360,12 +359,12 @@ struct atomic<weak_ptr<_Tp>> {
__c->__release_weak();
}
- _LIBCPP_HIDE_FROM_ABI bool is_lock_free() const _NOEXCEPT { return false; }
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool is_lock_free() const noexcept { return false; }
- _LIBCPP_HIDE_FROM_ABI void operator=(weak_ptr<_Tp> __desired) _NOEXCEPT { store(std::move(__desired)); }
- _LIBCPP_HIDE_FROM_ABI operator weak_ptr<_Tp>() const _NOEXCEPT { return load(); }
+ _LIBCPP_HIDE_FROM_ABI void operator=(weak_ptr<_Tp> __desired) noexcept { store(std::move(__desired)); }
+ _LIBCPP_HIDE_FROM_ABI operator weak_ptr<_Tp>() const noexcept { return load(); }
- _LIBCPP_HIDE_FROM_ABI void store(weak_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) _NOEXCEPT
+ _LIBCPP_HIDE_FROM_ABI void store(weak_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) noexcept
_LIBCPP_CHECK_STORE_MEMORY_ORDER(__m) {
(void)__m;
_Tp* __desired_ptr = __desired.__ptr_;
@@ -383,7 +382,7 @@ struct atomic<weak_ptr<_Tp>> {
__old_c->__release_weak();
}
- _LIBCPP_HIDE_FROM_ABI weak_ptr<_Tp> load(memory_order __m = memory_order_seq_cst) const _NOEXCEPT
+ _LIBCPP_HIDE_FROM_ABI weak_ptr<_Tp> load(memory_order __m = memory_order_seq_cst) const noexcept
_LIBCPP_CHECK_LOAD_MEMORY_ORDER(__m) {
(void)__m;
__fields_.__lock();
@@ -397,7 +396,7 @@ struct atomic<weak_ptr<_Tp>> {
}
_LIBCPP_HIDE_FROM_ABI weak_ptr<_Tp>
- exchange(weak_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) _NOEXCEPT {
+ exchange(weak_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) noexcept {
(void)__m;
_Tp* __desired_ptr = __desired.__ptr_;
__shared_weak_count* __desired_c = __desired.__cntrl_;
@@ -415,7 +414,7 @@ struct atomic<weak_ptr<_Tp>> {
}
_LIBCPP_HIDE_FROM_ABI bool compare_exchange_strong(
- weak_ptr<_Tp>& __expected, weak_ptr<_Tp> __desired, memory_order __success, memory_order __failure) _NOEXCEPT
+ weak_ptr<_Tp>& __expected, weak_ptr<_Tp> __desired, memory_order __success, memory_order __failure) noexcept
_LIBCPP_CHECK_EXCHANGE_MEMORY_ORDER(__success, __failure) {
(void)__success;
(void)__failure;
@@ -445,22 +444,22 @@ struct atomic<weak_ptr<_Tp>> {
}
_LIBCPP_HIDE_FROM_ABI bool compare_exchange_strong(
- weak_ptr<_Tp>& __expected, weak_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) _NOEXCEPT {
+ weak_ptr<_Tp>& __expected, weak_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) noexcept {
return compare_exchange_strong(__expected, std::move(__desired), __m, std::__to_failure_order(__m));
}
_LIBCPP_HIDE_FROM_ABI bool compare_exchange_weak(
- weak_ptr<_Tp>& __expected, weak_ptr<_Tp> __desired, memory_order __success, memory_order __failure) _NOEXCEPT
+ weak_ptr<_Tp>& __expected, weak_ptr<_Tp> __desired, memory_order __success, memory_order __failure) noexcept
_LIBCPP_CHECK_EXCHANGE_MEMORY_ORDER(__success, __failure) {
return compare_exchange_strong(__expected, std::move(__desired), __success, __failure);
}
_LIBCPP_HIDE_FROM_ABI bool compare_exchange_weak(
- weak_ptr<_Tp>& __expected, weak_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) _NOEXCEPT {
+ weak_ptr<_Tp>& __expected, weak_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) noexcept {
return compare_exchange_strong(__expected, std::move(__desired), __m, std::__to_failure_order(__m));
}
- _LIBCPP_HIDE_FROM_ABI void wait(weak_ptr<_Tp> __old, memory_order __m = memory_order_seq_cst) const _NOEXCEPT
+ _LIBCPP_HIDE_FROM_ABI void wait(weak_ptr<_Tp> __old, memory_order __m = memory_order_seq_cst) const noexcept
_LIBCPP_CHECK_WAIT_MEMORY_ORDER(__m) {
_Tp* __old_ptr = __old.__ptr_;
__shared_weak_count* __old_c = __old.__cntrl_;
@@ -475,8 +474,8 @@ struct atomic<weak_ptr<_Tp>> {
});
}
- _LIBCPP_HIDE_FROM_ABI void notify_one() _NOEXCEPT { std::__atomic_smart_ptr_notify_one(__fields_.__ctrl_address()); }
- _LIBCPP_HIDE_FROM_ABI void notify_all() _NOEXCEPT { std::__atomic_smart_ptr_notify_all(__fields_.__ctrl_address()); }
+ _LIBCPP_HIDE_FROM_ABI void notify_one() noexcept { std::__atomic_smart_ptr_notify_one(__fields_.__ctrl_address()); }
+ _LIBCPP_HIDE_FROM_ABI void notify_all() noexcept { std::__atomic_smart_ptr_notify_all(__fields_.__ctrl_address()); }
private:
__atomic_smart_ptr_fields<_Tp> __fields_;
diff --git a/libcxx/include/version b/libcxx/include/version
index 1c683b67e5700..ab11cf1fbcca7 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -398,7 +398,7 @@ __cpp_lib_void_t 201411L <type_traits>
# define __cpp_lib_atomic_float 201711L
# define __cpp_lib_atomic_lock_free_type_aliases 201907L
# define __cpp_lib_atomic_ref 201806L
-// # define __cpp_lib_atomic_shared_ptr 201711L
+# define __cpp_lib_atomic_shared_ptr 201711L
# define __cpp_lib_atomic_value_initialization 201911L
# define __cpp_lib_atomic_wait 201907L
# if _LIBCPP_HAS_THREADS
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/atomic.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/atomic.version.compile.pass.cpp
index e62416257a696..599d3654ffc92 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/atomic.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/atomic.version.compile.pass.cpp
@@ -188,17 +188,11 @@
# error "__cpp_lib_atomic_ref should have the value 201806L in c++20"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_atomic_shared_ptr
-# error "__cpp_lib_atomic_shared_ptr should be defined in c++20"
-# endif
-# if __cpp_lib_atomic_shared_ptr != 201711L
-# error "__cpp_lib_atomic_shared_ptr should have the value 201711L in c++20"
-# endif
-# else
-# ifdef __cpp_lib_atomic_shared_ptr
-# error "__cpp_lib_atomic_shared_ptr should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_atomic_shared_ptr
+# error "__cpp_lib_atomic_shared_ptr should be defined in c++20"
+# endif
+# if __cpp_lib_atomic_shared_ptr != 201711L
+# error "__cpp_lib_atomic_shared_ptr should have the value 201711L in c++20"
# endif
# ifndef __cpp_lib_atomic_value_initialization
@@ -269,17 +263,11 @@
# error "__cpp_lib_atomic_ref should have the value 201806L in c++23"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_atomic_shared_ptr
-# error "__cpp_lib_atomic_shared_ptr should be defined in c++23"
-# endif
-# if __cpp_lib_atomic_shared_ptr != 201711L
-# error "__cpp_lib_atomic_shared_ptr should have the value 201711L in c++23"
-# endif
-# else
-# ifdef __cpp_lib_atomic_shared_ptr
-# error "__cpp_lib_atomic_shared_ptr should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_atomic_shared_ptr
+# error "__cpp_lib_atomic_shared_ptr should be defined in c++23"
+# endif
+# if __cpp_lib_atomic_shared_ptr != 201711L
+# error "__cpp_lib_atomic_shared_ptr should have the value 201711L in c++23"
# endif
# ifndef __cpp_lib_atomic_value_initialization
@@ -359,17 +347,11 @@
# error "__cpp_lib_atomic_ref should have the value 202603L in c++26"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_atomic_shared_ptr
-# error "__cpp_lib_atomic_shared_ptr should be defined in c++26"
-# endif
-# if __cpp_lib_atomic_shared_ptr != 201711L
-# error "__cpp_lib_atomic_shared_ptr should have the value 201711L in c++26"
-# endif
-# else
-# ifdef __cpp_lib_atomic_shared_ptr
-# error "__cpp_lib_atomic_shared_ptr should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_atomic_shared_ptr
+# error "__cpp_lib_atomic_shared_ptr should be defined in c++26"
+# endif
+# if __cpp_lib_atomic_shared_ptr != 201711L
+# error "__cpp_lib_atomic_shared_ptr should have the value 201711L in c++26"
# endif
# ifndef __cpp_lib_atomic_value_initialization
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index dfee4b6d458db..203b03eb16ca8 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
@@ -3242,17 +3242,11 @@
# error "__cpp_lib_atomic_ref should have the value 201806L in c++20"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_atomic_shared_ptr
-# error "__cpp_lib_atomic_shared_ptr should be defined in c++20"
-# endif
-# if __cpp_lib_atomic_shared_ptr != 201711L
-# error "__cpp_lib_atomic_shared_ptr should have the value 201711L in c++20"
-# endif
-# else
-# ifdef __cpp_lib_atomic_shared_ptr
-# error "__cpp_lib_atomic_shared_ptr should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_atomic_shared_ptr
+# error "__cpp_lib_atomic_shared_ptr should be defined in c++20"
+# endif
+# if __cpp_lib_atomic_shared_ptr != 201711L
+# error "__cpp_lib_atomic_shared_ptr should have the value 201711L in c++20"
# endif
# ifndef __cpp_lib_atomic_value_initialization
@@ -4711,17 +4705,11 @@
# error "__cpp_lib_atomic_ref should have the value 201806L in c++23"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_atomic_shared_ptr
-# error "__cpp_lib_atomic_shared_ptr should be defined in c++23"
-# endif
-# if __cpp_lib_atomic_shared_ptr != 201711L
-# error "__cpp_lib_atomic_shared_ptr should have the value 201711L in c++23"
-# endif
-# else
-# ifdef __cpp_lib_atomic_shared_ptr
-# error "__cpp_lib_atomic_shared_ptr should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_atomic_shared_ptr
+# error "__cpp_lib_atomic_shared_ptr should be defined in c++23"
+# endif
+# if __cpp_lib_atomic_shared_ptr != 201711L
+# error "__cpp_lib_atomic_shared_ptr should have the value 201711L in c++23"
# endif
# ifndef __cpp_lib_atomic_value_initialization
@@ -6414,17 +6402,11 @@
# error "__cpp_lib_atomic_ref should have the value 202603L in c++26"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_atomic_shared_ptr
-# error "__cpp_lib_atomic_shared_ptr should be defined in c++26"
-# endif
-# if __cpp_lib_atomic_shared_ptr != 201711L
-# error "__cpp_lib_atomic_shared_ptr should have the value 201711L in c++26"
-# endif
-# else
-# ifdef __cpp_lib_atomic_shared_ptr
-# error "__cpp_lib_atomic_shared_ptr should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_atomic_shared_ptr
+# error "__cpp_lib_atomic_shared_ptr should be defined in c++26"
+# endif
+# if __cpp_lib_atomic_shared_ptr != 201711L
+# error "__cpp_lib_atomic_shared_ptr should have the value 201711L in c++26"
# endif
# ifndef __cpp_lib_atomic_value_initialization
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_aliasing.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_aliasing.pass.cpp
index 8c70c371475bd..33bfe02376f0c 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_aliasing.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_aliasing.pass.cpp
@@ -6,7 +6,7 @@
//
//===----------------------------------------------------------------------===//
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// REQUIRES: std-at-least-c++20
// UNSUPPORTED: no-threads
// [util.smartptr.atomic.shared] equivalence checks with aliasing shared_ptr.
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_class.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_class.pass.cpp
index 6eb3ab65fc2e1..68156b9cafb96 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_class.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_class.pass.cpp
@@ -6,7 +6,7 @@
//
//===----------------------------------------------------------------------===//
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// REQUIRES: std-at-least-c++20
// UNSUPPORTED: no-threads
#include <atomic>
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_memory_order.verify.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_memory_order.verify.cpp
index 047c2785cab6e..7b92e1b23dfcd 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_memory_order.verify.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_memory_order.verify.cpp
@@ -7,7 +7,7 @@
//===----------------------------------------------------------------------===//
// REQUIRES: diagnose-if-support
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// REQUIRES: std-at-least-c++20
// UNSUPPORTED: no-threads
#include <atomic>
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_nullptr.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_nullptr.pass.cpp
index 3662c491f72ee..3889e86717948 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_nullptr.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_nullptr.pass.cpp
@@ -6,13 +6,14 @@
//
//===----------------------------------------------------------------------===//
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// REQUIRES: std-at-least-c++20
// UNSUPPORTED: no-threads
// LWG 3661 and LWG 3893 coverage.
#include <atomic>
#include <cassert>
+#include <cstddef> // Armv7, Armv8 (-fno-exceptions) require for std::nullptr_t
#include <memory>
#include <type_traits>
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_refcount.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_refcount.pass.cpp
index 197506cd5b0ac..7957fb7a7b787 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_refcount.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_refcount.pass.cpp
@@ -6,12 +6,13 @@
//
//===----------------------------------------------------------------------===//
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// REQUIRES: std-at-least-c++20
// UNSUPPORTED: no-threads
#include <atomic>
#include <cassert>
#include <memory>
+#include <utility> // Armv7, Armv8 require for std::move
namespace {
struct Tracker {
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_stress.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_stress.pass.cpp
index e02d74931fce6..86b56bfa920bc 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_stress.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_stress.pass.cpp
@@ -6,7 +6,7 @@
//
//===----------------------------------------------------------------------===//
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// REQUIRES: std-at-least-c++20
// UNSUPPORTED: no-threads
#include <array>
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_weak_ptr_class.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_weak_ptr_class.pass.cpp
index 2b0c68234d3f0..9c8f0355d46d4 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_weak_ptr_class.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_weak_ptr_class.pass.cpp
@@ -6,7 +6,7 @@
//
//===----------------------------------------------------------------------===//
-// UNSUPPORTED: c++03, c++11, c++14, c++17
+// REQUIRES: std-at-least-c++20
// UNSUPPORTED: no-threads
#include <atomic>
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index f81eb0e0e8060..119bfb7e540f2 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -205,7 +205,6 @@ def add_version_header(tc):
"name": "__cpp_lib_atomic_shared_ptr",
"values": {"c++20": 201711},
"headers": ["atomic"],
- "unimplemented": True,
},
{
"name": "__cpp_lib_atomic_value_initialization",
>From 064b6f6c428afbeddbfaa840b5f20da0c1a9bd11 Mon Sep 17 00:00:00 2001
From: ViNN280801 <vladislav.semykin at gmail.com>
Date: Mon, 27 Apr 2026 10:51:48 +0300
Subject: [PATCH 03/14] [libcxx] Added nodiscard tests for the
atomic<shared_ptr>::load|::is_lock_free methods; additionaly marked 'load()'
as nodiscard
Signed-off-by: ViNN280801 <vladislav.semykin at gmail.com>
---
libcxx/include/__memory/atomic_shared_ptr.h | 4 ++--
.../test/libcxx/atomics/nodiscard.verify.cpp | 21 +++++++++++++++++++
2 files changed, 23 insertions(+), 2 deletions(-)
diff --git a/libcxx/include/__memory/atomic_shared_ptr.h b/libcxx/include/__memory/atomic_shared_ptr.h
index a4875b9687f45..9b8b5f291bbfb 100644
--- a/libcxx/include/__memory/atomic_shared_ptr.h
+++ b/libcxx/include/__memory/atomic_shared_ptr.h
@@ -237,7 +237,7 @@ struct atomic<shared_ptr<_Tp>> {
__old_c->__release_shared();
}
- _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> load(memory_order __m = memory_order_seq_cst) const noexcept
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> load(memory_order __m = memory_order_seq_cst) const noexcept
_LIBCPP_CHECK_LOAD_MEMORY_ORDER(__m) {
(void)__m;
__fields_.__lock();
@@ -382,7 +382,7 @@ struct atomic<weak_ptr<_Tp>> {
__old_c->__release_weak();
}
- _LIBCPP_HIDE_FROM_ABI weak_ptr<_Tp> load(memory_order __m = memory_order_seq_cst) const noexcept
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI weak_ptr<_Tp> load(memory_order __m = memory_order_seq_cst) const noexcept
_LIBCPP_CHECK_LOAD_MEMORY_ORDER(__m) {
(void)__m;
__fields_.__lock();
diff --git a/libcxx/test/libcxx/atomics/nodiscard.verify.cpp b/libcxx/test/libcxx/atomics/nodiscard.verify.cpp
index c1c2b95ecf89f..642d85ae67723 100644
--- a/libcxx/test/libcxx/atomics/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/atomics/nodiscard.verify.cpp
@@ -11,6 +11,7 @@
// Check that functions are marked [[nodiscard]]
#include <atomic>
+#include <memory>
#include "test_macros.h"
@@ -31,6 +32,26 @@ void test() {
// expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
atRef.load();
}
+
+ {
+ std::atomic<std::shared_ptr<int>> asp;
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ asp.is_lock_free();
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ asp.load();
+ }
+
+ {
+ std::atomic<std::weak_ptr<int>> awp;
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ awp.is_lock_free();
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ awp.load();
+ }
#endif
{
>From d7c61f1dcaece8609d533b8fb65f913a8c5f97b8 Mon Sep 17 00:00:00 2001
From: Vladislav Semykin <34096407+ViNN280801 at users.noreply.github.com>
Date: Tue, 28 Apr 2026 08:39:57 +0300
Subject: [PATCH 04/14] Apply suggestion from @H-G-Hristov
Co-authored-by: Hristo Hristov <hghristov.rmm at gmail.com>
---
libcxx/docs/Status/Cxx20Papers.csv | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv
index 3b1dc43a7fd5b..c57723fa7a309 100644
--- a/libcxx/docs/Status/Cxx20Papers.csv
+++ b/libcxx/docs/Status/Cxx20Papers.csv
@@ -12,7 +12,7 @@
"`P0600R1 <https://wg21.link/P0600R1>`__","nodiscard in the Library","2017-11 (Albuquerque)","|Complete|","16","`#104010 <https://github.com/llvm/llvm-project/issues/104010>`__",""
"`P0616R0 <https://wg21.link/P0616R0>`__","de-pessimize legacy <numeric> algorithms with std::move","2017-11 (Albuquerque)","|Complete|","12","`#104011 <https://github.com/llvm/llvm-project/issues/104011>`__",""
"`P0653R2 <https://wg21.link/P0653R2>`__","Utility to convert a pointer to a raw pointer","2017-11 (Albuquerque)","|Complete|","6","`#104012 <https://github.com/llvm/llvm-project/issues/104012>`__",""
-"`P0718R2 <https://wg21.link/P0718R2>`__","Atomic shared_ptr","2017-11 (Albuquerque)","|Complete|","22","`#99980 <https://github.com/llvm/llvm-project/issues/99980>`__",""
+"`P0718R2 <https://wg21.link/P0718R2>`__","Atomic shared_ptr","2017-11 (Albuquerque)","|Complete|","23","`#99980 <https://github.com/llvm/llvm-project/issues/99980>`__",""
"`P0767R1 <https://wg21.link/P0767R1>`__","Deprecate POD","2017-11 (Albuquerque)","|Complete|","21","`#104013 <https://github.com/llvm/llvm-project/issues/104013>`__","It was previously erroneously marked as complete in LLVM 7."
"`P0768R1 <https://wg21.link/P0768R1>`__","Library Support for the Spaceship (Comparison) Operator","2017-11 (Albuquerque)","|Complete|","","`#104014 <https://github.com/llvm/llvm-project/issues/104014>`__",""
"`P0777R1 <https://wg21.link/P0777R1>`__","Treating Unnecessary ``decay``\ ","2017-11 (Albuquerque)","|Complete|","7","`#104016 <https://github.com/llvm/llvm-project/issues/104016>`__",""
>From 40c38a7949c62023fc80c58f43708b1b01162742 Mon Sep 17 00:00:00 2001
From: Vladislav Semykin <34096407+ViNN280801 at users.noreply.github.com>
Date: Tue, 28 Apr 2026 08:41:14 +0300
Subject: [PATCH 05/14] Update libcxx/docs/Status/Cxx2cIssues.csv
Co-authored-by: Hristo Hristov <hghristov.rmm at gmail.com>
---
libcxx/docs/Status/Cxx2cIssues.csv | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcxx/docs/Status/Cxx2cIssues.csv b/libcxx/docs/Status/Cxx2cIssues.csv
index 0a868e89eba6b..f14240167e729 100644
--- a/libcxx/docs/Status/Cxx2cIssues.csv
+++ b/libcxx/docs/Status/Cxx2cIssues.csv
@@ -3,7 +3,7 @@
"`LWG3884 <https://wg21.link/LWG3884>`__","``flat_foo`` is missing allocator-extended copy/move constructors","2023-06 (Varna)","|Complete|","21","`#105269 <https://github.com/llvm/llvm-project/issues/105269>`__",""
"`LWG3885 <https://wg21.link/LWG3885>`__","``op`` should be in [zombie.names]","2023-06 (Varna)","|Nothing To Do|","","`#105270 <https://github.com/llvm/llvm-project/issues/105270>`__",""
"`LWG3887 <https://wg21.link/LWG3887>`__","Version macro for ``allocate_at_least``","2023-06 (Varna)","|Complete|","19","`#105271 <https://github.com/llvm/llvm-project/issues/105271>`__",""
-"`LWG3893 <https://wg21.link/LWG3893>`__","LWG 3661 broke ``atomic<shared_ptr<T>> a; a = nullptr;``","2023-06 (Varna)","|Complete|","22","`#105273 <https://github.com/llvm/llvm-project/issues/105273>`__",""
+"`LWG3893 <https://wg21.link/LWG3893>`__","LWG 3661 broke ``atomic<shared_ptr<T>> a; a = nullptr;``","2023-06 (Varna)","|Complete|","23","`#105273 <https://github.com/llvm/llvm-project/issues/105273>`__",""
"`LWG3894 <https://wg21.link/LWG3894>`__","``generator::promise_type::yield_value(ranges::elements_of<Rng, Alloc>)`` should not be ``noexcept``","2023-06 (Varna)","","","`#105274 <https://github.com/llvm/llvm-project/issues/105274>`__",""
"`LWG3903 <https://wg21.link/LWG3903>`__","span destructor is redundantly noexcept","2023-06 (Varna)","|Complete|","7","`#105275 <https://github.com/llvm/llvm-project/issues/105275>`__",""
"`LWG3904 <https://wg21.link/LWG3904>`__","``lazy_split_view::outer-iterator``'s const-converting constructor isn't setting ``trailing_empty_``","2023-06 (Varna)","","","`#105276 <https://github.com/llvm/llvm-project/issues/105276>`__",""
>From 4df1f1637e3cf26ca5c02e9f51437ec3463d33c5 Mon Sep 17 00:00:00 2001
From: Vladislav Semykin <34096407+ViNN280801 at users.noreply.github.com>
Date: Tue, 28 Apr 2026 08:41:32 +0300
Subject: [PATCH 06/14] Update libcxx/docs/Status/Cxx23Issues.csv
Co-authored-by: Hristo Hristov <hghristov.rmm at gmail.com>
---
libcxx/docs/Status/Cxx23Issues.csv | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index 7788cd285d997..676968340d420 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -157,7 +157,7 @@
"`LWG3654 <https://wg21.link/LWG3654>`__","``basic_format_context::arg(size_t)`` should be ``noexcept`` ","2022-02 (Virtual)","|Complete|","15","`#104959 <https://github.com/llvm/llvm-project/issues/104959>`__",""
"`LWG3657 <https://wg21.link/LWG3657>`__","``std::hash<std::filesystem::path>`` is not enabled","2022-02 (Virtual)","|Complete|","17","`#104960 <https://github.com/llvm/llvm-project/issues/104960>`__",""
"`LWG3660 <https://wg21.link/LWG3660>`__","``iterator_traits<common_iterator>::pointer`` should conform to ยง[iterator.traits]","2022-02 (Virtual)","|Complete|","14","`#104961 <https://github.com/llvm/llvm-project/issues/104961>`__",""
-"`LWG3661 <https://wg21.link/LWG3661>`__","``constinit atomic<shared_ptr<T>> a(nullptr);`` should work","2022-02 (Virtual)","|Complete|","22","`#104962 <https://github.com/llvm/llvm-project/issues/104962>`__",""
+"`LWG3661 <https://wg21.link/LWG3661>`__","``constinit atomic<shared_ptr<T>> a(nullptr);`` should work","2022-02 (Virtual)","|Complete|","23","`#104962 <https://github.com/llvm/llvm-project/issues/104962>`__",""
"","","","","","",""
"`LWG3564 <https://wg21.link/LWG3564>`__","``transform_view::iterator<true>::value_type`` and ``iterator_category`` should use ``const F&``","2022-07 (Virtual)","|Complete|","20","`#104963 <https://github.com/llvm/llvm-project/issues/104963>`__",""
"`LWG3617 <https://wg21.link/LWG3617>`__","``function``/``packaged_task`` deduction guides and deducing ``this``","2022-07 (Virtual)","","","`#104965 <https://github.com/llvm/llvm-project/issues/104965>`__",""
>From 235aee6c1e3254a8fff9cf4f9df0d7dbf54775b3 Mon Sep 17 00:00:00 2001
From: ViNN280801 <vladislav.semykin at gmail.com>
Date: Tue, 28 Apr 2026 20:56:39 +0300
Subject: [PATCH 07/14] [libcxx] Fix addressof and poll callable in
atomic<shared_ptr>
Use __builtin_addressof instead of & in TSAN macro call sites to avoid
potentially overloaded operator&. Take the poll callable by const lvalue
reference in __atomic_smart_ptr_wait_on_address since it is called
multiple times and does not need to be forwarded.
---
libcxx/include/__memory/atomic_shared_ptr.h | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/libcxx/include/__memory/atomic_shared_ptr.h b/libcxx/include/__memory/atomic_shared_ptr.h
index 9b8b5f291bbfb..1baa0060d3425 100644
--- a/libcxx/include/__memory/atomic_shared_ptr.h
+++ b/libcxx/include/__memory/atomic_shared_ptr.h
@@ -101,7 +101,8 @@ _LIBCPP_HIDE_FROM_ABI inline void __atomic_smart_ptr_notify_all(const void* __ad
}
template <class _Poll>
-_LIBCPP_HIDE_FROM_ABI inline void __atomic_smart_ptr_wait_on_address(const void* __address, _Poll&& __poll) noexcept {
+_LIBCPP_HIDE_FROM_ABI inline void
+__atomic_smart_ptr_wait_on_address(const void* __address, const _Poll& __poll) noexcept {
while (!__poll()) {
# if _LIBCPP_AVAILABILITY_HAS_NEW_SYNC
auto __monitor_value = std::__atomic_monitor_global(__address);
@@ -132,7 +133,7 @@ struct __atomic_smart_ptr_fields {
// Acquire lock bit on __ctrl_. Contended path sets notify bit and waits.
_LIBCPP_HIDE_FROM_ABI void __lock() const noexcept {
- _LIBCPP_ATOMIC_SP_TSAN_PRE_LOCK(&__ctrl_);
+ _LIBCPP_ATOMIC_SP_TSAN_PRE_LOCK(__builtin_addressof(__ctrl_));
uintptr_t __expected = std::__cxx_atomic_load(__builtin_addressof(__ctrl_), memory_order_relaxed);
for (;;) {
if (!__atomic_smart_ptr_storage::__has_lock(__expected)) {
@@ -143,7 +144,7 @@ struct __atomic_smart_ptr_fields {
__desired,
memory_order_acquire,
memory_order_relaxed)) {
- _LIBCPP_ATOMIC_SP_TSAN_POST_LOCK(&__ctrl_);
+ _LIBCPP_ATOMIC_SP_TSAN_POST_LOCK(__builtin_addressof(__ctrl_));
return;
}
continue;
@@ -170,12 +171,12 @@ struct __atomic_smart_ptr_fields {
// Publish new control pointer, clear bits, and notify waiters if needed.
_LIBCPP_HIDE_FROM_ABI void __unlock(__shared_weak_count* __ctrl_to_publish) const noexcept {
- _LIBCPP_ATOMIC_SP_TSAN_PRE_UNLOCK(&__ctrl_);
+ _LIBCPP_ATOMIC_SP_TSAN_PRE_UNLOCK(__builtin_addressof(__ctrl_));
uintptr_t __new_word = __atomic_smart_ptr_storage::__encode(__ctrl_to_publish, 0);
uintptr_t __previous = std::__cxx_atomic_exchange(__builtin_addressof(__ctrl_), __new_word, memory_order_release);
if (__atomic_smart_ptr_storage::__has_notify(__previous))
std::__atomic_smart_ptr_notify_all(__ctrl_address());
- _LIBCPP_ATOMIC_SP_TSAN_POST_UNLOCK(&__ctrl_);
+ _LIBCPP_ATOMIC_SP_TSAN_POST_UNLOCK(__builtin_addressof(__ctrl_));
}
};
>From 45bd0d80342cbb0711df815fb6e615d6f61cd27b Mon Sep 17 00:00:00 2001
From: ViNN280801 <vladislav.semykin at gmail.com>
Date: Tue, 28 Apr 2026 21:26:04 +0300
Subject: [PATCH 08/14] [libcxx] Written 2 simple benchmarks
Signed-off-by: ViNN280801 <vladislav.semykin at gmail.com>
---
.../benchmarks/atomic_shared_ptr.bench.cpp | 34 +++++++++++++++++++
1 file changed, 34 insertions(+)
create mode 100644 libcxx/test/benchmarks/atomic_shared_ptr.bench.cpp
diff --git a/libcxx/test/benchmarks/atomic_shared_ptr.bench.cpp b/libcxx/test/benchmarks/atomic_shared_ptr.bench.cpp
new file mode 100644
index 0000000000000..47239b5c9a613
--- /dev/null
+++ b/libcxx/test/benchmarks/atomic_shared_ptr.bench.cpp
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+#include <atomic>
+#include <memory>
+
+#include "benchmark/benchmark.h"
+
+static void BM_AtomicSharedPtrLoadUncontended(benchmark::State& st) {
+ std::atomic<std::shared_ptr<int>> atom(std::make_shared<int>(42));
+ while (st.KeepRunning()) {
+ auto snap = atom.load();
+ benchmark::DoNotOptimize(snap);
+ }
+}
+BENCHMARK(BM_AtomicSharedPtrLoadUncontended);
+
+static void BM_AtomicSharedPtrStoreUncontended(benchmark::State& st) {
+ std::atomic<std::shared_ptr<int>> atom;
+ auto keep = std::make_shared<int>(7);
+ while (st.KeepRunning()) {
+ atom.store(keep);
+ }
+}
+BENCHMARK(BM_AtomicSharedPtrStoreUncontended);
+
+BENCHMARK_MAIN();
>From b0d0a5a3b7a922c45220a0b7d323afdb79916a61 Mon Sep 17 00:00:00 2001
From: ViNN280801 <vladislav.semykin at gmail.com>
Date: Fri, 1 May 2026 15:37:16 +0300
Subject: [PATCH 09/14] [libcxx] Add per-method tests for atomic<shared_ptr<T>>
and atomic<weak_ptr<T>>; update <memory> synopsis
Signed-off-by: ViNN280801 <vladislav.semykin at gmail.com>
---
libcxx/include/memory | 8 ++
.../test/libcxx/atomics/nodiscard.verify.cpp | 21 ----
.../utilities/memory/nodiscard.verify.cpp | 22 ++++
.../atomic/atomic_smart_ptr_test_types.h | 102 ++++++++++++++++++
.../atomic/is_lock_free.compile.pass.cpp | 50 +++++++++
.../atomic/shared/assign.pass.cpp | 40 +++++++
.../shared/compare_exchange_strong.pass.cpp | 62 +++++++++++
.../shared/compare_exchange_weak.pass.cpp | 64 +++++++++++
.../atomic/shared/convert.pass.cpp | 41 +++++++
.../atomic/shared/exchange.pass.cpp | 45 ++++++++
.../is_always_lock_free.compile.pass.cpp | 40 +++++++
.../atomic/shared/load.compile.pass.cpp | 44 ++++++++
.../atomic/shared/notify_all.pass.cpp | 67 ++++++++++++
.../atomic/shared/notify_one.pass.cpp | 54 ++++++++++
.../atomic/shared/store.pass.cpp | 49 +++++++++
.../atomic/shared/types.compile.pass.cpp | 66 ++++++++++++
.../util.smartptr/atomic/shared/wait.pass.cpp | 58 ++++++++++
.../util.smartptr/atomic/weak/assign.pass.cpp | 50 +++++++++
.../weak/compare_exchange_strong.pass.cpp | 67 ++++++++++++
.../weak/compare_exchange_weak.pass.cpp | 69 ++++++++++++
.../atomic/weak/convert.pass.cpp | 44 ++++++++
.../atomic/weak/exchange.pass.cpp | 57 ++++++++++
.../weak/is_always_lock_free.compile.pass.cpp | 40 +++++++
.../atomic/weak/is_lock_free.compile.pass.cpp | 34 ++++++
.../atomic/weak/load.compile.pass.cpp | 44 ++++++++
.../atomic/weak/notify_all.pass.cpp | 69 ++++++++++++
.../atomic/weak/notify_one.pass.cpp | 57 ++++++++++
.../util.smartptr/atomic/weak/store.pass.cpp | 50 +++++++++
.../atomic/weak/types.compile.pass.cpp | 61 +++++++++++
.../util.smartptr/atomic/weak/wait.pass.cpp | 64 +++++++++++
30 files changed, 1518 insertions(+), 21 deletions(-)
create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/atomic/atomic_smart_ptr_test_types.h
create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/atomic/is_lock_free.compile.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/assign.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/compare_exchange_strong.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/compare_exchange_weak.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/convert.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/exchange.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/is_always_lock_free.compile.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/load.compile.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/notify_all.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/notify_one.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/store.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/types.compile.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/wait.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/assign.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/compare_exchange_strong.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/compare_exchange_weak.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/convert.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/exchange.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/is_always_lock_free.compile.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/is_lock_free.compile.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/load.compile.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/notify_all.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/notify_one.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/store.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/types.compile.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/wait.pass.cpp
diff --git a/libcxx/include/memory b/libcxx/include/memory
index ca880c83a544d..177a7efbb4e10 100644
--- a/libcxx/include/memory
+++ b/libcxx/include/memory
@@ -861,6 +861,14 @@ template<class T>
atomic_compare_exchange_strong_explicit(shared_ptr<T>* p, shared_ptr<T>* v,
shared_ptr<T> w, memory_order success,
memory_order failure);
+
+// [util.smartptr.atomic.shared], partial specialization for shared_ptr
+template <class T>
+struct atomic<shared_ptr<T>>;
+// [util.smartptr.atomic.weak], partial specialization for weak_ptr
+template <class T>
+struct atomic<weak_ptr<T>>;
+
// Hash support
template <class T> struct hash;
template <class T, class D> struct hash<unique_ptr<T, D> >;
diff --git a/libcxx/test/libcxx/atomics/nodiscard.verify.cpp b/libcxx/test/libcxx/atomics/nodiscard.verify.cpp
index 642d85ae67723..c1c2b95ecf89f 100644
--- a/libcxx/test/libcxx/atomics/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/atomics/nodiscard.verify.cpp
@@ -11,7 +11,6 @@
// Check that functions are marked [[nodiscard]]
#include <atomic>
-#include <memory>
#include "test_macros.h"
@@ -32,26 +31,6 @@ void test() {
// expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
atRef.load();
}
-
- {
- std::atomic<std::shared_ptr<int>> asp;
-
- // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
- asp.is_lock_free();
-
- // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
- asp.load();
- }
-
- {
- std::atomic<std::weak_ptr<int>> awp;
-
- // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
- awp.is_lock_free();
-
- // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
- awp.load();
- }
#endif
{
diff --git a/libcxx/test/libcxx/utilities/memory/nodiscard.verify.cpp b/libcxx/test/libcxx/utilities/memory/nodiscard.verify.cpp
index 6bddeceb9951a..5533e9f741983 100644
--- a/libcxx/test/libcxx/utilities/memory/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/utilities/memory/nodiscard.verify.cpp
@@ -124,4 +124,26 @@ void test() {
it.base(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
}
#endif
+
+#if TEST_STD_VER >= 20 && !defined(TEST_HAS_NO_THREADS)
+ {
+ std::atomic<std::shared_ptr<int>> asp;
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ asp.is_lock_free();
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ asp.load();
+ }
+
+ {
+ std::atomic<std::weak_ptr<int>> awp;
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ awp.is_lock_free();
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ awp.load();
+ }
+#endif
}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/atomic_smart_ptr_test_types.h b/libcxx/test/std/utilities/memory/util.smartptr/atomic/atomic_smart_ptr_test_types.h
new file mode 100644
index 0000000000000..309cc11bb5026
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/atomic_smart_ptr_test_types.h
@@ -0,0 +1,102 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Shared fixtures for libc++ tests under util.smartptr/atomic/{shared,weak}/:
+// heterogeneous value types (built-in, standard library, and user-defined).
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LIBCXX_TEST_STD_UTILITIES_MEMORY_UTIL_SMARTPTR_ATOMIC_ATOMIC_SMART_PTR_TEST_TYPES_H
+#define LIBCXX_TEST_STD_UTILITIES_MEMORY_UTIL_SMARTPTR_ATOMIC_ATOMIC_SMART_PTR_TEST_TYPES_H
+
+#include <cstdint>
+#include <memory>
+#include <string>
+
+namespace libcxx_atomic_smart_ptr_test {
+
+// --- User-defined and semi-random scalar-like types ---------------------------------
+
+struct TrackedPod {
+ std::uint32_t gen{};
+ std::int64_t salt{};
+ friend constexpr bool operator==(TrackedPod lhs, TrackedPod rhs) noexcept {
+ return lhs.gen == rhs.gen && lhs.salt == rhs.salt;
+ }
+};
+
+class Handle {
+ double coeff_{};
+
+public:
+ explicit Handle(double c = 0.0) : coeff_(c) {}
+ double coeff() const noexcept { return coeff_; }
+ friend bool operator==(Handle const& lhs, Handle const& rhs) noexcept { return lhs.coeff_ == rhs.coeff_; }
+};
+
+enum class Flag : std::uint16_t { Off = 0, Stale = 4099, On = 60000 };
+
+// --- Distinct shared states for compare/exchange style tests ------------------------
+
+template <class T>
+struct SpValues;
+
+template <>
+struct SpValues<int> {
+ static std::shared_ptr<int> state_a() { return std::make_shared<int>(-90210); }
+ static std::shared_ptr<int> state_b() { return std::make_shared<int>(404); }
+ static std::shared_ptr<int> state_c() { return std::make_shared<int>(7331); }
+};
+
+template <>
+struct SpValues<double> {
+ static std::shared_ptr<double> state_a() { return std::make_shared<double>(1.4142135623730951); }
+ static std::shared_ptr<double> state_b() { return std::make_shared<double>(2.7182818284590452); }
+ static std::shared_ptr<double> state_c() { return std::make_shared<double>(3.1415926535897932); }
+};
+
+template <>
+struct SpValues<std::string> {
+ static std::shared_ptr<std::string> state_a() { return std::make_shared<std::string>("kappa"); }
+ static std::shared_ptr<std::string> state_b() { return std::make_shared<std::string>("lambda"); }
+ static std::shared_ptr<std::string> state_c() { return std::make_shared<std::string>("mu"); }
+};
+
+template <>
+struct SpValues<TrackedPod> {
+ static std::shared_ptr<TrackedPod> state_a() { return std::make_shared<TrackedPod>(TrackedPod{3u, -77L}); }
+ static std::shared_ptr<TrackedPod> state_b() { return std::make_shared<TrackedPod>(TrackedPod{101u, 1L << 20}); }
+ static std::shared_ptr<TrackedPod> state_c() { return std::make_shared<TrackedPod>(TrackedPod{255u, -1L}); }
+};
+
+template <>
+struct SpValues<Handle> {
+ static std::shared_ptr<Handle> state_a() { return std::make_shared<Handle>(0.125); }
+ static std::shared_ptr<Handle> state_b() { return std::make_shared<Handle>(-4096.5); }
+ static std::shared_ptr<Handle> state_c() { return std::make_shared<Handle>(8192.25); }
+};
+
+template <>
+struct SpValues<Flag> {
+ static std::shared_ptr<Flag> state_a() { return std::make_shared<Flag>(Flag::Off); }
+ static std::shared_ptr<Flag> state_b() { return std::make_shared<Flag>(Flag::Stale); }
+ static std::shared_ptr<Flag> state_c() { return std::make_shared<Flag>(Flag::On); }
+};
+
+} // namespace libcxx_atomic_smart_ptr_test
+
+// Instantiate the same runtime coverage for every value type.
+#define LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(M) \
+ M(int) \
+ M(double) \
+ M(std::string) \
+ M(libcxx_atomic_smart_ptr_test::TrackedPod) \
+ M(libcxx_atomic_smart_ptr_test::Handle) \
+ M(libcxx_atomic_smart_ptr_test::Flag)
+
+#endif // LIBCXX_TEST_STD_UTILITIES_MEMORY_UTIL_SMARTPTR_ATOMIC_ATOMIC_SMART_PTR_TEST_TYPES_H
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/is_lock_free.compile.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/is_lock_free.compile.pass.cpp
new file mode 100644
index 0000000000000..71464f598f5eb
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/is_lock_free.compile.pass.cpp
@@ -0,0 +1,50 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+// UNSUPPORTED: no-threads
+
+// [util.smartptr.atomic.shared]
+// bool is_lock_free() const noexcept;
+
+// [util.smartptr.atomic.weak]
+// bool is_lock_free() const noexcept;
+
+#include <atomic>
+#include <memory>
+
+#include "atomic_smart_ptr_test_types.h"
+#include "test_macros.h"
+
+template <class T>
+void check(const std::atomic<std::shared_ptr<T>>& asp) noexcept {
+ static_assert(!std::atomic<std::shared_ptr<T>>::is_always_lock_free);
+ std::same_as<bool> decltype(auto) lf = asp.is_lock_free();
+ (void)lf;
+ ASSERT_SAME_TYPE(decltype(asp.is_lock_free()), bool);
+ ASSERT_NOEXCEPT(asp.is_lock_free());
+}
+
+template <class T>
+void check(const std::atomic<std::weak_ptr<T>>& awp) noexcept {
+ static_assert(!std::atomic<std::weak_ptr<T>>::is_always_lock_free);
+ std::same_as<bool> decltype(auto) lf = awp.is_lock_free();
+ (void)lf;
+ ASSERT_SAME_TYPE(decltype(awp.is_lock_free()), bool);
+ ASSERT_NOEXCEPT(awp.is_lock_free());
+}
+
+int main(int, char**) {
+#define LIBCXX_ATOMIC_SP_RUN_IS_LOCK_FREE(T) check<T>(std::atomic<std::shared_ptr<T>>());
+ LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_IS_LOCK_FREE)
+#undef LIBCXX_ATOMIC_SP_RUN_IS_LOCK_FREE
+#define LIBCXX_ATOMIC_SP_RUN_W_ILF(T) check<T>(std::atomic<std::weak_ptr<T>>());
+ LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_W_ILF)
+#undef LIBCXX_ATOMIC_SP_RUN_W_ILF
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/assign.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/assign.pass.cpp
new file mode 100644
index 0000000000000..3a39c30cb7413
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/assign.pass.cpp
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+// UNSUPPORTED: no-threads
+
+// void operator=(shared_ptr<T>) noexcept;
+// void operator=(nullptr_t) noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <memory>
+
+#include "../atomic_smart_ptr_test_types.h"
+#include "test_macros.h"
+
+template <class T>
+void test_assign() {
+ std::atomic<std::shared_ptr<T>> a;
+ auto p = libcxx_atomic_smart_ptr_test::SpValues<T>::state_a();
+ a = std::shared_ptr<T>(p);
+ assert(a.load().get() == p.get());
+ assert(*a.load() == *p);
+ a = nullptr;
+ assert(!a.load());
+ ASSERT_NOEXCEPT(a = nullptr);
+ ASSERT_NOEXCEPT(a = std::shared_ptr<T>(p));
+}
+
+int main(int, char**) {
+#define LIBCXX_ATOMIC_SP_RUN_ASSIGN(T) test_assign<T>();
+ LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_ASSIGN)
+#undef LIBCXX_ATOMIC_SP_RUN_ASSIGN
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/compare_exchange_strong.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/compare_exchange_strong.pass.cpp
new file mode 100644
index 0000000000000..5ceca3efad536
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/compare_exchange_strong.pass.cpp
@@ -0,0 +1,62 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+// UNSUPPORTED: no-threads
+
+// bool compare_exchange_strong(shared_ptr<T>&, shared_ptr<T>, memory_order, memory_order) noexcept;
+// bool compare_exchange_strong(shared_ptr<T>&, shared_ptr<T>, memory_order = memory_order_seq_cst) noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <memory>
+
+#include "../atomic_smart_ptr_test_types.h"
+#include "test_macros.h"
+
+template <class T>
+void test_compare_exchange_strong() {
+ using libcxx_atomic_smart_ptr_test::SpValues;
+ auto p1 = SpValues<T>::state_a();
+ auto p2 = SpValues<T>::state_b();
+ std::atomic<std::shared_ptr<T>> a((std::shared_ptr<T>(p1)));
+
+ {
+ std::shared_ptr<T> expected = p1;
+ std::same_as<bool> decltype(auto) ok = a.compare_exchange_strong(expected, std::shared_ptr<T>(p2));
+ assert(ok);
+ assert(expected.get() == p1.get());
+ assert(*a.load() == *p2);
+ ASSERT_NOEXCEPT(a.compare_exchange_strong(expected, std::shared_ptr<T>(p2)));
+ }
+
+ {
+ std::shared_ptr<T> expected = p1;
+ bool ok =
+ a.compare_exchange_strong(expected, std::make_shared<T>(*SpValues<T>::state_c()), std::memory_order_seq_cst);
+ assert(!ok);
+ assert(*expected == *p2);
+ assert(*a.load() == *p2);
+ }
+
+ {
+ std::shared_ptr<T> expected = p2;
+ const std::shared_ptr<T> next = SpValues<T>::state_c();
+ bool ok = a.compare_exchange_strong(
+ expected, std::shared_ptr<T>(next), std::memory_order_release, std::memory_order_relaxed);
+ assert(ok);
+ assert(*a.load() == *next);
+ }
+}
+
+int main(int, char**) {
+#define LIBCXX_ATOMIC_SP_RUN_CXS(T) test_compare_exchange_strong<T>();
+ LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_CXS)
+#undef LIBCXX_ATOMIC_SP_RUN_CXS
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/compare_exchange_weak.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/compare_exchange_weak.pass.cpp
new file mode 100644
index 0000000000000..713d04b7f5c47
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/compare_exchange_weak.pass.cpp
@@ -0,0 +1,64 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+// UNSUPPORTED: no-threads
+
+// bool compare_exchange_weak(shared_ptr<T>&, shared_ptr<T>, memory_order, memory_order) noexcept;
+// bool compare_exchange_weak(shared_ptr<T>&, shared_ptr<T>, memory_order = memory_order_seq_cst) noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <memory>
+
+#include "../atomic_smart_ptr_test_types.h"
+#include "test_macros.h"
+
+template <class T>
+void test_compare_exchange_weak() {
+ using libcxx_atomic_smart_ptr_test::SpValues;
+ auto p1 = SpValues<T>::state_a();
+ auto p2 = SpValues<T>::state_b();
+ std::atomic<std::shared_ptr<T>> a((std::shared_ptr<T>(p1)));
+
+ {
+ std::shared_ptr<T> expected = p1;
+ bool ok = false;
+ while (!ok)
+ ok = a.compare_exchange_weak(expected, std::shared_ptr<T>(p2));
+ assert(ok);
+ assert(*a.load() == *p2);
+ ASSERT_NOEXCEPT(a.compare_exchange_weak(expected, std::shared_ptr<T>(p2)));
+ }
+
+ {
+ std::shared_ptr<T> expected = p1;
+ bool ok =
+ a.compare_exchange_weak(expected, std::make_shared<T>(*SpValues<T>::state_c()), std::memory_order_seq_cst);
+ assert(!ok);
+ assert(*expected == *p2);
+ }
+
+ {
+ std::shared_ptr<T> expected = p2;
+ const std::shared_ptr<T> next = SpValues<T>::state_c();
+ bool ok = false;
+ while (!ok)
+ ok = a.compare_exchange_weak(
+ expected, std::shared_ptr<T>(next), std::memory_order_release, std::memory_order_relaxed);
+ assert(ok);
+ assert(*a.load() == *next);
+ }
+}
+
+int main(int, char**) {
+#define LIBCXX_ATOMIC_SP_RUN_CXW(T) test_compare_exchange_weak<T>();
+ LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_CXW)
+#undef LIBCXX_ATOMIC_SP_RUN_CXW
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/convert.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/convert.pass.cpp
new file mode 100644
index 0000000000000..4c219ae175d18
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/convert.pass.cpp
@@ -0,0 +1,41 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+// UNSUPPORTED: no-threads
+
+// operator shared_ptr<T>() const noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <memory>
+
+#include "../atomic_smart_ptr_test_types.h"
+#include "test_macros.h"
+
+template <class T>
+void test_convert() {
+ auto p = libcxx_atomic_smart_ptr_test::SpValues<T>::state_a();
+ const std::atomic<std::shared_ptr<T>> a((std::shared_ptr<T>(p)));
+
+ std::shared_ptr<T> s = a;
+ assert(s.get() == p.get());
+ assert(*s == *p);
+
+ std::same_as<std::shared_ptr<T>> decltype(auto) s2 = static_cast<std::shared_ptr<T>>(a);
+ assert(s2.get() == p.get());
+
+ ASSERT_NOEXCEPT(static_cast<std::shared_ptr<T>>(a));
+}
+
+int main(int, char**) {
+#define LIBCXX_ATOMIC_SP_RUN_CONVERT(T) test_convert<T>();
+ LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_CONVERT)
+#undef LIBCXX_ATOMIC_SP_RUN_CONVERT
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/exchange.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/exchange.pass.cpp
new file mode 100644
index 0000000000000..fa957d8ff7ebe
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/exchange.pass.cpp
@@ -0,0 +1,45 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+// UNSUPPORTED: no-threads
+
+// shared_ptr<T> exchange(shared_ptr<T> desired, memory_order order = memory_order::seq_cst) noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <memory>
+
+#include "../atomic_smart_ptr_test_types.h"
+#include "test_macros.h"
+
+template <class T>
+void test_exchange() {
+ using libcxx_atomic_smart_ptr_test::SpValues;
+ auto p1 = SpValues<T>::state_a();
+ auto p2 = SpValues<T>::state_b();
+ std::atomic<std::shared_ptr<T>> a((std::shared_ptr<T>(p1)));
+
+ std::same_as<std::shared_ptr<T>> decltype(auto) out = a.exchange(std::shared_ptr<T>(p2));
+ assert(*out == *p1);
+ assert(out.get() == p1.get());
+ assert(*a.load() == *p2);
+
+ out = a.exchange(nullptr, std::memory_order_seq_cst);
+ assert(*out == *p2);
+ assert(!a.load());
+
+ ASSERT_NOEXCEPT(a.exchange(nullptr));
+}
+
+int main(int, char**) {
+#define LIBCXX_ATOMIC_SP_RUN_EXCHANGE(T) test_exchange<T>();
+ LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_EXCHANGE)
+#undef LIBCXX_ATOMIC_SP_RUN_EXCHANGE
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/is_always_lock_free.compile.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/is_always_lock_free.compile.pass.cpp
new file mode 100644
index 0000000000000..b7ff230832d09
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/is_always_lock_free.compile.pass.cpp
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+// UNSUPPORTED: no-threads
+
+// static constexpr bool is_always_lock_free;
+// bool is_lock_free() const noexcept;
+
+#include <atomic>
+#include <memory>
+
+#include "../atomic_smart_ptr_test_types.h"
+#include "test_macros.h"
+
+template <class T>
+void check() {
+ using A = std::atomic<std::shared_ptr<T>>;
+
+ static_assert(std::same_as<decltype(A::is_always_lock_free), const bool>);
+ static_assert(!static_cast<bool>(A::is_always_lock_free));
+
+ const A asp;
+ std::same_as<bool> decltype(auto) lf = asp.is_lock_free();
+ (void)lf;
+ ASSERT_NOEXCEPT(A::is_always_lock_free);
+ ASSERT_NOEXCEPT(asp.is_lock_free());
+}
+
+int main(int, char**) {
+#define LIBCXX_ATOMIC_SP_RUN_IS_ALWAYS_LF(T) check<T>();
+ LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_IS_ALWAYS_LF)
+#undef LIBCXX_ATOMIC_SP_RUN_IS_ALWAYS_LF
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/load.compile.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/load.compile.pass.cpp
new file mode 100644
index 0000000000000..48bcb81ab6cce
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/load.compile.pass.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+// UNSUPPORTED: no-threads
+
+// shared_ptr<T> load(memory_order order = memory_order::seq_cst) const noexcept;
+
+#include <atomic>
+#include <memory>
+
+#include "../atomic_smart_ptr_test_types.h"
+#include "test_macros.h"
+
+template <class T>
+void check(const std::atomic<std::shared_ptr<T>>& asp) {
+ std::same_as<std::shared_ptr<T>> decltype(auto) no_arg = asp.load();
+ ASSERT_SAME_TYPE(decltype(asp.load()), std::shared_ptr<T>);
+ ASSERT_NOEXCEPT(asp.load());
+
+ std::same_as<std::shared_ptr<T>> decltype(auto) with_order = asp.load(std::memory_order_seq_cst);
+ ASSERT_SAME_TYPE(decltype(asp.load(std::memory_order_acquire)), std::shared_ptr<T>);
+ ASSERT_NOEXCEPT(asp.load(std::memory_order_seq_cst));
+ static_cast<void>(no_arg);
+ static_cast<void>(with_order);
+
+ {
+ const std::atomic<std::shared_ptr<T>> const_a;
+ static_assert(noexcept(const_a.load()));
+ ASSERT_SAME_TYPE(decltype(const_a.load()), std::shared_ptr<T>);
+ }
+}
+
+int main(int, char**) {
+#define LIBCXX_ATOMIC_SP_RUN_LOAD(T) check<T>(std::atomic<std::shared_ptr<T>>());
+ LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_LOAD)
+#undef LIBCXX_ATOMIC_SP_RUN_LOAD
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/notify_all.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/notify_all.pass.cpp
new file mode 100644
index 0000000000000..5d4c9b7714b23
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/notify_all.pass.cpp
@@ -0,0 +1,67 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+// UNSUPPORTED: no-threads
+
+// void notify_all() noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <memory>
+#include <thread>
+
+#include "../atomic_smart_ptr_test_types.h"
+#include "test_macros.h"
+
+template <class T>
+void test_notify_all() {
+ using libcxx_atomic_smart_ptr_test::SpValues;
+ std::atomic<std::shared_ptr<T>> a;
+
+ ASSERT_NOEXCEPT(a.notify_all());
+
+#if __cpp_lib_atomic_wait >= 201907L
+ auto p1 = SpValues<T>::state_a();
+ a.store(std::shared_ptr<T>(p1));
+
+ std::atomic<int> phase{0};
+ std::thread t1([&] {
+ std::shared_ptr<T> old = a.load();
+ phase.fetch_add(1, std::memory_order_acq_rel);
+ while (phase.load(std::memory_order_acquire) < 2) {
+ std::this_thread::yield();
+ }
+ a.wait(old);
+ });
+ std::thread t2([&] {
+ std::shared_ptr<T> old = a.load();
+ phase.fetch_add(1, std::memory_order_acq_rel);
+ while (phase.load(std::memory_order_acquire) < 2) {
+ std::this_thread::yield();
+ }
+ a.wait(old);
+ });
+
+ while (phase.load(std::memory_order_acquire) < 2) {
+ std::this_thread::yield();
+ }
+
+ a.store(std::make_shared<T>(*SpValues<T>::state_b()));
+ a.notify_all();
+ t1.join();
+ t2.join();
+#endif
+}
+
+int main(int, char**) {
+#define LIBCXX_ATOMIC_SP_RUN_NOTIFY_ALL(T) test_notify_all<T>();
+ LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_NOTIFY_ALL)
+#undef LIBCXX_ATOMIC_SP_RUN_NOTIFY_ALL
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/notify_one.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/notify_one.pass.cpp
new file mode 100644
index 0000000000000..b178fb13d1a0f
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/notify_one.pass.cpp
@@ -0,0 +1,54 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+// UNSUPPORTED: no-threads
+
+// void notify_one() noexcept;
+
+#include <atomic>
+#include <memory>
+#include <thread>
+
+#include "../atomic_smart_ptr_test_types.h"
+#include "test_macros.h"
+
+template <class T>
+void test_notify_one() {
+ using libcxx_atomic_smart_ptr_test::SpValues;
+ std::atomic<std::shared_ptr<T>> a;
+
+ ASSERT_NOEXCEPT(a.notify_one());
+
+#if __cpp_lib_atomic_wait >= 201907L
+ auto p1 = SpValues<T>::state_a();
+ a.store(std::shared_ptr<T>(p1));
+
+ std::atomic<bool> started{false};
+ std::thread t([&] {
+ std::shared_ptr<T> old = a.load();
+ started.store(true, std::memory_order_release);
+ a.wait(old);
+ });
+
+ while (!started.load(std::memory_order_acquire)) {
+ std::this_thread::yield();
+ }
+
+ a.store(std::make_shared<T>(*SpValues<T>::state_b()));
+ a.notify_one();
+ t.join();
+#endif
+}
+
+int main(int, char**) {
+#define LIBCXX_ATOMIC_SP_RUN_NOTIFY_ONE(T) test_notify_one<T>();
+ LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_NOTIFY_ONE)
+#undef LIBCXX_ATOMIC_SP_RUN_NOTIFY_ONE
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/store.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/store.pass.cpp
new file mode 100644
index 0000000000000..9ab808cbd0fae
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/store.pass.cpp
@@ -0,0 +1,49 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+// UNSUPPORTED: no-threads
+
+// void store(shared_ptr<T> desired, memory_order order = memory_order::seq_cst) noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <memory>
+
+#include "../atomic_smart_ptr_test_types.h"
+#include "test_macros.h"
+
+template <class T>
+void test_store() {
+ using libcxx_atomic_smart_ptr_test::SpValues;
+ std::atomic<std::shared_ptr<T>> a;
+
+ auto p = SpValues<T>::state_a();
+ a.store(std::shared_ptr<T>(p));
+ assert(a.load().get() == p.get());
+ assert(*a.load() == *p);
+
+ a.store(std::make_shared<T>(*SpValues<T>::state_b()));
+ assert(*a.load() == *SpValues<T>::state_b());
+
+ a.store(nullptr, std::memory_order_relaxed);
+ assert(!a.load());
+
+ ASSERT_NOEXCEPT(a.store(nullptr));
+ {
+ std::shared_ptr<T> desired = std::make_shared<T>(*SpValues<T>::state_c());
+ ASSERT_NOEXCEPT(a.store(std::move(desired), std::memory_order_seq_cst));
+ }
+}
+
+int main(int, char**) {
+#define LIBCXX_ATOMIC_SP_RUN_STORE(T) test_store<T>();
+ LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_STORE)
+#undef LIBCXX_ATOMIC_SP_RUN_STORE
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/types.compile.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/types.compile.pass.cpp
new file mode 100644
index 0000000000000..39a49c810f9a4
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/types.compile.pass.cpp
@@ -0,0 +1,66 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+// UNSUPPORTED: no-threads
+
+// using value_type = shared_ptr<T>;
+// static constexpr bool is_always_lock_free = false;
+// atomic() noexcept;
+// constexpr atomic(nullptr_t) noexcept;
+// atomic(shared_ptr<T>) noexcept;
+// atomic(const atomic&) = delete;
+// atomic& operator=(const atomic&) = delete;
+
+#include <atomic>
+#include <cassert>
+#include <memory>
+#include <type_traits>
+
+#include "../atomic_smart_ptr_test_types.h"
+#include "test_macros.h"
+
+template <class T>
+void traits() {
+ using A = std::atomic<std::shared_ptr<T>>;
+
+ static_assert(std::same_as<typename A::value_type, std::shared_ptr<T>>);
+ static_assert(!A::is_always_lock_free);
+
+ static_assert(!std::is_copy_constructible_v<A>);
+ static_assert(!std::is_copy_assignable_v<A>);
+
+ static_assert(std::is_nothrow_default_constructible_v<A>);
+ static_assert(std::is_nothrow_constructible_v<A, std::nullptr_t>);
+
+ {
+ A a;
+ assert(!a.load());
+ }
+
+ {
+ auto p = libcxx_atomic_smart_ptr_test::SpValues<T>::state_a();
+ A a((std::shared_ptr<T>(p)));
+ assert(a.load().get() == p.get());
+ assert(*a.load() == *p);
+ }
+}
+
+int main(int, char**) {
+#define LIBCXX_ATOMIC_SP_RUN_TRAITS(T) traits<T>();
+ LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_TRAITS)
+#undef LIBCXX_ATOMIC_SP_RUN_TRAITS
+
+ {
+ static_assert(std::is_nothrow_constructible_v<std::atomic<std::shared_ptr<int>>, std::nullptr_t>);
+ std::atomic<std::shared_ptr<int>> a(nullptr);
+ assert(!a.load());
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/wait.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/wait.pass.cpp
new file mode 100644
index 0000000000000..fef98a7008c27
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/wait.pass.cpp
@@ -0,0 +1,58 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+// UNSUPPORTED: no-threads
+
+// void wait(shared_ptr<T> old, memory_order order = memory_order::seq_cst) const noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <memory>
+#include <thread>
+
+#include "../atomic_smart_ptr_test_types.h"
+#include "test_macros.h"
+
+template <class T>
+void test_wait() {
+ using libcxx_atomic_smart_ptr_test::SpValues;
+ std::atomic<std::shared_ptr<T>> a;
+
+#if __cpp_lib_atomic_wait >= 201907L
+ auto p1 = SpValues<T>::state_a();
+ a.store(std::shared_ptr<T>(p1));
+
+ std::atomic<bool> started{false};
+ std::thread t([&] {
+ std::shared_ptr<T> old = a.load();
+ started.store(true, std::memory_order_release);
+ a.wait(old);
+ });
+
+ while (!started.load(std::memory_order_acquire)) {
+ std::this_thread::yield();
+ }
+
+ auto pWake = std::make_shared<T>(*SpValues<T>::state_c());
+ a.store(std::move(pWake));
+ a.notify_all();
+ t.join();
+
+ assert(*a.load() == *SpValues<T>::state_c());
+#endif
+
+ ASSERT_NOEXCEPT(a.wait(std::shared_ptr<T>(), std::memory_order_seq_cst));
+}
+
+int main(int, char**) {
+#define LIBCXX_ATOMIC_SP_RUN_WAIT(T) test_wait<T>();
+ LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_WAIT)
+#undef LIBCXX_ATOMIC_SP_RUN_WAIT
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/assign.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/assign.pass.cpp
new file mode 100644
index 0000000000000..efe373d1d8036
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/assign.pass.cpp
@@ -0,0 +1,50 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+// UNSUPPORTED: no-threads
+
+// void operator=(weak_ptr<T>) noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <memory>
+
+#include "../atomic_smart_ptr_test_types.h"
+#include "test_macros.h"
+
+template <class T>
+void test_assign_weak() {
+ using libcxx_atomic_smart_ptr_test::SpValues;
+ auto sp1 = SpValues<T>::state_a();
+ auto sp2 = SpValues<T>::state_b();
+ std::weak_ptr<T> wp1 = sp1;
+ std::weak_ptr<T> wp2 = sp2;
+
+ std::atomic<std::weak_ptr<T>> a;
+ a = std::weak_ptr<T>(wp1);
+ {
+ auto locked = a.load().lock();
+ assert(locked && *locked == *sp1);
+ }
+
+ a = std::weak_ptr<T>(wp2);
+ {
+ auto locked = a.load().lock();
+ assert(locked && *locked == *sp2);
+ }
+
+ ASSERT_NOEXCEPT(a = std::weak_ptr<T>(wp1));
+}
+
+int main(int, char**) {
+#define LIBCXX_ATOMIC_SP_RUN_W_ASSIGN(T) test_assign_weak<T>();
+ LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_W_ASSIGN)
+#undef LIBCXX_ATOMIC_SP_RUN_W_ASSIGN
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/compare_exchange_strong.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/compare_exchange_strong.pass.cpp
new file mode 100644
index 0000000000000..61047d17d264b
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/compare_exchange_strong.pass.cpp
@@ -0,0 +1,67 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+// UNSUPPORTED: no-threads
+
+// bool compare_exchange_strong(weak_ptr<T>&, weak_ptr<T>, memory_order, memory_order) noexcept;
+// bool compare_exchange_strong(weak_ptr<T>&, weak_ptr<T>, memory_order = memory_order_seq_cst) noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <memory>
+
+#include "../atomic_smart_ptr_test_types.h"
+#include "test_macros.h"
+
+template <class T>
+void test_compare_exchange_strong_weakptr() {
+ using libcxx_atomic_smart_ptr_test::SpValues;
+ auto sp1 = SpValues<T>::state_a();
+ auto sp2 = SpValues<T>::state_b();
+ std::weak_ptr<T> wp1 = sp1;
+ std::weak_ptr<T> wp2 = sp2;
+
+ std::atomic<std::weak_ptr<T>> a((std::weak_ptr<T>(wp1)));
+
+ {
+ std::weak_ptr<T> expected = wp1;
+ std::same_as<bool> decltype(auto) ok = a.compare_exchange_strong(expected, std::weak_ptr<T>(wp2));
+ assert(ok);
+ {
+ auto locked = a.load().lock();
+ assert(locked && *locked == *sp2);
+ }
+ ASSERT_NOEXCEPT(a.compare_exchange_strong(expected, std::weak_ptr<T>(wp2)));
+ }
+
+ {
+ std::weak_ptr<T> expected = wp1;
+ bool ok = a.compare_exchange_strong(expected, std::weak_ptr<T>(wp1), std::memory_order_seq_cst);
+ assert(!ok);
+ auto locked = expected.lock();
+ assert(locked && *locked == *sp2);
+ }
+
+ {
+ std::weak_ptr<T> expected = wp2;
+ auto sp3 = SpValues<T>::state_c();
+ bool ok = a.compare_exchange_strong(
+ expected, std::weak_ptr<T>(sp3), std::memory_order_release, std::memory_order_relaxed);
+ assert(ok);
+ auto locked = a.load().lock();
+ assert(locked && *locked == *sp3);
+ }
+}
+
+int main(int, char**) {
+#define LIBCXX_ATOMIC_SP_RUN_W_CXS(T) test_compare_exchange_strong_weakptr<T>();
+ LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_W_CXS)
+#undef LIBCXX_ATOMIC_SP_RUN_W_CXS
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/compare_exchange_weak.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/compare_exchange_weak.pass.cpp
new file mode 100644
index 0000000000000..f44450b46476c
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/compare_exchange_weak.pass.cpp
@@ -0,0 +1,69 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+// UNSUPPORTED: no-threads
+
+// bool compare_exchange_weak(weak_ptr<T>&, weak_ptr<T>, memory_order, memory_order) noexcept;
+// bool compare_exchange_weak(weak_ptr<T>&, weak_ptr<T>, memory_order = memory_order_seq_cst) noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <memory>
+
+#include "../atomic_smart_ptr_test_types.h"
+#include "test_macros.h"
+
+template <class T>
+void test_compare_exchange_weak_weakptr() {
+ using libcxx_atomic_smart_ptr_test::SpValues;
+ auto sp1 = SpValues<T>::state_a();
+ auto sp2 = SpValues<T>::state_b();
+ std::weak_ptr<T> wp1 = sp1;
+ std::weak_ptr<T> wp2 = sp2;
+
+ std::atomic<std::weak_ptr<T>> a((std::weak_ptr<T>(wp1)));
+
+ {
+ std::weak_ptr<T> expected = wp1;
+ bool ok = false;
+ while (!ok)
+ ok = a.compare_exchange_weak(expected, std::weak_ptr<T>(wp2));
+ assert(ok);
+ auto locked = a.load().lock();
+ assert(locked && *locked == *sp2);
+ ASSERT_NOEXCEPT(a.compare_exchange_weak(expected, std::weak_ptr<T>(wp2)));
+ }
+
+ {
+ std::weak_ptr<T> expected = wp1;
+ bool ok = a.compare_exchange_weak(expected, std::weak_ptr<T>(wp1), std::memory_order_seq_cst);
+ assert(!ok);
+ auto locked = expected.lock();
+ assert(locked && *locked == *sp2);
+ }
+
+ {
+ std::weak_ptr<T> expected = wp2;
+ auto sp3 = SpValues<T>::state_c();
+ bool ok = false;
+ while (!ok)
+ ok = a.compare_exchange_weak(
+ expected, std::weak_ptr<T>(sp3), std::memory_order_release, std::memory_order_relaxed);
+ assert(ok);
+ auto locked = a.load().lock();
+ assert(locked && *locked == *sp3);
+ }
+}
+
+int main(int, char**) {
+#define LIBCXX_ATOMIC_SP_RUN_W_CXW(T) test_compare_exchange_weak_weakptr<T>();
+ LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_W_CXW)
+#undef LIBCXX_ATOMIC_SP_RUN_W_CXW
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/convert.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/convert.pass.cpp
new file mode 100644
index 0000000000000..c74d5f4925641
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/convert.pass.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+// UNSUPPORTED: no-threads
+
+// operator weak_ptr<T>() const noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <memory>
+
+#include "../atomic_smart_ptr_test_types.h"
+#include "test_macros.h"
+
+template <class T>
+void test_convert_weak() {
+ using libcxx_atomic_smart_ptr_test::SpValues;
+ auto sp = SpValues<T>::state_a();
+ std::weak_ptr<T> wp = sp;
+ const std::atomic<std::weak_ptr<T>> a((std::weak_ptr<T>(wp)));
+
+ std::weak_ptr<T> w = a;
+ auto locked = w.lock();
+ assert(locked && *locked == *sp);
+
+ std::same_as<std::weak_ptr<T>> decltype(auto) w2 = static_cast<std::weak_ptr<T>>(a);
+ auto locked2 = w2.lock();
+ assert(locked2 && *locked2 == *sp);
+
+ ASSERT_NOEXCEPT(static_cast<std::weak_ptr<T>>(a));
+}
+
+int main(int, char**) {
+#define LIBCXX_ATOMIC_SP_RUN_W_CONVERT(T) test_convert_weak<T>();
+ LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_W_CONVERT)
+#undef LIBCXX_ATOMIC_SP_RUN_W_CONVERT
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/exchange.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/exchange.pass.cpp
new file mode 100644
index 0000000000000..2c777efaabb5b
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/exchange.pass.cpp
@@ -0,0 +1,57 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+// UNSUPPORTED: no-threads
+
+// weak_ptr<T> exchange(weak_ptr<T> desired, memory_order order = memory_order::seq_cst) noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <memory>
+
+#include "../atomic_smart_ptr_test_types.h"
+#include "test_macros.h"
+
+template <class T>
+void test_exchange_weak() {
+ using libcxx_atomic_smart_ptr_test::SpValues;
+ auto sp1 = SpValues<T>::state_a();
+ auto sp2 = SpValues<T>::state_b();
+ std::weak_ptr<T> wp1 = sp1;
+ std::weak_ptr<T> wp2 = sp2;
+
+ std::atomic<std::weak_ptr<T>> a((std::weak_ptr<T>(wp1)));
+
+ std::same_as<std::weak_ptr<T>> decltype(auto) out = a.exchange(std::weak_ptr<T>(wp2));
+ {
+ auto locked = out.lock();
+ assert(locked && *locked == *sp1);
+ }
+ {
+ auto locked = a.load().lock();
+ assert(locked && *locked == *sp2);
+ }
+
+ std::weak_ptr<T> empty;
+ out = a.exchange(std::weak_ptr<T>(empty), std::memory_order_seq_cst);
+ {
+ auto locked = out.lock();
+ assert(locked && *locked == *sp2);
+ }
+ assert(a.load().expired());
+
+ ASSERT_NOEXCEPT(a.exchange(std::weak_ptr<T>(wp1)));
+}
+
+int main(int, char**) {
+#define LIBCXX_ATOMIC_SP_RUN_W_EXCHANGE(T) test_exchange_weak<T>();
+ LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_W_EXCHANGE)
+#undef LIBCXX_ATOMIC_SP_RUN_W_EXCHANGE
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/is_always_lock_free.compile.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/is_always_lock_free.compile.pass.cpp
new file mode 100644
index 0000000000000..ceb3dbce26c29
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/is_always_lock_free.compile.pass.cpp
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+// UNSUPPORTED: no-threads
+
+// static constexpr bool is_always_lock_free;
+// bool is_lock_free() const noexcept;
+
+#include <atomic>
+#include <memory>
+
+#include "../atomic_smart_ptr_test_types.h"
+#include "test_macros.h"
+
+template <class T>
+void check() {
+ using A = std::atomic<std::weak_ptr<T>>;
+
+ static_assert(std::same_as<decltype(A::is_always_lock_free), const bool>);
+ static_assert(A::is_always_lock_free == false);
+
+ const A awp;
+ std::same_as<bool> decltype(auto) lf = awp.is_lock_free();
+ (void)lf;
+ ASSERT_NOEXCEPT(A::is_always_lock_free);
+ ASSERT_NOEXCEPT(awp.is_lock_free());
+}
+
+int main(int, char**) {
+#define LIBCXX_ATOMIC_SP_RUN_W_IALF(T) check<T>();
+ LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_W_IALF)
+#undef LIBCXX_ATOMIC_SP_RUN_W_IALF
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/is_lock_free.compile.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/is_lock_free.compile.pass.cpp
new file mode 100644
index 0000000000000..55421abb2026b
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/is_lock_free.compile.pass.cpp
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+// UNSUPPORTED: no-threads
+
+// bool is_lock_free() const noexcept;
+
+#include <atomic>
+#include <memory>
+
+#include "../atomic_smart_ptr_test_types.h"
+#include "test_macros.h"
+
+template <class T>
+void check(const std::atomic<std::weak_ptr<T>>& awp) noexcept {
+ static_assert(!std::atomic<std::weak_ptr<T>>::is_always_lock_free);
+ std::same_as<bool> decltype(auto) lf = awp.is_lock_free();
+ (void)lf;
+ ASSERT_SAME_TYPE(decltype(awp.is_lock_free()), bool);
+ ASSERT_NOEXCEPT(awp.is_lock_free());
+}
+
+int main(int, char**) {
+#define LIBCXX_ATOMIC_SP_RUN_W_ILF(T) check<T>(std::atomic<std::weak_ptr<T>>());
+ LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_W_ILF)
+#undef LIBCXX_ATOMIC_SP_RUN_W_ILF
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/load.compile.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/load.compile.pass.cpp
new file mode 100644
index 0000000000000..b372c94cd1597
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/load.compile.pass.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+// UNSUPPORTED: no-threads
+
+// weak_ptr<T> load(memory_order order = memory_order::seq_cst) const noexcept;
+
+#include <atomic>
+#include <memory>
+
+#include "../atomic_smart_ptr_test_types.h"
+#include "test_macros.h"
+
+template <class T>
+void check(const std::atomic<std::weak_ptr<T>>& awp) {
+ std::same_as<std::weak_ptr<T>> decltype(auto) no_arg = awp.load();
+ ASSERT_SAME_TYPE(decltype(awp.load()), std::weak_ptr<T>);
+ ASSERT_NOEXCEPT(awp.load());
+
+ std::same_as<std::weak_ptr<T>> decltype(auto) with_order = awp.load(std::memory_order_seq_cst);
+ ASSERT_SAME_TYPE(decltype(awp.load(std::memory_order_acquire)), std::weak_ptr<T>);
+ ASSERT_NOEXCEPT(awp.load(std::memory_order_seq_cst));
+ static_cast<void>(no_arg);
+ static_cast<void>(with_order);
+
+ {
+ const std::atomic<std::weak_ptr<T>> const_a;
+ static_assert(noexcept(const_a.load()));
+ ASSERT_SAME_TYPE(decltype(const_a.load()), std::weak_ptr<T>);
+ }
+}
+
+int main(int, char**) {
+#define LIBCXX_ATOMIC_SP_RUN_W_LOAD(T) check<T>(std::atomic<std::weak_ptr<T>>());
+ LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_W_LOAD)
+#undef LIBCXX_ATOMIC_SP_RUN_W_LOAD
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/notify_all.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/notify_all.pass.cpp
new file mode 100644
index 0000000000000..3211c651143f3
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/notify_all.pass.cpp
@@ -0,0 +1,69 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+// UNSUPPORTED: no-threads
+
+// void notify_all() noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <memory>
+#include <thread>
+
+#include "../atomic_smart_ptr_test_types.h"
+#include "test_macros.h"
+
+template <class T>
+void test_notify_all_weak() {
+ using libcxx_atomic_smart_ptr_test::SpValues;
+ std::atomic<std::weak_ptr<T>> a;
+
+ ASSERT_NOEXCEPT(a.notify_all());
+
+#if __cpp_lib_atomic_wait >= 201907L
+ auto sp1 = SpValues<T>::state_a();
+ std::weak_ptr<T> wp1 = sp1;
+ a.store(std::weak_ptr<T>(wp1));
+
+ std::atomic<int> phase{0};
+ std::thread t1([&] {
+ std::weak_ptr<T> old = a.load();
+ phase.fetch_add(1, std::memory_order_acq_rel);
+ while (phase.load(std::memory_order_acquire) < 2) {
+ std::this_thread::yield();
+ }
+ a.wait(old);
+ });
+ std::thread t2([&] {
+ std::weak_ptr<T> old = a.load();
+ phase.fetch_add(1, std::memory_order_acq_rel);
+ while (phase.load(std::memory_order_acquire) < 2) {
+ std::this_thread::yield();
+ }
+ a.wait(old);
+ });
+
+ while (phase.load(std::memory_order_acquire) < 2) {
+ std::this_thread::yield();
+ }
+
+ auto sp2 = SpValues<T>::state_b();
+ a.store(std::weak_ptr<T>(sp2));
+ a.notify_all();
+ t1.join();
+ t2.join();
+#endif
+}
+
+int main(int, char**) {
+#define LIBCXX_ATOMIC_SP_RUN_W_NA(T) test_notify_all_weak<T>();
+ LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_W_NA)
+#undef LIBCXX_ATOMIC_SP_RUN_W_NA
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/notify_one.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/notify_one.pass.cpp
new file mode 100644
index 0000000000000..440aab1d3b5e7
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/notify_one.pass.cpp
@@ -0,0 +1,57 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+// UNSUPPORTED: no-threads
+
+// void notify_one() noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <memory>
+#include <thread>
+
+#include "../atomic_smart_ptr_test_types.h"
+#include "test_macros.h"
+
+template <class T>
+void test_notify_one_weak() {
+ using libcxx_atomic_smart_ptr_test::SpValues;
+ std::atomic<std::weak_ptr<T>> a;
+
+ ASSERT_NOEXCEPT(a.notify_one());
+
+#if __cpp_lib_atomic_wait >= 201907L
+ auto sp1 = SpValues<T>::state_a();
+ std::weak_ptr<T> wp1 = sp1;
+ a.store(std::weak_ptr<T>(wp1));
+
+ std::atomic<bool> started{false};
+ std::thread t([&] {
+ std::weak_ptr<T> old = a.load();
+ started.store(true, std::memory_order_release);
+ a.wait(old);
+ });
+
+ while (!started.load(std::memory_order_acquire)) {
+ std::this_thread::yield();
+ }
+
+ auto spB = SpValues<T>::state_b();
+ a.store(std::weak_ptr<T>(spB));
+ a.notify_one();
+ t.join();
+#endif
+}
+
+int main(int, char**) {
+#define LIBCXX_ATOMIC_SP_RUN_W_N1(T) test_notify_one_weak<T>();
+ LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_W_N1)
+#undef LIBCXX_ATOMIC_SP_RUN_W_N1
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/store.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/store.pass.cpp
new file mode 100644
index 0000000000000..35baa907c1437
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/store.pass.cpp
@@ -0,0 +1,50 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+// UNSUPPORTED: no-threads
+
+// void store(weak_ptr<T> desired, memory_order order = memory_order::seq_cst) noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <memory>
+
+#include "../atomic_smart_ptr_test_types.h"
+#include "test_macros.h"
+
+template <class T>
+void test_store_weak() {
+ using libcxx_atomic_smart_ptr_test::SpValues;
+ auto sp1 = SpValues<T>::state_a();
+ auto sp2 = SpValues<T>::state_b();
+ std::weak_ptr<T> wp1 = sp1;
+ std::weak_ptr<T> wp2 = sp2;
+
+ std::atomic<std::weak_ptr<T>> a;
+ a.store(std::weak_ptr<T>(wp1));
+ {
+ auto locked = a.load().lock();
+ assert(locked && *locked == *sp1);
+ }
+
+ a.store(std::weak_ptr<T>(wp2), std::memory_order_relaxed);
+ {
+ auto locked = a.load().lock();
+ assert(locked && *locked == *sp2);
+ }
+
+ ASSERT_NOEXCEPT(a.store(std::weak_ptr<T>(wp1)));
+}
+
+int main(int, char**) {
+#define LIBCXX_ATOMIC_SP_RUN_W_STORE(T) test_store_weak<T>();
+ LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_W_STORE)
+#undef LIBCXX_ATOMIC_SP_RUN_W_STORE
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/types.compile.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/types.compile.pass.cpp
new file mode 100644
index 0000000000000..a130c9920d99c
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/types.compile.pass.cpp
@@ -0,0 +1,61 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+// UNSUPPORTED: no-threads
+
+// Note: atomic<weak_ptr<T>> intentionally has no constexpr atomic(nullptr_t) constructor,
+// unlike atomic<shared_ptr<T>>. See [util.smartptr.atomic.weak].
+
+// using value_type = weak_ptr<T>;
+// static constexpr bool is_always_lock_free = false;
+// atomic() noexcept;
+// atomic(weak_ptr<T>) noexcept;
+// atomic(const atomic&) = delete;
+// atomic& operator=(const atomic&) = delete;
+
+#include <atomic>
+#include <cassert>
+#include <memory>
+#include <type_traits>
+
+#include "../atomic_smart_ptr_test_types.h"
+#include "test_macros.h"
+
+template <class T>
+void traits() {
+ using A = std::atomic<std::weak_ptr<T>>;
+
+ static_assert(std::same_as<typename A::value_type, std::weak_ptr<T>>);
+ static_assert(!A::is_always_lock_free);
+
+ static_assert(!std::is_copy_constructible_v<A>);
+ static_assert(!std::is_copy_assignable_v<A>);
+
+ static_assert(std::is_nothrow_default_constructible_v<A>);
+
+ {
+ A a;
+ assert(a.load().expired());
+ }
+
+ {
+ auto sp = libcxx_atomic_smart_ptr_test::SpValues<T>::state_a();
+ std::weak_ptr<T> wp = sp;
+ A a((std::weak_ptr<T>(wp)));
+ auto locked = a.load().lock();
+ assert(locked && *locked == *sp);
+ }
+}
+
+int main(int, char**) {
+#define LIBCXX_ATOMIC_SP_RUN_W_TRAITS(T) traits<T>();
+ LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_W_TRAITS)
+#undef LIBCXX_ATOMIC_SP_RUN_W_TRAITS
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/wait.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/wait.pass.cpp
new file mode 100644
index 0000000000000..f840c4ec00d32
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/wait.pass.cpp
@@ -0,0 +1,64 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+// UNSUPPORTED: no-threads
+
+// void wait(weak_ptr<T> old, memory_order order = memory_order::seq_cst) const noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <memory>
+#include <thread>
+
+#include "../atomic_smart_ptr_test_types.h"
+#include "test_macros.h"
+
+template <class T>
+void test_wait_weak() {
+ using libcxx_atomic_smart_ptr_test::SpValues;
+ std::atomic<std::weak_ptr<T>> a;
+
+#if __cpp_lib_atomic_wait >= 201907L
+ auto sp_for_wait = SpValues<T>::state_a();
+ std::weak_ptr<T> wp_for_wait = sp_for_wait;
+ a.store(std::weak_ptr<T>(wp_for_wait));
+
+ auto sp1 = SpValues<T>::state_b();
+ std::weak_ptr<T> wp1 = sp1;
+
+ std::atomic<bool> started{false};
+ std::thread t([&] {
+ std::weak_ptr<T> old = a.load();
+ started.store(true, std::memory_order_release);
+ a.wait(old);
+ });
+
+ while (!started.load(std::memory_order_acquire)) {
+ std::this_thread::yield();
+ }
+
+ a.store(std::weak_ptr<T>(wp1));
+ a.notify_all();
+ t.join();
+
+ {
+ auto locked = a.load().lock();
+ assert(locked && *locked == *sp1);
+ }
+#endif
+
+ ASSERT_NOEXCEPT(a.wait(std::weak_ptr<T>(), std::memory_order_seq_cst));
+}
+
+int main(int, char**) {
+#define LIBCXX_ATOMIC_SP_RUN_W_WAIT(T) test_wait_weak<T>();
+ LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_W_WAIT)
+#undef LIBCXX_ATOMIC_SP_RUN_W_WAIT
+ return 0;
+}
>From 3be5c0f2ca555cf522a234a9cffcccbcc3231330 Mon Sep 17 00:00:00 2001
From: ViNN280801 <vladislav.semykin at gmail.com>
Date: Sat, 2 May 2026 14:33:43 +0300
Subject: [PATCH 10/14] [libcxx][NFC] Yielding another thread in stress test to
decrease the execution time; fixed issue with same_as on Armv7 and Armv8 (and
with -fno-exceptions); applied formatting changes suggested by GH bot
Signed-off-by: ViNN280801 <vladislav.semykin at gmail.com>
---
.../atomic/atomic_smart_ptr_test_types.h | 12 ++++++------
.../atomic/shared/types.compile.pass.cpp | 1 +
.../util.smartptr/atomic/weak/types.compile.pass.cpp | 1 +
.../atomic_shared_ptr_stress.pass.cpp | 1 +
4 files changed, 9 insertions(+), 6 deletions(-)
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/atomic_smart_ptr_test_types.h b/libcxx/test/std/utilities/memory/util.smartptr/atomic/atomic_smart_ptr_test_types.h
index 309cc11bb5026..4ea5a52472891 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/atomic/atomic_smart_ptr_test_types.h
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/atomic_smart_ptr_test_types.h
@@ -91,12 +91,12 @@ struct SpValues<Flag> {
} // namespace libcxx_atomic_smart_ptr_test
// Instantiate the same runtime coverage for every value type.
-#define LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(M) \
- M(int) \
- M(double) \
- M(std::string) \
- M(libcxx_atomic_smart_ptr_test::TrackedPod) \
- M(libcxx_atomic_smart_ptr_test::Handle) \
+#define LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(M) \
+ M(int) \
+ M(double) \
+ M(std::string) \
+ M(libcxx_atomic_smart_ptr_test::TrackedPod) \
+ M(libcxx_atomic_smart_ptr_test::Handle) \
M(libcxx_atomic_smart_ptr_test::Flag)
#endif // LIBCXX_TEST_STD_UTILITIES_MEMORY_UTIL_SMARTPTR_ATOMIC_ATOMIC_SMART_PTR_TEST_TYPES_H
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/types.compile.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/types.compile.pass.cpp
index 39a49c810f9a4..bc8ee81abe2b1 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/types.compile.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/types.compile.pass.cpp
@@ -19,6 +19,7 @@
#include <atomic>
#include <cassert>
+#include <concepts>
#include <memory>
#include <type_traits>
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/types.compile.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/types.compile.pass.cpp
index a130c9920d99c..40e0f4d5e38d6 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/types.compile.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/types.compile.pass.cpp
@@ -21,6 +21,7 @@
#include <atomic>
#include <cassert>
+#include <concepts>
#include <memory>
#include <type_traits>
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_stress.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_stress.pass.cpp
index 86b56bfa920bc..da73a98b44526 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_stress.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared.atomic/atomic_shared_ptr_stress.pass.cpp
@@ -56,6 +56,7 @@ void run(Atomic& a, Make make_value, const std::array<int, kCandidateCount>& exp
}
assert(ok);
}
+ std::this_thread::yield();
(void)r;
}
});
>From 861960381cadf6a3edc57b4a89dc7bb7fa048812 Mon Sep 17 00:00:00 2001
From: ViNN280801 <vladislav.semykin at gmail.com>
Date: Sat, 2 May 2026 16:31:27 +0300
Subject: [PATCH 11/14] [libcxx][NFC] Applied review feedback to
atomic<shared_ptr>/<weak_ptr> tests
Signed-off-by: ViNN280801 <vladislav.semykin at gmail.com>
---
.../atomic/atomic_smart_ptr_test_types.h | 29 +++++++++----------
...compile.pass.cpp => is_lock_free.pass.cpp} | 24 ++++++++-------
.../atomic/shared/assign.pass.cpp | 15 ++++++----
.../shared/compare_exchange_strong.pass.cpp | 12 ++++----
.../shared/compare_exchange_weak.pass.cpp | 12 ++++----
.../atomic/shared/convert.pass.cpp | 13 +++++----
.../atomic/shared/exchange.pass.cpp | 12 ++++----
....pass.cpp => is_always_lock_free.pass.cpp} | 13 +++++----
.../{load.compile.pass.cpp => load.pass.cpp} | 18 +++++++-----
.../atomic/shared/notify_all.pass.cpp | 12 ++++----
.../atomic/shared/notify_one.pass.cpp | 12 ++++----
.../atomic/shared/store.pass.cpp | 14 +++++----
...{types.compile.pass.cpp => types.pass.cpp} | 11 ++++---
.../util.smartptr/atomic/shared/wait.pass.cpp | 12 ++++----
.../util.smartptr/atomic/weak/assign.pass.cpp | 12 ++++----
.../weak/compare_exchange_strong.pass.cpp | 12 ++++----
.../weak/compare_exchange_weak.pass.cpp | 12 ++++----
.../atomic/weak/convert.pass.cpp | 12 ++++----
.../atomic/weak/exchange.pass.cpp | 12 ++++----
....pass.cpp => is_always_lock_free.pass.cpp} | 13 +++++----
...compile.pass.cpp => is_lock_free.pass.cpp} | 12 ++++----
.../{load.compile.pass.cpp => load.pass.cpp} | 18 +++++++-----
.../atomic/weak/notify_all.pass.cpp | 12 ++++----
.../atomic/weak/notify_one.pass.cpp | 12 ++++----
.../util.smartptr/atomic/weak/store.pass.cpp | 12 ++++----
...{types.compile.pass.cpp => types.pass.cpp} | 11 ++++---
.../util.smartptr/atomic/weak/wait.pass.cpp | 12 ++++----
27 files changed, 215 insertions(+), 156 deletions(-)
rename libcxx/test/std/utilities/memory/util.smartptr/atomic/{is_lock_free.compile.pass.cpp => is_lock_free.pass.cpp} (66%)
rename libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/{is_always_lock_free.compile.pass.cpp => is_always_lock_free.pass.cpp} (77%)
rename libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/{load.compile.pass.cpp => load.pass.cpp} (68%)
rename libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/{types.compile.pass.cpp => types.pass.cpp} (88%)
rename libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/{is_always_lock_free.compile.pass.cpp => is_always_lock_free.pass.cpp} (78%)
rename libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/{is_lock_free.compile.pass.cpp => is_lock_free.pass.cpp} (75%)
rename libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/{load.compile.pass.cpp => load.pass.cpp} (68%)
rename libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/{types.compile.pass.cpp => types.pass.cpp} (86%)
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/atomic_smart_ptr_test_types.h b/libcxx/test/std/utilities/memory/util.smartptr/atomic/atomic_smart_ptr_test_types.h
index 4ea5a52472891..b8a235e8e1416 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/atomic/atomic_smart_ptr_test_types.h
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/atomic_smart_ptr_test_types.h
@@ -11,15 +11,13 @@
//
//===----------------------------------------------------------------------===//
-#ifndef LIBCXX_TEST_STD_UTILITIES_MEMORY_UTIL_SMARTPTR_ATOMIC_ATOMIC_SMART_PTR_TEST_TYPES_H
-#define LIBCXX_TEST_STD_UTILITIES_MEMORY_UTIL_SMARTPTR_ATOMIC_ATOMIC_SMART_PTR_TEST_TYPES_H
+#ifndef TEST_STD_UTILITIES_MEMORY_UTIL_SMARTPTR_ATOMIC_ATOMIC_SMART_PTR_TEST_TYPES_H
+#define TEST_STD_UTILITIES_MEMORY_UTIL_SMARTPTR_ATOMIC_ATOMIC_SMART_PTR_TEST_TYPES_H
#include <cstdint>
#include <memory>
#include <string>
-namespace libcxx_atomic_smart_ptr_test {
-
// --- User-defined and semi-random scalar-like types ---------------------------------
struct TrackedPod {
@@ -88,15 +86,16 @@ struct SpValues<Flag> {
static std::shared_ptr<Flag> state_c() { return std::make_shared<Flag>(Flag::On); }
};
-} // namespace libcxx_atomic_smart_ptr_test
-
-// Instantiate the same runtime coverage for every value type.
-#define LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(M) \
- M(int) \
- M(double) \
- M(std::string) \
- M(libcxx_atomic_smart_ptr_test::TrackedPod) \
- M(libcxx_atomic_smart_ptr_test::Handle) \
- M(libcxx_atomic_smart_ptr_test::Flag)
+struct ForEachSmartPtrType {
+ template <template <class> class Fn>
+ void operator()() const {
+ Fn<int>{}();
+ Fn<double>{}();
+ Fn<std::string>{}();
+ Fn<TrackedPod>{}();
+ Fn<Handle>{}();
+ Fn<Flag>{}();
+ }
+};
-#endif // LIBCXX_TEST_STD_UTILITIES_MEMORY_UTIL_SMARTPTR_ATOMIC_ATOMIC_SMART_PTR_TEST_TYPES_H
+#endif // TEST_STD_UTILITIES_MEMORY_UTIL_SMARTPTR_ATOMIC_ATOMIC_SMART_PTR_TEST_TYPES_H
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/is_lock_free.compile.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/is_lock_free.pass.cpp
similarity index 66%
rename from libcxx/test/std/utilities/memory/util.smartptr/atomic/is_lock_free.compile.pass.cpp
rename to libcxx/test/std/utilities/memory/util.smartptr/atomic/is_lock_free.pass.cpp
index 71464f598f5eb..574a89fababb3 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/atomic/is_lock_free.compile.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/is_lock_free.pass.cpp
@@ -26,8 +26,7 @@ void check(const std::atomic<std::shared_ptr<T>>& asp) noexcept {
static_assert(!std::atomic<std::shared_ptr<T>>::is_always_lock_free);
std::same_as<bool> decltype(auto) lf = asp.is_lock_free();
(void)lf;
- ASSERT_SAME_TYPE(decltype(asp.is_lock_free()), bool);
- ASSERT_NOEXCEPT(asp.is_lock_free());
+ static_assert(noexcept(asp.is_lock_free()));
}
template <class T>
@@ -35,16 +34,21 @@ void check(const std::atomic<std::weak_ptr<T>>& awp) noexcept {
static_assert(!std::atomic<std::weak_ptr<T>>::is_always_lock_free);
std::same_as<bool> decltype(auto) lf = awp.is_lock_free();
(void)lf;
- ASSERT_SAME_TYPE(decltype(awp.is_lock_free()), bool);
- ASSERT_NOEXCEPT(awp.is_lock_free());
+ static_assert(noexcept(awp.is_lock_free()));
}
+template <class T>
+struct TestIsLockFreeShared {
+ void operator()() const { check<T>(std::atomic<std::shared_ptr<T>>()); }
+};
+
+template <class T>
+struct TestIsLockFreeWeak {
+ void operator()() const { check<T>(std::atomic<std::weak_ptr<T>>()); }
+};
+
int main(int, char**) {
-#define LIBCXX_ATOMIC_SP_RUN_IS_LOCK_FREE(T) check<T>(std::atomic<std::shared_ptr<T>>());
- LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_IS_LOCK_FREE)
-#undef LIBCXX_ATOMIC_SP_RUN_IS_LOCK_FREE
-#define LIBCXX_ATOMIC_SP_RUN_W_ILF(T) check<T>(std::atomic<std::weak_ptr<T>>());
- LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_W_ILF)
-#undef LIBCXX_ATOMIC_SP_RUN_W_ILF
+ ForEachSmartPtrType{}.template operator()<TestIsLockFreeShared>();
+ ForEachSmartPtrType{}.template operator()<TestIsLockFreeWeak>();
return 0;
}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/assign.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/assign.pass.cpp
index 3a39c30cb7413..c3ad8ec3904c5 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/assign.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/assign.pass.cpp
@@ -22,19 +22,22 @@
template <class T>
void test_assign() {
std::atomic<std::shared_ptr<T>> a;
- auto p = libcxx_atomic_smart_ptr_test::SpValues<T>::state_a();
+ auto p = SpValues<T>::state_a();
a = std::shared_ptr<T>(p);
assert(a.load().get() == p.get());
assert(*a.load() == *p);
a = nullptr;
assert(!a.load());
- ASSERT_NOEXCEPT(a = nullptr);
- ASSERT_NOEXCEPT(a = std::shared_ptr<T>(p));
+ static_assert(noexcept(a = nullptr));
+ static_assert(noexcept(a = std::shared_ptr<T>(p)));
}
+template <class T>
+struct TestAssign {
+ void operator()() const { test_assign<T>(); }
+};
+
int main(int, char**) {
-#define LIBCXX_ATOMIC_SP_RUN_ASSIGN(T) test_assign<T>();
- LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_ASSIGN)
-#undef LIBCXX_ATOMIC_SP_RUN_ASSIGN
+ ForEachSmartPtrType{}.template operator()<TestAssign>();
return 0;
}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/compare_exchange_strong.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/compare_exchange_strong.pass.cpp
index 5ceca3efad536..bb10b75746c2d 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/compare_exchange_strong.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/compare_exchange_strong.pass.cpp
@@ -21,7 +21,6 @@
template <class T>
void test_compare_exchange_strong() {
- using libcxx_atomic_smart_ptr_test::SpValues;
auto p1 = SpValues<T>::state_a();
auto p2 = SpValues<T>::state_b();
std::atomic<std::shared_ptr<T>> a((std::shared_ptr<T>(p1)));
@@ -32,7 +31,7 @@ void test_compare_exchange_strong() {
assert(ok);
assert(expected.get() == p1.get());
assert(*a.load() == *p2);
- ASSERT_NOEXCEPT(a.compare_exchange_strong(expected, std::shared_ptr<T>(p2)));
+ static_assert(noexcept(a.compare_exchange_strong(expected, std::shared_ptr<T>(p2))));
}
{
@@ -54,9 +53,12 @@ void test_compare_exchange_strong() {
}
}
+template <class T>
+struct TestCompareExchangeStrong {
+ void operator()() const { test_compare_exchange_strong<T>(); }
+};
+
int main(int, char**) {
-#define LIBCXX_ATOMIC_SP_RUN_CXS(T) test_compare_exchange_strong<T>();
- LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_CXS)
-#undef LIBCXX_ATOMIC_SP_RUN_CXS
+ ForEachSmartPtrType{}.template operator()<TestCompareExchangeStrong>();
return 0;
}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/compare_exchange_weak.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/compare_exchange_weak.pass.cpp
index 713d04b7f5c47..73de9af913df8 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/compare_exchange_weak.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/compare_exchange_weak.pass.cpp
@@ -21,7 +21,6 @@
template <class T>
void test_compare_exchange_weak() {
- using libcxx_atomic_smart_ptr_test::SpValues;
auto p1 = SpValues<T>::state_a();
auto p2 = SpValues<T>::state_b();
std::atomic<std::shared_ptr<T>> a((std::shared_ptr<T>(p1)));
@@ -33,7 +32,7 @@ void test_compare_exchange_weak() {
ok = a.compare_exchange_weak(expected, std::shared_ptr<T>(p2));
assert(ok);
assert(*a.load() == *p2);
- ASSERT_NOEXCEPT(a.compare_exchange_weak(expected, std::shared_ptr<T>(p2)));
+ static_assert(noexcept(a.compare_exchange_weak(expected, std::shared_ptr<T>(p2))));
}
{
@@ -56,9 +55,12 @@ void test_compare_exchange_weak() {
}
}
+template <class T>
+struct TestCompareExchangeWeak {
+ void operator()() const { test_compare_exchange_weak<T>(); }
+};
+
int main(int, char**) {
-#define LIBCXX_ATOMIC_SP_RUN_CXW(T) test_compare_exchange_weak<T>();
- LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_CXW)
-#undef LIBCXX_ATOMIC_SP_RUN_CXW
+ ForEachSmartPtrType{}.template operator()<TestCompareExchangeWeak>();
return 0;
}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/convert.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/convert.pass.cpp
index 4c219ae175d18..85de5e07b7058 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/convert.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/convert.pass.cpp
@@ -20,7 +20,7 @@
template <class T>
void test_convert() {
- auto p = libcxx_atomic_smart_ptr_test::SpValues<T>::state_a();
+ auto p = SpValues<T>::state_a();
const std::atomic<std::shared_ptr<T>> a((std::shared_ptr<T>(p)));
std::shared_ptr<T> s = a;
@@ -30,12 +30,15 @@ void test_convert() {
std::same_as<std::shared_ptr<T>> decltype(auto) s2 = static_cast<std::shared_ptr<T>>(a);
assert(s2.get() == p.get());
- ASSERT_NOEXCEPT(static_cast<std::shared_ptr<T>>(a));
+ static_assert(noexcept(static_cast<std::shared_ptr<T>>(a)));
}
+template <class T>
+struct TestConvert {
+ void operator()() const { test_convert<T>(); }
+};
+
int main(int, char**) {
-#define LIBCXX_ATOMIC_SP_RUN_CONVERT(T) test_convert<T>();
- LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_CONVERT)
-#undef LIBCXX_ATOMIC_SP_RUN_CONVERT
+ ForEachSmartPtrType{}.template operator()<TestConvert>();
return 0;
}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/exchange.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/exchange.pass.cpp
index fa957d8ff7ebe..5c5339389ac7b 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/exchange.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/exchange.pass.cpp
@@ -20,7 +20,6 @@
template <class T>
void test_exchange() {
- using libcxx_atomic_smart_ptr_test::SpValues;
auto p1 = SpValues<T>::state_a();
auto p2 = SpValues<T>::state_b();
std::atomic<std::shared_ptr<T>> a((std::shared_ptr<T>(p1)));
@@ -34,12 +33,15 @@ void test_exchange() {
assert(*out == *p2);
assert(!a.load());
- ASSERT_NOEXCEPT(a.exchange(nullptr));
+ static_assert(noexcept(a.exchange(nullptr)));
}
+template <class T>
+struct TestExchange {
+ void operator()() const { test_exchange<T>(); }
+};
+
int main(int, char**) {
-#define LIBCXX_ATOMIC_SP_RUN_EXCHANGE(T) test_exchange<T>();
- LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_EXCHANGE)
-#undef LIBCXX_ATOMIC_SP_RUN_EXCHANGE
+ ForEachSmartPtrType{}.template operator()<TestExchange>();
return 0;
}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/is_always_lock_free.compile.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/is_always_lock_free.pass.cpp
similarity index 77%
rename from libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/is_always_lock_free.compile.pass.cpp
rename to libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/is_always_lock_free.pass.cpp
index b7ff230832d09..879fd1f204ceb 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/is_always_lock_free.compile.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/is_always_lock_free.pass.cpp
@@ -28,13 +28,16 @@ void check() {
const A asp;
std::same_as<bool> decltype(auto) lf = asp.is_lock_free();
(void)lf;
- ASSERT_NOEXCEPT(A::is_always_lock_free);
- ASSERT_NOEXCEPT(asp.is_lock_free());
+ static_assert(noexcept(A::is_always_lock_free));
+ static_assert(noexcept(asp.is_lock_free()));
}
+template <class T>
+struct TestIsAlwaysLockFreeShared {
+ void operator()() const { check<T>(); }
+};
+
int main(int, char**) {
-#define LIBCXX_ATOMIC_SP_RUN_IS_ALWAYS_LF(T) check<T>();
- LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_IS_ALWAYS_LF)
-#undef LIBCXX_ATOMIC_SP_RUN_IS_ALWAYS_LF
+ ForEachSmartPtrType{}.template operator()<TestIsAlwaysLockFreeShared>();
return 0;
}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/load.compile.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/load.pass.cpp
similarity index 68%
rename from libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/load.compile.pass.cpp
rename to libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/load.pass.cpp
index 48bcb81ab6cce..f6271923d3db8 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/load.compile.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/load.pass.cpp
@@ -20,25 +20,27 @@
template <class T>
void check(const std::atomic<std::shared_ptr<T>>& asp) {
std::same_as<std::shared_ptr<T>> decltype(auto) no_arg = asp.load();
- ASSERT_SAME_TYPE(decltype(asp.load()), std::shared_ptr<T>);
- ASSERT_NOEXCEPT(asp.load());
+ static_assert(noexcept(asp.load()));
std::same_as<std::shared_ptr<T>> decltype(auto) with_order = asp.load(std::memory_order_seq_cst);
- ASSERT_SAME_TYPE(decltype(asp.load(std::memory_order_acquire)), std::shared_ptr<T>);
- ASSERT_NOEXCEPT(asp.load(std::memory_order_seq_cst));
+ static_assert(noexcept(asp.load(std::memory_order_seq_cst)));
static_cast<void>(no_arg);
static_cast<void>(with_order);
{
const std::atomic<std::shared_ptr<T>> const_a;
static_assert(noexcept(const_a.load()));
- ASSERT_SAME_TYPE(decltype(const_a.load()), std::shared_ptr<T>);
+ std::same_as<std::shared_ptr<T>> decltype(auto) loaded = const_a.load();
+ (void)loaded;
}
}
+template <class T>
+struct TestLoadShared {
+ void operator()() const { check<T>(std::atomic<std::shared_ptr<T>>()); }
+};
+
int main(int, char**) {
-#define LIBCXX_ATOMIC_SP_RUN_LOAD(T) check<T>(std::atomic<std::shared_ptr<T>>());
- LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_LOAD)
-#undef LIBCXX_ATOMIC_SP_RUN_LOAD
+ ForEachSmartPtrType{}.template operator()<TestLoadShared>();
return 0;
}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/notify_all.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/notify_all.pass.cpp
index 5d4c9b7714b23..6c9cef0eceb5a 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/notify_all.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/notify_all.pass.cpp
@@ -21,10 +21,9 @@
template <class T>
void test_notify_all() {
- using libcxx_atomic_smart_ptr_test::SpValues;
std::atomic<std::shared_ptr<T>> a;
- ASSERT_NOEXCEPT(a.notify_all());
+ static_assert(noexcept(a.notify_all()));
#if __cpp_lib_atomic_wait >= 201907L
auto p1 = SpValues<T>::state_a();
@@ -59,9 +58,12 @@ void test_notify_all() {
#endif
}
+template <class T>
+struct TestNotifyAll {
+ void operator()() const { test_notify_all<T>(); }
+};
+
int main(int, char**) {
-#define LIBCXX_ATOMIC_SP_RUN_NOTIFY_ALL(T) test_notify_all<T>();
- LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_NOTIFY_ALL)
-#undef LIBCXX_ATOMIC_SP_RUN_NOTIFY_ALL
+ ForEachSmartPtrType{}.template operator()<TestNotifyAll>();
return 0;
}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/notify_one.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/notify_one.pass.cpp
index b178fb13d1a0f..8d9d3a3ecdcc8 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/notify_one.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/notify_one.pass.cpp
@@ -20,10 +20,9 @@
template <class T>
void test_notify_one() {
- using libcxx_atomic_smart_ptr_test::SpValues;
std::atomic<std::shared_ptr<T>> a;
- ASSERT_NOEXCEPT(a.notify_one());
+ static_assert(noexcept(a.notify_one()));
#if __cpp_lib_atomic_wait >= 201907L
auto p1 = SpValues<T>::state_a();
@@ -46,9 +45,12 @@ void test_notify_one() {
#endif
}
+template <class T>
+struct TestNotifyOne {
+ void operator()() const { test_notify_one<T>(); }
+};
+
int main(int, char**) {
-#define LIBCXX_ATOMIC_SP_RUN_NOTIFY_ONE(T) test_notify_one<T>();
- LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_NOTIFY_ONE)
-#undef LIBCXX_ATOMIC_SP_RUN_NOTIFY_ONE
+ ForEachSmartPtrType{}.template operator()<TestNotifyOne>();
return 0;
}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/store.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/store.pass.cpp
index 9ab808cbd0fae..51f1df0f2a2d9 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/store.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/store.pass.cpp
@@ -20,7 +20,6 @@
template <class T>
void test_store() {
- using libcxx_atomic_smart_ptr_test::SpValues;
std::atomic<std::shared_ptr<T>> a;
auto p = SpValues<T>::state_a();
@@ -34,16 +33,19 @@ void test_store() {
a.store(nullptr, std::memory_order_relaxed);
assert(!a.load());
- ASSERT_NOEXCEPT(a.store(nullptr));
+ static_assert(noexcept(a.store(nullptr)));
{
std::shared_ptr<T> desired = std::make_shared<T>(*SpValues<T>::state_c());
- ASSERT_NOEXCEPT(a.store(std::move(desired), std::memory_order_seq_cst));
+ static_assert(noexcept(a.store(std::move(desired), std::memory_order_seq_cst)));
}
}
+template <class T>
+struct TestStore {
+ void operator()() const { test_store<T>(); }
+};
+
int main(int, char**) {
-#define LIBCXX_ATOMIC_SP_RUN_STORE(T) test_store<T>();
- LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_STORE)
-#undef LIBCXX_ATOMIC_SP_RUN_STORE
+ ForEachSmartPtrType{}.template operator()<TestStore>();
return 0;
}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/types.compile.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/types.pass.cpp
similarity index 88%
rename from libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/types.compile.pass.cpp
rename to libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/types.pass.cpp
index bc8ee81abe2b1..563c383d76177 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/types.compile.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/types.pass.cpp
@@ -45,17 +45,20 @@ void traits() {
}
{
- auto p = libcxx_atomic_smart_ptr_test::SpValues<T>::state_a();
+ auto p = SpValues<T>::state_a();
A a((std::shared_ptr<T>(p)));
assert(a.load().get() == p.get());
assert(*a.load() == *p);
}
}
+template <class T>
+struct TestTraitsShared {
+ void operator()() const { traits<T>(); }
+};
+
int main(int, char**) {
-#define LIBCXX_ATOMIC_SP_RUN_TRAITS(T) traits<T>();
- LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_TRAITS)
-#undef LIBCXX_ATOMIC_SP_RUN_TRAITS
+ ForEachSmartPtrType{}.template operator()<TestTraitsShared>();
{
static_assert(std::is_nothrow_constructible_v<std::atomic<std::shared_ptr<int>>, std::nullptr_t>);
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/wait.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/wait.pass.cpp
index fef98a7008c27..fc9600688e039 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/wait.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/wait.pass.cpp
@@ -21,7 +21,6 @@
template <class T>
void test_wait() {
- using libcxx_atomic_smart_ptr_test::SpValues;
std::atomic<std::shared_ptr<T>> a;
#if __cpp_lib_atomic_wait >= 201907L
@@ -47,12 +46,15 @@ void test_wait() {
assert(*a.load() == *SpValues<T>::state_c());
#endif
- ASSERT_NOEXCEPT(a.wait(std::shared_ptr<T>(), std::memory_order_seq_cst));
+ static_assert(noexcept(a.wait(std::shared_ptr<T>(), std::memory_order_seq_cst)));
}
+template <class T>
+struct TestWait {
+ void operator()() const { test_wait<T>(); }
+};
+
int main(int, char**) {
-#define LIBCXX_ATOMIC_SP_RUN_WAIT(T) test_wait<T>();
- LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_WAIT)
-#undef LIBCXX_ATOMIC_SP_RUN_WAIT
+ ForEachSmartPtrType{}.template operator()<TestWait>();
return 0;
}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/assign.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/assign.pass.cpp
index efe373d1d8036..21d182ba32de4 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/assign.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/assign.pass.cpp
@@ -20,7 +20,6 @@
template <class T>
void test_assign_weak() {
- using libcxx_atomic_smart_ptr_test::SpValues;
auto sp1 = SpValues<T>::state_a();
auto sp2 = SpValues<T>::state_b();
std::weak_ptr<T> wp1 = sp1;
@@ -39,12 +38,15 @@ void test_assign_weak() {
assert(locked && *locked == *sp2);
}
- ASSERT_NOEXCEPT(a = std::weak_ptr<T>(wp1));
+ static_assert(noexcept(a = std::weak_ptr<T>(wp1)));
}
+template <class T>
+struct TestAssignWeak {
+ void operator()() const { test_assign_weak<T>(); }
+};
+
int main(int, char**) {
-#define LIBCXX_ATOMIC_SP_RUN_W_ASSIGN(T) test_assign_weak<T>();
- LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_W_ASSIGN)
-#undef LIBCXX_ATOMIC_SP_RUN_W_ASSIGN
+ ForEachSmartPtrType{}.template operator()<TestAssignWeak>();
return 0;
}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/compare_exchange_strong.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/compare_exchange_strong.pass.cpp
index 61047d17d264b..9d225ae0ba3f5 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/compare_exchange_strong.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/compare_exchange_strong.pass.cpp
@@ -21,7 +21,6 @@
template <class T>
void test_compare_exchange_strong_weakptr() {
- using libcxx_atomic_smart_ptr_test::SpValues;
auto sp1 = SpValues<T>::state_a();
auto sp2 = SpValues<T>::state_b();
std::weak_ptr<T> wp1 = sp1;
@@ -37,7 +36,7 @@ void test_compare_exchange_strong_weakptr() {
auto locked = a.load().lock();
assert(locked && *locked == *sp2);
}
- ASSERT_NOEXCEPT(a.compare_exchange_strong(expected, std::weak_ptr<T>(wp2)));
+ static_assert(noexcept(a.compare_exchange_strong(expected, std::weak_ptr<T>(wp2))));
}
{
@@ -59,9 +58,12 @@ void test_compare_exchange_strong_weakptr() {
}
}
+template <class T>
+struct TestCompareExchangeStrongWeak {
+ void operator()() const { test_compare_exchange_strong_weakptr<T>(); }
+};
+
int main(int, char**) {
-#define LIBCXX_ATOMIC_SP_RUN_W_CXS(T) test_compare_exchange_strong_weakptr<T>();
- LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_W_CXS)
-#undef LIBCXX_ATOMIC_SP_RUN_W_CXS
+ ForEachSmartPtrType{}.template operator()<TestCompareExchangeStrongWeak>();
return 0;
}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/compare_exchange_weak.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/compare_exchange_weak.pass.cpp
index f44450b46476c..028b40fd13f50 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/compare_exchange_weak.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/compare_exchange_weak.pass.cpp
@@ -21,7 +21,6 @@
template <class T>
void test_compare_exchange_weak_weakptr() {
- using libcxx_atomic_smart_ptr_test::SpValues;
auto sp1 = SpValues<T>::state_a();
auto sp2 = SpValues<T>::state_b();
std::weak_ptr<T> wp1 = sp1;
@@ -37,7 +36,7 @@ void test_compare_exchange_weak_weakptr() {
assert(ok);
auto locked = a.load().lock();
assert(locked && *locked == *sp2);
- ASSERT_NOEXCEPT(a.compare_exchange_weak(expected, std::weak_ptr<T>(wp2)));
+ static_assert(noexcept(a.compare_exchange_weak(expected, std::weak_ptr<T>(wp2))));
}
{
@@ -61,9 +60,12 @@ void test_compare_exchange_weak_weakptr() {
}
}
+template <class T>
+struct TestCompareExchangeWeakWeak {
+ void operator()() const { test_compare_exchange_weak_weakptr<T>(); }
+};
+
int main(int, char**) {
-#define LIBCXX_ATOMIC_SP_RUN_W_CXW(T) test_compare_exchange_weak_weakptr<T>();
- LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_W_CXW)
-#undef LIBCXX_ATOMIC_SP_RUN_W_CXW
+ ForEachSmartPtrType{}.template operator()<TestCompareExchangeWeakWeak>();
return 0;
}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/convert.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/convert.pass.cpp
index c74d5f4925641..110491bca6b09 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/convert.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/convert.pass.cpp
@@ -20,7 +20,6 @@
template <class T>
void test_convert_weak() {
- using libcxx_atomic_smart_ptr_test::SpValues;
auto sp = SpValues<T>::state_a();
std::weak_ptr<T> wp = sp;
const std::atomic<std::weak_ptr<T>> a((std::weak_ptr<T>(wp)));
@@ -33,12 +32,15 @@ void test_convert_weak() {
auto locked2 = w2.lock();
assert(locked2 && *locked2 == *sp);
- ASSERT_NOEXCEPT(static_cast<std::weak_ptr<T>>(a));
+ static_assert(noexcept(static_cast<std::weak_ptr<T>>(a)));
}
+template <class T>
+struct TestConvertWeak {
+ void operator()() const { test_convert_weak<T>(); }
+};
+
int main(int, char**) {
-#define LIBCXX_ATOMIC_SP_RUN_W_CONVERT(T) test_convert_weak<T>();
- LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_W_CONVERT)
-#undef LIBCXX_ATOMIC_SP_RUN_W_CONVERT
+ ForEachSmartPtrType{}.template operator()<TestConvertWeak>();
return 0;
}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/exchange.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/exchange.pass.cpp
index 2c777efaabb5b..de4677fc1c166 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/exchange.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/exchange.pass.cpp
@@ -20,7 +20,6 @@
template <class T>
void test_exchange_weak() {
- using libcxx_atomic_smart_ptr_test::SpValues;
auto sp1 = SpValues<T>::state_a();
auto sp2 = SpValues<T>::state_b();
std::weak_ptr<T> wp1 = sp1;
@@ -46,12 +45,15 @@ void test_exchange_weak() {
}
assert(a.load().expired());
- ASSERT_NOEXCEPT(a.exchange(std::weak_ptr<T>(wp1)));
+ static_assert(noexcept(a.exchange(std::weak_ptr<T>(wp1))));
}
+template <class T>
+struct TestExchangeWeak {
+ void operator()() const { test_exchange_weak<T>(); }
+};
+
int main(int, char**) {
-#define LIBCXX_ATOMIC_SP_RUN_W_EXCHANGE(T) test_exchange_weak<T>();
- LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_W_EXCHANGE)
-#undef LIBCXX_ATOMIC_SP_RUN_W_EXCHANGE
+ ForEachSmartPtrType{}.template operator()<TestExchangeWeak>();
return 0;
}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/is_always_lock_free.compile.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/is_always_lock_free.pass.cpp
similarity index 78%
rename from libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/is_always_lock_free.compile.pass.cpp
rename to libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/is_always_lock_free.pass.cpp
index ceb3dbce26c29..2f8a679f21fab 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/is_always_lock_free.compile.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/is_always_lock_free.pass.cpp
@@ -28,13 +28,16 @@ void check() {
const A awp;
std::same_as<bool> decltype(auto) lf = awp.is_lock_free();
(void)lf;
- ASSERT_NOEXCEPT(A::is_always_lock_free);
- ASSERT_NOEXCEPT(awp.is_lock_free());
+ static_assert(noexcept(A::is_always_lock_free));
+ static_assert(noexcept(awp.is_lock_free()));
}
+template <class T>
+struct TestIsAlwaysLockFreeWeak {
+ void operator()() const { check<T>(); }
+};
+
int main(int, char**) {
-#define LIBCXX_ATOMIC_SP_RUN_W_IALF(T) check<T>();
- LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_W_IALF)
-#undef LIBCXX_ATOMIC_SP_RUN_W_IALF
+ ForEachSmartPtrType{}.template operator()<TestIsAlwaysLockFreeWeak>();
return 0;
}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/is_lock_free.compile.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/is_lock_free.pass.cpp
similarity index 75%
rename from libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/is_lock_free.compile.pass.cpp
rename to libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/is_lock_free.pass.cpp
index 55421abb2026b..15c9b81f34e80 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/is_lock_free.compile.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/is_lock_free.pass.cpp
@@ -22,13 +22,15 @@ void check(const std::atomic<std::weak_ptr<T>>& awp) noexcept {
static_assert(!std::atomic<std::weak_ptr<T>>::is_always_lock_free);
std::same_as<bool> decltype(auto) lf = awp.is_lock_free();
(void)lf;
- ASSERT_SAME_TYPE(decltype(awp.is_lock_free()), bool);
- ASSERT_NOEXCEPT(awp.is_lock_free());
+ static_assert(noexcept(awp.is_lock_free()));
}
+template <class T>
+struct TestIsLockFreeWeak {
+ void operator()() const { check<T>(std::atomic<std::weak_ptr<T>>()); }
+};
+
int main(int, char**) {
-#define LIBCXX_ATOMIC_SP_RUN_W_ILF(T) check<T>(std::atomic<std::weak_ptr<T>>());
- LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_W_ILF)
-#undef LIBCXX_ATOMIC_SP_RUN_W_ILF
+ ForEachSmartPtrType{}.template operator()<TestIsLockFreeWeak>();
return 0;
}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/load.compile.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/load.pass.cpp
similarity index 68%
rename from libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/load.compile.pass.cpp
rename to libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/load.pass.cpp
index b372c94cd1597..84206c5832636 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/load.compile.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/load.pass.cpp
@@ -20,25 +20,27 @@
template <class T>
void check(const std::atomic<std::weak_ptr<T>>& awp) {
std::same_as<std::weak_ptr<T>> decltype(auto) no_arg = awp.load();
- ASSERT_SAME_TYPE(decltype(awp.load()), std::weak_ptr<T>);
- ASSERT_NOEXCEPT(awp.load());
+ static_assert(noexcept(awp.load()));
std::same_as<std::weak_ptr<T>> decltype(auto) with_order = awp.load(std::memory_order_seq_cst);
- ASSERT_SAME_TYPE(decltype(awp.load(std::memory_order_acquire)), std::weak_ptr<T>);
- ASSERT_NOEXCEPT(awp.load(std::memory_order_seq_cst));
+ static_assert(noexcept(awp.load(std::memory_order_seq_cst)));
static_cast<void>(no_arg);
static_cast<void>(with_order);
{
const std::atomic<std::weak_ptr<T>> const_a;
static_assert(noexcept(const_a.load()));
- ASSERT_SAME_TYPE(decltype(const_a.load()), std::weak_ptr<T>);
+ std::same_as<std::weak_ptr<T>> decltype(auto) loaded = const_a.load();
+ (void)loaded;
}
}
+template <class T>
+struct TestLoadWeak {
+ void operator()() const { check<T>(std::atomic<std::weak_ptr<T>>()); }
+};
+
int main(int, char**) {
-#define LIBCXX_ATOMIC_SP_RUN_W_LOAD(T) check<T>(std::atomic<std::weak_ptr<T>>());
- LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_W_LOAD)
-#undef LIBCXX_ATOMIC_SP_RUN_W_LOAD
+ ForEachSmartPtrType{}.template operator()<TestLoadWeak>();
return 0;
}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/notify_all.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/notify_all.pass.cpp
index 3211c651143f3..29e7f78b275db 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/notify_all.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/notify_all.pass.cpp
@@ -21,10 +21,9 @@
template <class T>
void test_notify_all_weak() {
- using libcxx_atomic_smart_ptr_test::SpValues;
std::atomic<std::weak_ptr<T>> a;
- ASSERT_NOEXCEPT(a.notify_all());
+ static_assert(noexcept(a.notify_all()));
#if __cpp_lib_atomic_wait >= 201907L
auto sp1 = SpValues<T>::state_a();
@@ -61,9 +60,12 @@ void test_notify_all_weak() {
#endif
}
+template <class T>
+struct TestNotifyAllWeak {
+ void operator()() const { test_notify_all_weak<T>(); }
+};
+
int main(int, char**) {
-#define LIBCXX_ATOMIC_SP_RUN_W_NA(T) test_notify_all_weak<T>();
- LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_W_NA)
-#undef LIBCXX_ATOMIC_SP_RUN_W_NA
+ ForEachSmartPtrType{}.template operator()<TestNotifyAllWeak>();
return 0;
}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/notify_one.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/notify_one.pass.cpp
index 440aab1d3b5e7..58fa25e9dda7f 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/notify_one.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/notify_one.pass.cpp
@@ -21,10 +21,9 @@
template <class T>
void test_notify_one_weak() {
- using libcxx_atomic_smart_ptr_test::SpValues;
std::atomic<std::weak_ptr<T>> a;
- ASSERT_NOEXCEPT(a.notify_one());
+ static_assert(noexcept(a.notify_one()));
#if __cpp_lib_atomic_wait >= 201907L
auto sp1 = SpValues<T>::state_a();
@@ -49,9 +48,12 @@ void test_notify_one_weak() {
#endif
}
+template <class T>
+struct TestNotifyOneWeak {
+ void operator()() const { test_notify_one_weak<T>(); }
+};
+
int main(int, char**) {
-#define LIBCXX_ATOMIC_SP_RUN_W_N1(T) test_notify_one_weak<T>();
- LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_W_N1)
-#undef LIBCXX_ATOMIC_SP_RUN_W_N1
+ ForEachSmartPtrType{}.template operator()<TestNotifyOneWeak>();
return 0;
}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/store.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/store.pass.cpp
index 35baa907c1437..1f50b44b84116 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/store.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/store.pass.cpp
@@ -20,7 +20,6 @@
template <class T>
void test_store_weak() {
- using libcxx_atomic_smart_ptr_test::SpValues;
auto sp1 = SpValues<T>::state_a();
auto sp2 = SpValues<T>::state_b();
std::weak_ptr<T> wp1 = sp1;
@@ -39,12 +38,15 @@ void test_store_weak() {
assert(locked && *locked == *sp2);
}
- ASSERT_NOEXCEPT(a.store(std::weak_ptr<T>(wp1)));
+ static_assert(noexcept(a.store(std::weak_ptr<T>(wp1))));
}
+template <class T>
+struct TestStoreWeak {
+ void operator()() const { test_store_weak<T>(); }
+};
+
int main(int, char**) {
-#define LIBCXX_ATOMIC_SP_RUN_W_STORE(T) test_store_weak<T>();
- LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_W_STORE)
-#undef LIBCXX_ATOMIC_SP_RUN_W_STORE
+ ForEachSmartPtrType{}.template operator()<TestStoreWeak>();
return 0;
}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/types.compile.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/types.pass.cpp
similarity index 86%
rename from libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/types.compile.pass.cpp
rename to libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/types.pass.cpp
index 40e0f4d5e38d6..4c1a60ea2e78d 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/types.compile.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/types.pass.cpp
@@ -46,7 +46,7 @@ void traits() {
}
{
- auto sp = libcxx_atomic_smart_ptr_test::SpValues<T>::state_a();
+ auto sp = SpValues<T>::state_a();
std::weak_ptr<T> wp = sp;
A a((std::weak_ptr<T>(wp)));
auto locked = a.load().lock();
@@ -54,9 +54,12 @@ void traits() {
}
}
+template <class T>
+struct TestTraitsWeak {
+ void operator()() const { traits<T>(); }
+};
+
int main(int, char**) {
-#define LIBCXX_ATOMIC_SP_RUN_W_TRAITS(T) traits<T>();
- LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_W_TRAITS)
-#undef LIBCXX_ATOMIC_SP_RUN_W_TRAITS
+ ForEachSmartPtrType{}.template operator()<TestTraitsWeak>();
return 0;
}
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/wait.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/wait.pass.cpp
index f840c4ec00d32..20b6474121f07 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/wait.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/wait.pass.cpp
@@ -21,7 +21,6 @@
template <class T>
void test_wait_weak() {
- using libcxx_atomic_smart_ptr_test::SpValues;
std::atomic<std::weak_ptr<T>> a;
#if __cpp_lib_atomic_wait >= 201907L
@@ -53,12 +52,15 @@ void test_wait_weak() {
}
#endif
- ASSERT_NOEXCEPT(a.wait(std::weak_ptr<T>(), std::memory_order_seq_cst));
+ static_assert(noexcept(a.wait(std::weak_ptr<T>(), std::memory_order_seq_cst)));
}
+template <class T>
+struct TestWaitWeak {
+ void operator()() const { test_wait_weak<T>(); }
+};
+
int main(int, char**) {
-#define LIBCXX_ATOMIC_SP_RUN_W_WAIT(T) test_wait_weak<T>();
- LIBCXX_ATOMIC_SP_FOR_ALL_RUNTIME_TYPES(LIBCXX_ATOMIC_SP_RUN_W_WAIT)
-#undef LIBCXX_ATOMIC_SP_RUN_W_WAIT
+ ForEachSmartPtrType{}.template operator()<TestWaitWeak>();
return 0;
}
>From 9f1af1e17f8d0b41192c63c7806462a0bb964392 Mon Sep 17 00:00:00 2001
From: ViNN280801 <vladislav.semykin at gmail.com>
Date: Sat, 2 May 2026 21:21:38 +0300
Subject: [PATCH 12/14] [libcxx] Included <concepts> for Armv7 and Armv8
(including -fno-exceptions); TODO: figure out why stress test on AIX (32 and
64 bit) is timeouting
Signed-off-by: ViNN280801 <vladislav.semykin at gmail.com>
---
.../utilities/memory/util.smartptr/atomic/is_lock_free.pass.cpp | 1 +
.../util.smartptr/atomic/shared/compare_exchange_strong.pass.cpp | 1 +
.../memory/util.smartptr/atomic/shared/convert.pass.cpp | 1 +
.../memory/util.smartptr/atomic/shared/exchange.pass.cpp | 1 +
.../util.smartptr/atomic/shared/is_always_lock_free.pass.cpp | 1 +
.../utilities/memory/util.smartptr/atomic/shared/load.pass.cpp | 1 +
.../util.smartptr/atomic/weak/compare_exchange_strong.pass.cpp | 1 +
.../utilities/memory/util.smartptr/atomic/weak/convert.pass.cpp | 1 +
.../utilities/memory/util.smartptr/atomic/weak/exchange.pass.cpp | 1 +
.../util.smartptr/atomic/weak/is_always_lock_free.pass.cpp | 1 +
.../memory/util.smartptr/atomic/weak/is_lock_free.pass.cpp | 1 +
.../std/utilities/memory/util.smartptr/atomic/weak/load.pass.cpp | 1 +
12 files changed, 12 insertions(+)
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/is_lock_free.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/is_lock_free.pass.cpp
index 574a89fababb3..16ccf9bac493b 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/atomic/is_lock_free.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/is_lock_free.pass.cpp
@@ -16,6 +16,7 @@
// bool is_lock_free() const noexcept;
#include <atomic>
+#include <concepts> // needed on Armv7/Armv8 with -fmodules
#include <memory>
#include "atomic_smart_ptr_test_types.h"
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/compare_exchange_strong.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/compare_exchange_strong.pass.cpp
index bb10b75746c2d..a94a9563839c8 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/compare_exchange_strong.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/compare_exchange_strong.pass.cpp
@@ -14,6 +14,7 @@
#include <atomic>
#include <cassert>
+#include <concepts> // needed on Armv7/Armv8 with -fmodules
#include <memory>
#include "../atomic_smart_ptr_test_types.h"
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/convert.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/convert.pass.cpp
index 85de5e07b7058..7c5ddcea6c8c7 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/convert.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/convert.pass.cpp
@@ -13,6 +13,7 @@
#include <atomic>
#include <cassert>
+#include <concepts> // needed on Armv7/Armv8 with -fmodules
#include <memory>
#include "../atomic_smart_ptr_test_types.h"
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/exchange.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/exchange.pass.cpp
index 5c5339389ac7b..39fa057141d92 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/exchange.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/exchange.pass.cpp
@@ -13,6 +13,7 @@
#include <atomic>
#include <cassert>
+#include <concepts> // needed on Armv7/Armv8 with -fmodules
#include <memory>
#include "../atomic_smart_ptr_test_types.h"
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/is_always_lock_free.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/is_always_lock_free.pass.cpp
index 879fd1f204ceb..ac685fbf8bfc1 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/is_always_lock_free.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/is_always_lock_free.pass.cpp
@@ -13,6 +13,7 @@
// bool is_lock_free() const noexcept;
#include <atomic>
+#include <concepts> // needed on Armv7/Armv8 with -fmodules
#include <memory>
#include "../atomic_smart_ptr_test_types.h"
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/load.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/load.pass.cpp
index f6271923d3db8..2942cfe6c1017 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/load.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/shared/load.pass.cpp
@@ -12,6 +12,7 @@
// shared_ptr<T> load(memory_order order = memory_order::seq_cst) const noexcept;
#include <atomic>
+#include <concepts> // needed on Armv7/Armv8 with -fmodules
#include <memory>
#include "../atomic_smart_ptr_test_types.h"
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/compare_exchange_strong.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/compare_exchange_strong.pass.cpp
index 9d225ae0ba3f5..8f282baef9bb2 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/compare_exchange_strong.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/compare_exchange_strong.pass.cpp
@@ -14,6 +14,7 @@
#include <atomic>
#include <cassert>
+#include <concepts> // needed on Armv7/Armv8 with -fmodules
#include <memory>
#include "../atomic_smart_ptr_test_types.h"
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/convert.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/convert.pass.cpp
index 110491bca6b09..97d46023e4e37 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/convert.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/convert.pass.cpp
@@ -13,6 +13,7 @@
#include <atomic>
#include <cassert>
+#include <concepts> // needed on Armv7/Armv8 with -fmodules
#include <memory>
#include "../atomic_smart_ptr_test_types.h"
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/exchange.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/exchange.pass.cpp
index de4677fc1c166..d553dbc34390a 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/exchange.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/exchange.pass.cpp
@@ -13,6 +13,7 @@
#include <atomic>
#include <cassert>
+#include <concepts> // needed on Armv7/Armv8 with -fmodules
#include <memory>
#include "../atomic_smart_ptr_test_types.h"
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/is_always_lock_free.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/is_always_lock_free.pass.cpp
index 2f8a679f21fab..31b7fe877a92c 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/is_always_lock_free.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/is_always_lock_free.pass.cpp
@@ -13,6 +13,7 @@
// bool is_lock_free() const noexcept;
#include <atomic>
+#include <concepts> // needed on Armv7/Armv8 with -fmodules
#include <memory>
#include "../atomic_smart_ptr_test_types.h"
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/is_lock_free.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/is_lock_free.pass.cpp
index 15c9b81f34e80..f65b24d55449d 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/is_lock_free.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/is_lock_free.pass.cpp
@@ -12,6 +12,7 @@
// bool is_lock_free() const noexcept;
#include <atomic>
+#include <concepts> // needed on Armv7/Armv8 with -fmodules
#include <memory>
#include "../atomic_smart_ptr_test_types.h"
diff --git a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/load.pass.cpp b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/load.pass.cpp
index 84206c5832636..73fb38efdf9ac 100644
--- a/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/load.pass.cpp
+++ b/libcxx/test/std/utilities/memory/util.smartptr/atomic/weak/load.pass.cpp
@@ -12,6 +12,7 @@
// weak_ptr<T> load(memory_order order = memory_order::seq_cst) const noexcept;
#include <atomic>
+#include <concepts> // needed on Armv7/Armv8 with -fmodules
#include <memory>
#include "../atomic_smart_ptr_test_types.h"
>From 7817a3b7bdaae473fd490a82c9c1a3294b871a90 Mon Sep 17 00:00:00 2001
From: Vladislav Semykin <vladislav.semykin at gmail.com>
Date: Thu, 28 May 2026 22:13:59 +0300
Subject: [PATCH 13/14] [libc++] The '__to_failure_order' function has been
moved to a separate file to avoid CE on gcc 15 and make it reusable; Used
macro '_LIBCPP_{BEGIN|END}_EXPLICIT_ABI_ANNOTATIONS' from #193045 to avoid CE
Signed-off-by: Vladislav Semykin <vladislav.semykin at gmail.com>
---
libcxx/include/CMakeLists.txt | 1 +
libcxx/include/__atomic/atomic_sync_lite.h | 4 +++
libcxx/include/__atomic/support/c11.h | 8 +-----
libcxx/include/__atomic/to_failure_order.h | 30 +++++++++++++++++++++
libcxx/include/__memory/atomic_shared_ptr.h | 1 +
libcxx/include/__memory/shared_ptr.h | 8 +++---
libcxx/include/module.modulemap.in | 1 +
7 files changed, 42 insertions(+), 11 deletions(-)
create mode 100644 libcxx/include/__atomic/to_failure_order.h
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 48968128e47bd..0c7278c30330e 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -229,6 +229,7 @@ set(files
__atomic/support.h
__atomic/support/c11.h
__atomic/support/gcc.h
+ __atomic/to_failure_order.h
__atomic/to_gcc_order.h
__bit/bit_cast.h
__bit/bit_ceil.h
diff --git a/libcxx/include/__atomic/atomic_sync_lite.h b/libcxx/include/__atomic/atomic_sync_lite.h
index a0433e16b506a..8354d12bdea7a 100644
--- a/libcxx/include/__atomic/atomic_sync_lite.h
+++ b/libcxx/include/__atomic/atomic_sync_lite.h
@@ -20,6 +20,8 @@ _LIBCPP_BEGIN_NAMESPACE_STD
#if _LIBCPP_STD_VER >= 20 && _LIBCPP_HAS_THREADS
+_LIBCPP_BEGIN_EXPLICIT_ABI_ANNOTATIONS
+
# if !_LIBCPP_AVAILABILITY_HAS_NEW_SYNC
// Old dylib interface kept for backwards compatibility.
_LIBCPP_EXPORTED_FROM_ABI void __cxx_atomic_notify_one(void const volatile*) _NOEXCEPT;
@@ -38,6 +40,8 @@ __atomic_wait_global_table(void const* __address, __cxx_contention_t __monitor_v
_LIBCPP_AVAILABILITY_NEW_SYNC _LIBCPP_EXPORTED_FROM_ABI void __atomic_notify_one_global_table(void const*) _NOEXCEPT;
_LIBCPP_AVAILABILITY_NEW_SYNC _LIBCPP_EXPORTED_FROM_ABI void __atomic_notify_all_global_table(void const*) _NOEXCEPT;
+_LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
+
#endif // _LIBCPP_STD_VER >= 20 && _LIBCPP_HAS_THREADS
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 1ad299882a12a..84db7accc8c73 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -10,6 +10,7 @@
#define _LIBCPP___ATOMIC_SUPPORT_C11_H
#include <__atomic/memory_order.h>
+#include <__atomic/to_failure_order.h>
#include <__config>
#include <__cstddef/ptrdiff_t.h>
#include <__memory/addressof.h>
@@ -110,13 +111,6 @@ __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp>* __a, _Tp __value, memory_orde
std::addressof(__a->__a_value), __value, static_cast<__memory_order_underlying_t>(__order));
}
-_LIBCPP_HIDE_FROM_ABI inline _LIBCPP_CONSTEXPR memory_order __to_failure_order(memory_order __order) {
- // Avoid switch statement to make this a constexpr.
- return __order == memory_order_release
- ? memory_order_relaxed
- : (__order == memory_order_acq_rel ? memory_order_acquire : __order);
-}
-
template <class _Tp>
_LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_compare_exchange_strong(
__cxx_atomic_base_impl<_Tp> volatile* __a,
diff --git a/libcxx/include/__atomic/to_failure_order.h b/libcxx/include/__atomic/to_failure_order.h
new file mode 100644
index 0000000000000..7e7017ab5d8ac
--- /dev/null
+++ b/libcxx/include/__atomic/to_failure_order.h
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___ATOMIC_TO_FAILURE_ORDER_H
+#define _LIBCPP___ATOMIC_TO_FAILURE_ORDER_H
+
+#include <__atomic/memory_order.h>
+#include <__config>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+_LIBCPP_HIDE_FROM_ABI inline _LIBCPP_CONSTEXPR memory_order __to_failure_order(memory_order __order) {
+ // Avoid switch statement to make this a constexpr.
+ return __order == memory_order_release
+ ? memory_order_relaxed
+ : (__order == memory_order_acq_rel ? memory_order_acquire : __order);
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___ATOMIC_TO_FAILURE_ORDER_H
diff --git a/libcxx/include/__memory/atomic_shared_ptr.h b/libcxx/include/__memory/atomic_shared_ptr.h
index 1baa0060d3425..156885d9a688d 100644
--- a/libcxx/include/__memory/atomic_shared_ptr.h
+++ b/libcxx/include/__memory/atomic_shared_ptr.h
@@ -14,6 +14,7 @@
#include <__atomic/check_memory_order.h>
#include <__atomic/memory_order.h>
#include <__atomic/support.h>
+#include <__atomic/to_failure_order.h>
#include <__config>
#include <__cstddef/nullptr_t.h>
#include <__memory/shared_count.h>
diff --git a/libcxx/include/__memory/shared_ptr.h b/libcxx/include/__memory/shared_ptr.h
index 45a82abebbb15..7466a2ceb620a 100644
--- a/libcxx/include/__memory/shared_ptr.h
+++ b/libcxx/include/__memory/shared_ptr.h
@@ -733,8 +733,8 @@ struct __unbounded_array_control_block<_Tp[], _Alloc> : __shared_weak_count {
// for the whole allocation to be a multiple of _Tp's alignment. That formula is taken from [1].
//
// [1]: https://en.wikipedia.org/wiki/Data_structure_alignment#Computing_padding
- size_t __bytes = __elements == 0 ? sizeof(__unbounded_array_control_block)
- : (__elements - 1) * sizeof(_Tp) + sizeof(__unbounded_array_control_block);
+ size_t __bytes = __elements == 0 ? sizeof(__unbounded_array_control_block)
+ : (__elements - 1) * sizeof(_Tp) + sizeof(__unbounded_array_control_block);
constexpr size_t __align = alignof(__unbounded_array_control_block);
return (__bytes + __align - 1) & ~(__align - 1);
}
@@ -1081,8 +1081,8 @@ template <class _Tp, class _Up>
#endif
template <class _Tp, class _Up>
-[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp>
-dynamic_pointer_cast(const shared_ptr<_Up>& __r) _NOEXCEPT {
+[[__nodiscard__]] inline
+ _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> dynamic_pointer_cast(const shared_ptr<_Up>& __r) _NOEXCEPT {
typedef typename shared_ptr<_Tp>::element_type _ET;
_ET* __p = dynamic_cast<_ET*>(__r.get());
return __p ? shared_ptr<_Tp>(__r, __p) : shared_ptr<_Tp>();
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 704af8af9a5e2..3238146851ede 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -888,6 +888,7 @@ module std [system] {
module is_always_lock_free { header "__atomic/is_always_lock_free.h" }
module kill_dependency { header "__atomic/kill_dependency.h" }
module memory_order { header "__atomic/memory_order.h" }
+ module to_failure_order { header "__atomic/to_failure_order.h" }
module to_gcc_order { header "__atomic/to_gcc_order.h" }
module support {
>From fdad7d0ab977a656d8bad32245f7589675492b26 Mon Sep 17 00:00:00 2001
From: Vladislav Semykin <vladislav.semykin at gmail.com>
Date: Sat, 30 May 2026 18:39:48 +0300
Subject: [PATCH 14/14] [libc++] Implemented lock-free DWCAS-based approach
Signed-off-by: Vladislav Semykin <vladislav.semykin at gmail.com>
---
libcxx/include/CMakeLists.txt | 2 +
libcxx/include/__memory/atomic_shared_ptr.h | 481 +-----------
.../__memory/atomic_shared_ptr_lock_based.h | 491 ++++++++++++
.../__memory/atomic_shared_ptr_lock_free.h | 710 ++++++++++++++++++
libcxx/include/module.modulemap.in | 2 +
.../atomic_shared_ptr_dwcas.pass.cpp | 31 +
6 files changed, 1250 insertions(+), 467 deletions(-)
create mode 100644 libcxx/include/__memory/atomic_shared_ptr_lock_based.h
create mode 100644 libcxx/include/__memory/atomic_shared_ptr_lock_free.h
create mode 100644 libcxx/test/libcxx/utilities/memory/util.smartptr/atomic_shared_ptr_dwcas.pass.cpp
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 0c7278c30330e..cef420a25790c 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -599,6 +599,8 @@ set(files
__memory/array_cookie.h
__memory/assume_aligned.h
__memory/atomic_shared_ptr.h
+ __memory/atomic_shared_ptr_lock_based.h
+ __memory/atomic_shared_ptr_lock_free.h
__memory/auto_ptr.h
__memory/compressed_pair.h
__memory/concepts.h
diff --git a/libcxx/include/__memory/atomic_shared_ptr.h b/libcxx/include/__memory/atomic_shared_ptr.h
index 156885d9a688d..273b8da9140c8 100644
--- a/libcxx/include/__memory/atomic_shared_ptr.h
+++ b/libcxx/include/__memory/atomic_shared_ptr.h
@@ -10,483 +10,30 @@
#ifndef _LIBCPP___MEMORY_ATOMIC_SHARED_PTR_H
#define _LIBCPP___MEMORY_ATOMIC_SHARED_PTR_H
-#include <__atomic/atomic_sync_lite.h>
-#include <__atomic/check_memory_order.h>
-#include <__atomic/memory_order.h>
-#include <__atomic/support.h>
-#include <__atomic/to_failure_order.h>
#include <__config>
-#include <__cstddef/nullptr_t.h>
-#include <__memory/shared_count.h>
-#include <__utility/move.h>
-
-#include <cstdint>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif
-#if defined(__SANITIZE_THREAD__) || (defined(__has_feature) && __has_feature(thread_sanitizer))
-# if __has_include(<sanitizer/tsan_interface.h>)
-# include <sanitizer/tsan_interface.h>
-# define _LIBCPP_ATOMIC_SHARED_PTR_TSAN 1
+// The lock-free path requires a hardware 16-byte CAS to update both fields of
+// the (ptr, ctrl) pair as one linearizable step; without it the spinlock path
+// is the only correct option. is_always_lock_free stays false because the same
+// source built without cx16/LSE flags must still see a non-lock-free type.
+#if !defined(_LIBCPP_HAS_LOCKFREE_ATOMIC_SHARED_PTR)
+# if _LIBCPP_HAS_THREADS && _LIBCPP_HAS_INT128 && \
+ ((defined(__x86_64__) && defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_16)) || \
+ (defined(__aarch64__) && defined(__ARM_FEATURE_ATOMICS)))
+# define _LIBCPP_HAS_LOCKFREE_ATOMIC_SHARED_PTR 1
+# else
+# define _LIBCPP_HAS_LOCKFREE_ATOMIC_SHARED_PTR 0
# endif
#endif
-_LIBCPP_PUSH_MACROS
-#include <__undef_macros>
-
-// TSAN annotations model the lock-bit protocol on __ctrl_.
-#if defined(_LIBCPP_ATOMIC_SHARED_PTR_TSAN)
-# define _LIBCPP_ATOMIC_SP_TSAN_PRE_LOCK(addr) \
- ::__tsan_mutex_pre_lock(reinterpret_cast<void*>(const_cast<__cxx_atomic_impl<uintptr_t>*>(addr)), 0)
-# define _LIBCPP_ATOMIC_SP_TSAN_POST_LOCK(addr) \
- ::__tsan_mutex_post_lock(reinterpret_cast<void*>(const_cast<__cxx_atomic_impl<uintptr_t>*>(addr)), 0, 0)
-# define _LIBCPP_ATOMIC_SP_TSAN_PRE_UNLOCK(addr) \
- ::__tsan_mutex_pre_unlock(reinterpret_cast<void*>(const_cast<__cxx_atomic_impl<uintptr_t>*>(addr)), 0)
-# define _LIBCPP_ATOMIC_SP_TSAN_POST_UNLOCK(addr) \
- ::__tsan_mutex_post_unlock(reinterpret_cast<void*>(const_cast<__cxx_atomic_impl<uintptr_t>*>(addr)), 0)
+#if _LIBCPP_HAS_LOCKFREE_ATOMIC_SHARED_PTR
+# include <__memory/atomic_shared_ptr_lock_free.h>
#else
-# define _LIBCPP_ATOMIC_SP_TSAN_PRE_LOCK(addr) ((void)(addr))
-# define _LIBCPP_ATOMIC_SP_TSAN_POST_LOCK(addr) ((void)(addr))
-# define _LIBCPP_ATOMIC_SP_TSAN_PRE_UNLOCK(addr) ((void)(addr))
-# define _LIBCPP_ATOMIC_SP_TSAN_POST_UNLOCK(addr) ((void)(addr))
+# include <__memory/atomic_shared_ptr_lock_based.h>
#endif
-_LIBCPP_BEGIN_NAMESPACE_STD
-
-#if _LIBCPP_STD_VER >= 20 && _LIBCPP_HAS_THREADS && _LIBCPP_HAS_ATOMIC_HEADER
-
-template <class _Tp>
-class shared_ptr;
-
-template <class _Tp>
-class weak_ptr;
-
-template <class _Tp>
-struct atomic;
-
-// Split state into pointer word and control word.
-// The control word stores control-block pointer plus lock/notify bits.
-struct __atomic_smart_ptr_storage {
- static constexpr uintptr_t __lock_bit_ = uintptr_t{1};
- static constexpr uintptr_t __notify_bit_ = uintptr_t{2};
- static constexpr uintptr_t __ptr_mask_ = ~(__lock_bit_ | __notify_bit_);
-
- _LIBCPP_HIDE_FROM_ABI static uintptr_t __encode(__shared_weak_count* __ctrl, uintptr_t __bits) noexcept {
- return (reinterpret_cast<uintptr_t>(__ctrl) & __ptr_mask_) | (__bits & ~__ptr_mask_);
- }
-
- _LIBCPP_HIDE_FROM_ABI static __shared_weak_count* __decode(uintptr_t __word) noexcept {
- return reinterpret_cast<__shared_weak_count*>(__word & __ptr_mask_);
- }
-
- _LIBCPP_HIDE_FROM_ABI static bool __has_lock(uintptr_t __word) noexcept { return (__word & __lock_bit_) != 0; }
- _LIBCPP_HIDE_FROM_ABI static bool __has_notify(uintptr_t __word) noexcept { return (__word & __notify_bit_) != 0; }
-};
-
-_LIBCPP_HIDE_FROM_ABI inline void __atomic_smart_ptr_notify_one(const void* __address) noexcept {
-# if _LIBCPP_AVAILABILITY_HAS_NEW_SYNC
- std::__atomic_notify_one_global_table(__address);
-# else
- std::__cxx_atomic_notify_one(reinterpret_cast<void const volatile*>(__address));
-# endif
-}
-
-_LIBCPP_HIDE_FROM_ABI inline void __atomic_smart_ptr_notify_all(const void* __address) noexcept {
-# if _LIBCPP_AVAILABILITY_HAS_NEW_SYNC
- std::__atomic_notify_all_global_table(__address);
-# else
- std::__cxx_atomic_notify_all(reinterpret_cast<void const volatile*>(__address));
-# endif
-}
-
-template <class _Poll>
-_LIBCPP_HIDE_FROM_ABI inline void
-__atomic_smart_ptr_wait_on_address(const void* __address, const _Poll& __poll) noexcept {
- while (!__poll()) {
-# if _LIBCPP_AVAILABILITY_HAS_NEW_SYNC
- auto __monitor_value = std::__atomic_monitor_global(__address);
- if (__poll())
- return;
- std::__atomic_wait_global_table(__address, __monitor_value);
-# else
- void const volatile* __volatile_address = reinterpret_cast<void const volatile*>(__address);
- auto __monitor_value = std::__libcpp_atomic_monitor(__volatile_address);
- if (__poll())
- return;
- std::__libcpp_atomic_wait(__volatile_address, __monitor_value);
-# endif
- }
-}
-
-template <class _Element>
-struct __atomic_smart_ptr_fields {
- mutable __cxx_atomic_impl<_Element*> __ptr_;
- mutable __cxx_atomic_impl<uintptr_t> __ctrl_;
-
- _LIBCPP_HIDE_FROM_ABI __atomic_smart_ptr_fields(_Element* __p, __shared_weak_count* __c) noexcept
- : __ptr_(__p), __ctrl_(__atomic_smart_ptr_storage::__encode(__c, 0)) {}
-
- _LIBCPP_HIDE_FROM_ABI const void* __ctrl_address() const noexcept {
- return static_cast<const void*>(__builtin_addressof(__ctrl_));
- }
-
- // Acquire lock bit on __ctrl_. Contended path sets notify bit and waits.
- _LIBCPP_HIDE_FROM_ABI void __lock() const noexcept {
- _LIBCPP_ATOMIC_SP_TSAN_PRE_LOCK(__builtin_addressof(__ctrl_));
- uintptr_t __expected = std::__cxx_atomic_load(__builtin_addressof(__ctrl_), memory_order_relaxed);
- for (;;) {
- if (!__atomic_smart_ptr_storage::__has_lock(__expected)) {
- uintptr_t __desired = __expected | __atomic_smart_ptr_storage::__lock_bit_;
- if (std::__cxx_atomic_compare_exchange_weak(
- __builtin_addressof(__ctrl_),
- __builtin_addressof(__expected),
- __desired,
- memory_order_acquire,
- memory_order_relaxed)) {
- _LIBCPP_ATOMIC_SP_TSAN_POST_LOCK(__builtin_addressof(__ctrl_));
- return;
- }
- continue;
- }
-
- uintptr_t __with_notify = __expected | __atomic_smart_ptr_storage::__notify_bit_;
- if (!__atomic_smart_ptr_storage::__has_notify(__expected)) {
- if (!std::__cxx_atomic_compare_exchange_weak(
- __builtin_addressof(__ctrl_),
- __builtin_addressof(__expected),
- __with_notify,
- memory_order_relaxed,
- memory_order_relaxed))
- continue;
- __expected = __with_notify;
- }
-
- std::__atomic_smart_ptr_wait_on_address(__ctrl_address(), [&] {
- __expected = std::__cxx_atomic_load(__builtin_addressof(__ctrl_), memory_order_relaxed);
- return !__atomic_smart_ptr_storage::__has_lock(__expected);
- });
- }
- }
-
- // Publish new control pointer, clear bits, and notify waiters if needed.
- _LIBCPP_HIDE_FROM_ABI void __unlock(__shared_weak_count* __ctrl_to_publish) const noexcept {
- _LIBCPP_ATOMIC_SP_TSAN_PRE_UNLOCK(__builtin_addressof(__ctrl_));
- uintptr_t __new_word = __atomic_smart_ptr_storage::__encode(__ctrl_to_publish, 0);
- uintptr_t __previous = std::__cxx_atomic_exchange(__builtin_addressof(__ctrl_), __new_word, memory_order_release);
- if (__atomic_smart_ptr_storage::__has_notify(__previous))
- std::__atomic_smart_ptr_notify_all(__ctrl_address());
- _LIBCPP_ATOMIC_SP_TSAN_POST_UNLOCK(__builtin_addressof(__ctrl_));
- }
-};
-
-// [util.smartptr.atomic.shared]: same stored pointer and same ownership, or both empty.
-template <class _Element>
-_LIBCPP_HIDE_FROM_ABI inline bool __atomic_smart_ptr_equivalent(
- _Element* __ptr,
- __shared_weak_count* __ctrl,
- _Element* __expected_ptr,
- __shared_weak_count* __expected_ctrl) noexcept {
- if (__ctrl == nullptr && __expected_ctrl == nullptr)
- return true;
- return __ptr == __expected_ptr && __ctrl == __expected_ctrl;
-}
-
-template <class _Tp>
-struct atomic<shared_ptr<_Tp>> {
- using value_type = shared_ptr<_Tp>;
-
- static constexpr bool is_always_lock_free = false;
-
- _LIBCPP_HIDE_FROM_ABI atomic() noexcept : __fields_(nullptr, nullptr) {}
- _LIBCPP_HIDE_FROM_ABI constexpr atomic(nullptr_t) noexcept : __fields_(nullptr, nullptr) {}
- _LIBCPP_HIDE_FROM_ABI atomic(shared_ptr<_Tp> __desired) noexcept : __fields_(__desired.__ptr_, __desired.__cntrl_) {
- __desired.__ptr_ = nullptr;
- __desired.__cntrl_ = nullptr;
- }
-
- atomic(const atomic&) = delete;
- atomic& operator=(const atomic&) = delete;
-
- _LIBCPP_HIDE_FROM_ABI ~atomic() {
- if (auto* __c = __atomic_smart_ptr_storage::__decode(
- std::__cxx_atomic_load(__builtin_addressof(__fields_.__ctrl_), memory_order_relaxed)))
- __c->__release_shared();
- }
-
- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool is_lock_free() const noexcept { return false; }
-
- _LIBCPP_HIDE_FROM_ABI void operator=(shared_ptr<_Tp> __desired) noexcept { store(std::move(__desired)); }
- _LIBCPP_HIDE_FROM_ABI void operator=(nullptr_t) noexcept { store(nullptr); }
- _LIBCPP_HIDE_FROM_ABI operator shared_ptr<_Tp>() const noexcept { return load(); }
-
- _LIBCPP_HIDE_FROM_ABI void store(shared_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) noexcept
- _LIBCPP_CHECK_STORE_MEMORY_ORDER(__m) {
- (void)__m;
- _Tp* __desired_ptr = __desired.__ptr_;
- __shared_weak_count* __desired_c = __desired.__cntrl_;
- __desired.__ptr_ = nullptr;
- __desired.__cntrl_ = nullptr;
-
- __fields_.__lock();
- __shared_weak_count* __old_c = __atomic_smart_ptr_storage::__decode(
- std::__cxx_atomic_load(__builtin_addressof(__fields_.__ctrl_), memory_order_relaxed));
- std::__cxx_atomic_store(__builtin_addressof(__fields_.__ptr_), __desired_ptr, memory_order_relaxed);
- __fields_.__unlock(__desired_c);
-
- if (__old_c)
- __old_c->__release_shared();
- }
-
- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> load(memory_order __m = memory_order_seq_cst) const noexcept
- _LIBCPP_CHECK_LOAD_MEMORY_ORDER(__m) {
- (void)__m;
- __fields_.__lock();
- _Tp* __ptr = std::__cxx_atomic_load(__builtin_addressof(__fields_.__ptr_), memory_order_relaxed);
- __shared_weak_count* __c = __atomic_smart_ptr_storage::__decode(
- std::__cxx_atomic_load(__builtin_addressof(__fields_.__ctrl_), memory_order_relaxed));
- if (__c)
- __c->__add_shared();
- __fields_.__unlock(__c);
- return shared_ptr<_Tp>::__create_with_control_block(__ptr, __c);
- }
-
- _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp>
- exchange(shared_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) noexcept {
- (void)__m;
- _Tp* __desired_ptr = __desired.__ptr_;
- __shared_weak_count* __desired_c = __desired.__cntrl_;
- __desired.__ptr_ = nullptr;
- __desired.__cntrl_ = nullptr;
-
- __fields_.__lock();
- _Tp* __old_ptr = std::__cxx_atomic_load(__builtin_addressof(__fields_.__ptr_), memory_order_relaxed);
- __shared_weak_count* __old_c = __atomic_smart_ptr_storage::__decode(
- std::__cxx_atomic_load(__builtin_addressof(__fields_.__ctrl_), memory_order_relaxed));
- std::__cxx_atomic_store(__builtin_addressof(__fields_.__ptr_), __desired_ptr, memory_order_relaxed);
- __fields_.__unlock(__desired_c);
-
- return shared_ptr<_Tp>::__create_with_control_block(__old_ptr, __old_c);
- }
-
- _LIBCPP_HIDE_FROM_ABI bool compare_exchange_strong(
- shared_ptr<_Tp>& __expected, shared_ptr<_Tp> __desired, memory_order __success, memory_order __failure) noexcept
- _LIBCPP_CHECK_EXCHANGE_MEMORY_ORDER(__success, __failure) {
- (void)__success;
- (void)__failure;
- __fields_.__lock();
- _Tp* __cur_ptr = std::__cxx_atomic_load(__builtin_addressof(__fields_.__ptr_), memory_order_relaxed);
- __shared_weak_count* __cur_c = __atomic_smart_ptr_storage::__decode(
- std::__cxx_atomic_load(__builtin_addressof(__fields_.__ctrl_), memory_order_relaxed));
-
- if (__atomic_smart_ptr_equivalent(__cur_ptr, __cur_c, __expected.__ptr_, __expected.__cntrl_)) {
- _Tp* __desired_ptr = __desired.__ptr_;
- __shared_weak_count* __desired_c = __desired.__cntrl_;
- __desired.__ptr_ = nullptr;
- __desired.__cntrl_ = nullptr;
-
- std::__cxx_atomic_store(__builtin_addressof(__fields_.__ptr_), __desired_ptr, memory_order_relaxed);
- __fields_.__unlock(__desired_c);
- if (__cur_c)
- __cur_c->__release_shared();
- return true;
- }
-
- if (__cur_c)
- __cur_c->__add_shared();
- __fields_.__unlock(__cur_c);
- __expected = shared_ptr<_Tp>::__create_with_control_block(__cur_ptr, __cur_c);
- return false;
- }
-
- _LIBCPP_HIDE_FROM_ABI bool compare_exchange_strong(
- shared_ptr<_Tp>& __expected, shared_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) noexcept {
- return compare_exchange_strong(__expected, std::move(__desired), __m, std::__to_failure_order(__m));
- }
-
- _LIBCPP_HIDE_FROM_ABI bool compare_exchange_weak(
- shared_ptr<_Tp>& __expected, shared_ptr<_Tp> __desired, memory_order __success, memory_order __failure) noexcept
- _LIBCPP_CHECK_EXCHANGE_MEMORY_ORDER(__success, __failure) {
- return compare_exchange_strong(__expected, std::move(__desired), __success, __failure);
- }
-
- _LIBCPP_HIDE_FROM_ABI bool compare_exchange_weak(
- shared_ptr<_Tp>& __expected, shared_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) noexcept {
- return compare_exchange_strong(__expected, std::move(__desired), __m, std::__to_failure_order(__m));
- }
-
- // Wait until the stored value is not equivalent to __old.
- // __ctrl_ is the wait address; pointer changes are published with control updates.
- _LIBCPP_HIDE_FROM_ABI void wait(shared_ptr<_Tp> __old, memory_order __m = memory_order_seq_cst) const noexcept
- _LIBCPP_CHECK_WAIT_MEMORY_ORDER(__m) {
- _Tp* __old_ptr = __old.__ptr_;
- __shared_weak_count* __old_c = __old.__cntrl_;
-
- std::__atomic_smart_ptr_wait_on_address(__fields_.__ctrl_address(), [&] {
- uintptr_t __word = std::__cxx_atomic_load(__builtin_addressof(__fields_.__ctrl_), __m);
- __shared_weak_count* __cur_c = __atomic_smart_ptr_storage::__decode(__word);
- if (__cur_c != __old_c)
- return true;
- _Tp* __cur_ptr = std::__cxx_atomic_load(__builtin_addressof(__fields_.__ptr_), __m);
- return !__atomic_smart_ptr_equivalent(__cur_ptr, __cur_c, __old_ptr, __old_c);
- });
- }
-
- _LIBCPP_HIDE_FROM_ABI void notify_one() noexcept { std::__atomic_smart_ptr_notify_one(__fields_.__ctrl_address()); }
- _LIBCPP_HIDE_FROM_ABI void notify_all() noexcept { std::__atomic_smart_ptr_notify_all(__fields_.__ctrl_address()); }
-
-private:
- __atomic_smart_ptr_fields<_Tp> __fields_;
-};
-
-template <class _Tp>
-struct atomic<weak_ptr<_Tp>> {
- using value_type = weak_ptr<_Tp>;
-
- static constexpr bool is_always_lock_free = false;
-
- _LIBCPP_HIDE_FROM_ABI atomic() noexcept : __fields_(nullptr, nullptr) {}
- _LIBCPP_HIDE_FROM_ABI atomic(weak_ptr<_Tp> __desired) noexcept : __fields_(__desired.__ptr_, __desired.__cntrl_) {
- __desired.__ptr_ = nullptr;
- __desired.__cntrl_ = nullptr;
- }
-
- atomic(const atomic&) = delete;
- atomic& operator=(const atomic&) = delete;
-
- _LIBCPP_HIDE_FROM_ABI ~atomic() {
- if (auto* __c = __atomic_smart_ptr_storage::__decode(
- std::__cxx_atomic_load(__builtin_addressof(__fields_.__ctrl_), memory_order_relaxed)))
- __c->__release_weak();
- }
-
- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool is_lock_free() const noexcept { return false; }
-
- _LIBCPP_HIDE_FROM_ABI void operator=(weak_ptr<_Tp> __desired) noexcept { store(std::move(__desired)); }
- _LIBCPP_HIDE_FROM_ABI operator weak_ptr<_Tp>() const noexcept { return load(); }
-
- _LIBCPP_HIDE_FROM_ABI void store(weak_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) noexcept
- _LIBCPP_CHECK_STORE_MEMORY_ORDER(__m) {
- (void)__m;
- _Tp* __desired_ptr = __desired.__ptr_;
- __shared_weak_count* __desired_c = __desired.__cntrl_;
- __desired.__ptr_ = nullptr;
- __desired.__cntrl_ = nullptr;
-
- __fields_.__lock();
- __shared_weak_count* __old_c = __atomic_smart_ptr_storage::__decode(
- std::__cxx_atomic_load(__builtin_addressof(__fields_.__ctrl_), memory_order_relaxed));
- std::__cxx_atomic_store(__builtin_addressof(__fields_.__ptr_), __desired_ptr, memory_order_relaxed);
- __fields_.__unlock(__desired_c);
-
- if (__old_c)
- __old_c->__release_weak();
- }
-
- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI weak_ptr<_Tp> load(memory_order __m = memory_order_seq_cst) const noexcept
- _LIBCPP_CHECK_LOAD_MEMORY_ORDER(__m) {
- (void)__m;
- __fields_.__lock();
- _Tp* __ptr = std::__cxx_atomic_load(__builtin_addressof(__fields_.__ptr_), memory_order_relaxed);
- __shared_weak_count* __c = __atomic_smart_ptr_storage::__decode(
- std::__cxx_atomic_load(__builtin_addressof(__fields_.__ctrl_), memory_order_relaxed));
- if (__c)
- __c->__add_weak();
- __fields_.__unlock(__c);
- return weak_ptr<_Tp>::__create_with_control_block(__ptr, __c);
- }
-
- _LIBCPP_HIDE_FROM_ABI weak_ptr<_Tp>
- exchange(weak_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) noexcept {
- (void)__m;
- _Tp* __desired_ptr = __desired.__ptr_;
- __shared_weak_count* __desired_c = __desired.__cntrl_;
- __desired.__ptr_ = nullptr;
- __desired.__cntrl_ = nullptr;
-
- __fields_.__lock();
- _Tp* __old_ptr = std::__cxx_atomic_load(__builtin_addressof(__fields_.__ptr_), memory_order_relaxed);
- __shared_weak_count* __old_c = __atomic_smart_ptr_storage::__decode(
- std::__cxx_atomic_load(__builtin_addressof(__fields_.__ctrl_), memory_order_relaxed));
- std::__cxx_atomic_store(__builtin_addressof(__fields_.__ptr_), __desired_ptr, memory_order_relaxed);
- __fields_.__unlock(__desired_c);
-
- return weak_ptr<_Tp>::__create_with_control_block(__old_ptr, __old_c);
- }
-
- _LIBCPP_HIDE_FROM_ABI bool compare_exchange_strong(
- weak_ptr<_Tp>& __expected, weak_ptr<_Tp> __desired, memory_order __success, memory_order __failure) noexcept
- _LIBCPP_CHECK_EXCHANGE_MEMORY_ORDER(__success, __failure) {
- (void)__success;
- (void)__failure;
- __fields_.__lock();
- _Tp* __cur_ptr = std::__cxx_atomic_load(__builtin_addressof(__fields_.__ptr_), memory_order_relaxed);
- __shared_weak_count* __cur_c = __atomic_smart_ptr_storage::__decode(
- std::__cxx_atomic_load(__builtin_addressof(__fields_.__ctrl_), memory_order_relaxed));
-
- if (__atomic_smart_ptr_equivalent(__cur_ptr, __cur_c, __expected.__ptr_, __expected.__cntrl_)) {
- _Tp* __desired_ptr = __desired.__ptr_;
- __shared_weak_count* __desired_c = __desired.__cntrl_;
- __desired.__ptr_ = nullptr;
- __desired.__cntrl_ = nullptr;
-
- std::__cxx_atomic_store(__builtin_addressof(__fields_.__ptr_), __desired_ptr, memory_order_relaxed);
- __fields_.__unlock(__desired_c);
- if (__cur_c)
- __cur_c->__release_weak();
- return true;
- }
-
- if (__cur_c)
- __cur_c->__add_weak();
- __fields_.__unlock(__cur_c);
- __expected = weak_ptr<_Tp>::__create_with_control_block(__cur_ptr, __cur_c);
- return false;
- }
-
- _LIBCPP_HIDE_FROM_ABI bool compare_exchange_strong(
- weak_ptr<_Tp>& __expected, weak_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) noexcept {
- return compare_exchange_strong(__expected, std::move(__desired), __m, std::__to_failure_order(__m));
- }
-
- _LIBCPP_HIDE_FROM_ABI bool compare_exchange_weak(
- weak_ptr<_Tp>& __expected, weak_ptr<_Tp> __desired, memory_order __success, memory_order __failure) noexcept
- _LIBCPP_CHECK_EXCHANGE_MEMORY_ORDER(__success, __failure) {
- return compare_exchange_strong(__expected, std::move(__desired), __success, __failure);
- }
-
- _LIBCPP_HIDE_FROM_ABI bool compare_exchange_weak(
- weak_ptr<_Tp>& __expected, weak_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) noexcept {
- return compare_exchange_strong(__expected, std::move(__desired), __m, std::__to_failure_order(__m));
- }
-
- _LIBCPP_HIDE_FROM_ABI void wait(weak_ptr<_Tp> __old, memory_order __m = memory_order_seq_cst) const noexcept
- _LIBCPP_CHECK_WAIT_MEMORY_ORDER(__m) {
- _Tp* __old_ptr = __old.__ptr_;
- __shared_weak_count* __old_c = __old.__cntrl_;
-
- std::__atomic_smart_ptr_wait_on_address(__fields_.__ctrl_address(), [&] {
- uintptr_t __word = std::__cxx_atomic_load(__builtin_addressof(__fields_.__ctrl_), __m);
- __shared_weak_count* __cur_c = __atomic_smart_ptr_storage::__decode(__word);
- if (__cur_c != __old_c)
- return true;
- _Tp* __cur_ptr = std::__cxx_atomic_load(__builtin_addressof(__fields_.__ptr_), __m);
- return !__atomic_smart_ptr_equivalent(__cur_ptr, __cur_c, __old_ptr, __old_c);
- });
- }
-
- _LIBCPP_HIDE_FROM_ABI void notify_one() noexcept { std::__atomic_smart_ptr_notify_one(__fields_.__ctrl_address()); }
- _LIBCPP_HIDE_FROM_ABI void notify_all() noexcept { std::__atomic_smart_ptr_notify_all(__fields_.__ctrl_address()); }
-
-private:
- __atomic_smart_ptr_fields<_Tp> __fields_;
-};
-
-#endif // _LIBCPP_STD_VER >= 20 && _LIBCPP_HAS_THREADS && _LIBCPP_HAS_ATOMIC_HEADER
-
-_LIBCPP_END_NAMESPACE_STD
-
-_LIBCPP_POP_MACROS
-
#endif // _LIBCPP___MEMORY_ATOMIC_SHARED_PTR_H
diff --git a/libcxx/include/__memory/atomic_shared_ptr_lock_based.h b/libcxx/include/__memory/atomic_shared_ptr_lock_based.h
new file mode 100644
index 0000000000000..e4584612b5009
--- /dev/null
+++ b/libcxx/include/__memory/atomic_shared_ptr_lock_based.h
@@ -0,0 +1,491 @@
+// -*- 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___MEMORY_ATOMIC_SHARED_PTR_LOCK_BASED_H
+#define _LIBCPP___MEMORY_ATOMIC_SHARED_PTR_LOCK_BASED_H
+
+#include <__atomic/atomic_sync_lite.h>
+#include <__atomic/check_memory_order.h>
+#include <__atomic/memory_order.h>
+#include <__atomic/support.h>
+#include <__atomic/to_failure_order.h>
+#include <__config>
+#include <__cstddef/nullptr_t.h>
+#include <__memory/shared_count.h>
+#include <__utility/move.h>
+
+#include <cstdint>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+#if defined(__SANITIZE_THREAD__) || (defined(__has_feature) && __has_feature(thread_sanitizer))
+# if __has_include(<sanitizer/tsan_interface.h>)
+# include <sanitizer/tsan_interface.h>
+# define _LIBCPP_ATOMIC_SHARED_PTR_TSAN 1
+# endif
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+// TSAN annotations model the lock-bit protocol on __ctrl_.
+#if defined(_LIBCPP_ATOMIC_SHARED_PTR_TSAN)
+# define _LIBCPP_ATOMIC_SP_TSAN_PRE_LOCK(addr) \
+ ::__tsan_mutex_pre_lock(reinterpret_cast<void*>(const_cast<__cxx_atomic_impl<uintptr_t>*>(addr)), 0)
+# define _LIBCPP_ATOMIC_SP_TSAN_POST_LOCK(addr) \
+ ::__tsan_mutex_post_lock(reinterpret_cast<void*>(const_cast<__cxx_atomic_impl<uintptr_t>*>(addr)), 0, 0)
+# define _LIBCPP_ATOMIC_SP_TSAN_PRE_UNLOCK(addr) \
+ ::__tsan_mutex_pre_unlock(reinterpret_cast<void*>(const_cast<__cxx_atomic_impl<uintptr_t>*>(addr)), 0)
+# define _LIBCPP_ATOMIC_SP_TSAN_POST_UNLOCK(addr) \
+ ::__tsan_mutex_post_unlock(reinterpret_cast<void*>(const_cast<__cxx_atomic_impl<uintptr_t>*>(addr)), 0)
+#else
+# define _LIBCPP_ATOMIC_SP_TSAN_PRE_LOCK(addr) ((void)(addr))
+# define _LIBCPP_ATOMIC_SP_TSAN_POST_LOCK(addr) ((void)(addr))
+# define _LIBCPP_ATOMIC_SP_TSAN_PRE_UNLOCK(addr) ((void)(addr))
+# define _LIBCPP_ATOMIC_SP_TSAN_POST_UNLOCK(addr) ((void)(addr))
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20 && _LIBCPP_HAS_THREADS && _LIBCPP_HAS_ATOMIC_HEADER
+
+template <class _Tp>
+class shared_ptr;
+
+template <class _Tp>
+class weak_ptr;
+
+template <class _Tp>
+struct atomic;
+
+// CB is at least 4-byte aligned, so bits 0..1 of its pointer are always zero
+// and can be stolen for the spinlock and notify bit without aliasing the address.
+struct __atomic_smart_ptr_storage {
+ static constexpr uintptr_t __lock_bit_ = uintptr_t{1};
+ static constexpr uintptr_t __notify_bit_ = uintptr_t{2};
+ static constexpr uintptr_t __ptr_mask_ = ~(__lock_bit_ | __notify_bit_);
+
+ static uintptr_t __encode(__shared_weak_count* __ctrl, uintptr_t __bits) noexcept {
+ return (reinterpret_cast<uintptr_t>(__ctrl) & __ptr_mask_) | (__bits & ~__ptr_mask_);
+ }
+
+ static __shared_weak_count* __decode(uintptr_t __word) noexcept {
+ return reinterpret_cast<__shared_weak_count*>(__word & __ptr_mask_);
+ }
+
+ static bool __has_lock(uintptr_t __word) noexcept { return (__word & __lock_bit_) != 0; }
+ static bool __has_notify(uintptr_t __word) noexcept { return (__word & __notify_bit_) != 0; }
+};
+
+inline void __atomic_smart_ptr_notify_one(const void* __address) noexcept {
+# if _LIBCPP_AVAILABILITY_HAS_NEW_SYNC
+ std::__atomic_notify_one_global_table(__address);
+# else
+ std::__cxx_atomic_notify_one(reinterpret_cast<void const volatile*>(__address));
+# endif
+}
+
+inline void __atomic_smart_ptr_notify_all(const void* __address) noexcept {
+# if _LIBCPP_AVAILABILITY_HAS_NEW_SYNC
+ std::__atomic_notify_all_global_table(__address);
+# else
+ std::__cxx_atomic_notify_all(reinterpret_cast<void const volatile*>(__address));
+# endif
+}
+
+template <class _Poll>
+inline void __atomic_smart_ptr_wait_on_address(const void* __address, const _Poll& __poll) noexcept {
+ while (!__poll()) {
+# if _LIBCPP_AVAILABILITY_HAS_NEW_SYNC
+ auto __monitor_value = std::__atomic_monitor_global(__address);
+ if (__poll())
+ return;
+ std::__atomic_wait_global_table(__address, __monitor_value);
+# else
+ void const volatile* __volatile_address = reinterpret_cast<void const volatile*>(__address);
+ auto __monitor_value = std::__libcpp_atomic_monitor(__volatile_address);
+ if (__poll())
+ return;
+ std::__libcpp_atomic_wait(__volatile_address, __monitor_value);
+# endif
+ }
+}
+
+template <class _Element>
+struct __atomic_smart_ptr_fields {
+ mutable __cxx_atomic_impl<_Element*> __ptr_;
+ mutable __cxx_atomic_impl<uintptr_t> __ctrl_;
+
+ __atomic_smart_ptr_fields(_Element* __p, __shared_weak_count* __c) noexcept
+ : __ptr_(__p), __ctrl_(__atomic_smart_ptr_storage::__encode(__c, 0)) {}
+
+ const void* __ctrl_address() const noexcept { return static_cast<const void*>(__builtin_addressof(__ctrl_)); }
+
+ // Notify bit is set before sleeping so __unlock wakes waiters; without it
+ // sleeping threads would remain blocked after the lock is released.
+ void __lock() const noexcept {
+ _LIBCPP_ATOMIC_SP_TSAN_PRE_LOCK(__builtin_addressof(__ctrl_));
+ uintptr_t __expected = std::__cxx_atomic_load(__builtin_addressof(__ctrl_), memory_order_relaxed);
+ for (;;) {
+ if (!__atomic_smart_ptr_storage::__has_lock(__expected)) {
+ uintptr_t __desired = __expected | __atomic_smart_ptr_storage::__lock_bit_;
+ if (std::__cxx_atomic_compare_exchange_weak(
+ __builtin_addressof(__ctrl_),
+ __builtin_addressof(__expected),
+ __desired,
+ memory_order_acquire,
+ memory_order_relaxed)) {
+ _LIBCPP_ATOMIC_SP_TSAN_POST_LOCK(__builtin_addressof(__ctrl_));
+ return;
+ }
+ continue;
+ }
+
+ uintptr_t __with_notify = __expected | __atomic_smart_ptr_storage::__notify_bit_;
+ if (!__atomic_smart_ptr_storage::__has_notify(__expected)) {
+ if (!std::__cxx_atomic_compare_exchange_weak(
+ __builtin_addressof(__ctrl_),
+ __builtin_addressof(__expected),
+ __with_notify,
+ memory_order_relaxed,
+ memory_order_relaxed))
+ continue;
+ __expected = __with_notify;
+ }
+
+ std::__atomic_smart_ptr_wait_on_address(__ctrl_address(), [&] {
+ __expected = std::__cxx_atomic_load(__builtin_addressof(__ctrl_), memory_order_relaxed);
+ return !__atomic_smart_ptr_storage::__has_lock(__expected);
+ });
+ }
+ }
+
+ // Notify bit means at least one waiter is sleeping on __ctrl_; wake them
+ // after publishing the new CB so they don't miss the change.
+ void __unlock(__shared_weak_count* __ctrl_to_publish) const noexcept {
+ _LIBCPP_ATOMIC_SP_TSAN_PRE_UNLOCK(__builtin_addressof(__ctrl_));
+ uintptr_t __new_word = __atomic_smart_ptr_storage::__encode(__ctrl_to_publish, 0);
+ uintptr_t __previous = std::__cxx_atomic_exchange(__builtin_addressof(__ctrl_), __new_word, memory_order_release);
+ if (__atomic_smart_ptr_storage::__has_notify(__previous))
+ std::__atomic_smart_ptr_notify_all(__ctrl_address());
+ _LIBCPP_ATOMIC_SP_TSAN_POST_UNLOCK(__builtin_addressof(__ctrl_));
+ }
+};
+
+// [util.smartptr.atomic.shared]: same stored pointer and same ownership, or both empty.
+template <class _Element>
+inline bool __atomic_smart_ptr_equivalent(
+ _Element* __ptr,
+ __shared_weak_count* __ctrl,
+ _Element* __expected_ptr,
+ __shared_weak_count* __expected_ctrl) noexcept {
+ if (__ctrl == nullptr && __expected_ctrl == nullptr)
+ return true;
+ return __ptr == __expected_ptr && __ctrl == __expected_ctrl;
+}
+
+template <class _Tp>
+struct atomic<shared_ptr<_Tp>> {
+ using value_type = shared_ptr<_Tp>;
+
+ static constexpr bool is_always_lock_free = false;
+
+ atomic() noexcept : __fields_(nullptr, nullptr) {}
+ constexpr atomic(nullptr_t) noexcept : __fields_(nullptr, nullptr) {}
+ atomic(shared_ptr<_Tp> __desired) noexcept : __fields_(__desired.__ptr_, __desired.__cntrl_) {
+ __desired.__ptr_ = nullptr;
+ __desired.__cntrl_ = nullptr;
+ }
+
+ atomic(const atomic&) = delete;
+ atomic& operator=(const atomic&) = delete;
+
+ ~atomic() {
+ if (auto* __c = __atomic_smart_ptr_storage::__decode(
+ std::__cxx_atomic_load(__builtin_addressof(__fields_.__ctrl_), memory_order_relaxed)))
+ __c->__release_shared();
+ }
+
+ [[nodiscard]] bool is_lock_free() const noexcept { return false; }
+
+ void operator=(shared_ptr<_Tp> __desired) noexcept { store(std::move(__desired)); }
+ void operator=(nullptr_t) noexcept { store(nullptr); }
+ operator shared_ptr<_Tp>() const noexcept { return load(); }
+
+ void store(shared_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) noexcept
+ _LIBCPP_CHECK_STORE_MEMORY_ORDER(__m) {
+ (void)__m;
+ _Tp* __desired_ptr = __desired.__ptr_;
+ __shared_weak_count* __desired_c = __desired.__cntrl_;
+ __desired.__ptr_ = nullptr;
+ __desired.__cntrl_ = nullptr;
+
+ __fields_.__lock();
+ __shared_weak_count* __old_c = __atomic_smart_ptr_storage::__decode(
+ std::__cxx_atomic_load(__builtin_addressof(__fields_.__ctrl_), memory_order_relaxed));
+ std::__cxx_atomic_store(__builtin_addressof(__fields_.__ptr_), __desired_ptr, memory_order_relaxed);
+ __fields_.__unlock(__desired_c);
+
+ if (__old_c)
+ __old_c->__release_shared();
+ }
+
+ [[nodiscard]] shared_ptr<_Tp> load(memory_order __m = memory_order_seq_cst) const noexcept
+ _LIBCPP_CHECK_LOAD_MEMORY_ORDER(__m) {
+ (void)__m;
+ __fields_.__lock();
+ _Tp* __ptr = std::__cxx_atomic_load(__builtin_addressof(__fields_.__ptr_), memory_order_relaxed);
+ __shared_weak_count* __c = __atomic_smart_ptr_storage::__decode(
+ std::__cxx_atomic_load(__builtin_addressof(__fields_.__ctrl_), memory_order_relaxed));
+ if (__c)
+ __c->__add_shared();
+ __fields_.__unlock(__c);
+ return shared_ptr<_Tp>::__create_with_control_block(__ptr, __c);
+ }
+
+ shared_ptr<_Tp> exchange(shared_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) noexcept {
+ (void)__m;
+ _Tp* __desired_ptr = __desired.__ptr_;
+ __shared_weak_count* __desired_c = __desired.__cntrl_;
+ __desired.__ptr_ = nullptr;
+ __desired.__cntrl_ = nullptr;
+
+ __fields_.__lock();
+ _Tp* __old_ptr = std::__cxx_atomic_load(__builtin_addressof(__fields_.__ptr_), memory_order_relaxed);
+ __shared_weak_count* __old_c = __atomic_smart_ptr_storage::__decode(
+ std::__cxx_atomic_load(__builtin_addressof(__fields_.__ctrl_), memory_order_relaxed));
+ std::__cxx_atomic_store(__builtin_addressof(__fields_.__ptr_), __desired_ptr, memory_order_relaxed);
+ __fields_.__unlock(__desired_c);
+
+ return shared_ptr<_Tp>::__create_with_control_block(__old_ptr, __old_c);
+ }
+
+ bool compare_exchange_strong(
+ shared_ptr<_Tp>& __expected, shared_ptr<_Tp> __desired, memory_order __success, memory_order __failure) noexcept
+ _LIBCPP_CHECK_EXCHANGE_MEMORY_ORDER(__success, __failure) {
+ (void)__success;
+ (void)__failure;
+ __fields_.__lock();
+ _Tp* __cur_ptr = std::__cxx_atomic_load(__builtin_addressof(__fields_.__ptr_), memory_order_relaxed);
+ __shared_weak_count* __cur_c = __atomic_smart_ptr_storage::__decode(
+ std::__cxx_atomic_load(__builtin_addressof(__fields_.__ctrl_), memory_order_relaxed));
+
+ if (__atomic_smart_ptr_equivalent(__cur_ptr, __cur_c, __expected.__ptr_, __expected.__cntrl_)) {
+ _Tp* __desired_ptr = __desired.__ptr_;
+ __shared_weak_count* __desired_c = __desired.__cntrl_;
+ __desired.__ptr_ = nullptr;
+ __desired.__cntrl_ = nullptr;
+
+ std::__cxx_atomic_store(__builtin_addressof(__fields_.__ptr_), __desired_ptr, memory_order_relaxed);
+ __fields_.__unlock(__desired_c);
+ if (__cur_c)
+ __cur_c->__release_shared();
+ return true;
+ }
+
+ if (__cur_c)
+ __cur_c->__add_shared();
+ __fields_.__unlock(__cur_c);
+ __expected = shared_ptr<_Tp>::__create_with_control_block(__cur_ptr, __cur_c);
+ return false;
+ }
+
+ bool compare_exchange_strong(
+ shared_ptr<_Tp>& __expected, shared_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) noexcept {
+ return compare_exchange_strong(__expected, std::move(__desired), __m, std::__to_failure_order(__m));
+ }
+
+ bool compare_exchange_weak(
+ shared_ptr<_Tp>& __expected, shared_ptr<_Tp> __desired, memory_order __success, memory_order __failure) noexcept
+ _LIBCPP_CHECK_EXCHANGE_MEMORY_ORDER(__success, __failure) {
+ return compare_exchange_strong(__expected, std::move(__desired), __success, __failure);
+ }
+
+ bool compare_exchange_weak(
+ shared_ptr<_Tp>& __expected, shared_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) noexcept {
+ return compare_exchange_strong(__expected, std::move(__desired), __m, std::__to_failure_order(__m));
+ }
+
+ // __ctrl_ is the wait address: __ptr_ changes are always published together
+ // with __ctrl_ through __unlock, so watching __ctrl_ is sufficient.
+ void wait(shared_ptr<_Tp> __old, memory_order __m = memory_order_seq_cst) const noexcept
+ _LIBCPP_CHECK_WAIT_MEMORY_ORDER(__m) {
+ _Tp* __old_ptr = __old.__ptr_;
+ __shared_weak_count* __old_c = __old.__cntrl_;
+
+ std::__atomic_smart_ptr_wait_on_address(__fields_.__ctrl_address(), [&] {
+ uintptr_t __word = std::__cxx_atomic_load(__builtin_addressof(__fields_.__ctrl_), __m);
+ __shared_weak_count* __cur_c = __atomic_smart_ptr_storage::__decode(__word);
+ if (__cur_c != __old_c)
+ return true;
+ _Tp* __cur_ptr = std::__cxx_atomic_load(__builtin_addressof(__fields_.__ptr_), __m);
+ return !__atomic_smart_ptr_equivalent(__cur_ptr, __cur_c, __old_ptr, __old_c);
+ });
+ }
+
+ void notify_one() noexcept { std::__atomic_smart_ptr_notify_one(__fields_.__ctrl_address()); }
+ void notify_all() noexcept { std::__atomic_smart_ptr_notify_all(__fields_.__ctrl_address()); }
+
+private:
+ __atomic_smart_ptr_fields<_Tp> __fields_;
+};
+
+template <class _Tp>
+struct atomic<weak_ptr<_Tp>> {
+ using value_type = weak_ptr<_Tp>;
+
+ static constexpr bool is_always_lock_free = false;
+
+ atomic() noexcept : __fields_(nullptr, nullptr) {}
+ atomic(weak_ptr<_Tp> __desired) noexcept : __fields_(__desired.__ptr_, __desired.__cntrl_) {
+ __desired.__ptr_ = nullptr;
+ __desired.__cntrl_ = nullptr;
+ }
+
+ atomic(const atomic&) = delete;
+ atomic& operator=(const atomic&) = delete;
+
+ ~atomic() {
+ if (auto* __c = __atomic_smart_ptr_storage::__decode(
+ std::__cxx_atomic_load(__builtin_addressof(__fields_.__ctrl_), memory_order_relaxed)))
+ __c->__release_weak();
+ }
+
+ [[nodiscard]] bool is_lock_free() const noexcept { return false; }
+
+ void operator=(weak_ptr<_Tp> __desired) noexcept { store(std::move(__desired)); }
+ operator weak_ptr<_Tp>() const noexcept { return load(); }
+
+ void store(weak_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) noexcept
+ _LIBCPP_CHECK_STORE_MEMORY_ORDER(__m) {
+ (void)__m;
+ _Tp* __desired_ptr = __desired.__ptr_;
+ __shared_weak_count* __desired_c = __desired.__cntrl_;
+ __desired.__ptr_ = nullptr;
+ __desired.__cntrl_ = nullptr;
+
+ __fields_.__lock();
+ __shared_weak_count* __old_c = __atomic_smart_ptr_storage::__decode(
+ std::__cxx_atomic_load(__builtin_addressof(__fields_.__ctrl_), memory_order_relaxed));
+ std::__cxx_atomic_store(__builtin_addressof(__fields_.__ptr_), __desired_ptr, memory_order_relaxed);
+ __fields_.__unlock(__desired_c);
+
+ if (__old_c)
+ __old_c->__release_weak();
+ }
+
+ [[nodiscard]] weak_ptr<_Tp> load(memory_order __m = memory_order_seq_cst) const noexcept
+ _LIBCPP_CHECK_LOAD_MEMORY_ORDER(__m) {
+ (void)__m;
+ __fields_.__lock();
+ _Tp* __ptr = std::__cxx_atomic_load(__builtin_addressof(__fields_.__ptr_), memory_order_relaxed);
+ __shared_weak_count* __c = __atomic_smart_ptr_storage::__decode(
+ std::__cxx_atomic_load(__builtin_addressof(__fields_.__ctrl_), memory_order_relaxed));
+ if (__c)
+ __c->__add_weak();
+ __fields_.__unlock(__c);
+ return weak_ptr<_Tp>::__create_with_control_block(__ptr, __c);
+ }
+
+ weak_ptr<_Tp> exchange(weak_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) noexcept {
+ (void)__m;
+ _Tp* __desired_ptr = __desired.__ptr_;
+ __shared_weak_count* __desired_c = __desired.__cntrl_;
+ __desired.__ptr_ = nullptr;
+ __desired.__cntrl_ = nullptr;
+
+ __fields_.__lock();
+ _Tp* __old_ptr = std::__cxx_atomic_load(__builtin_addressof(__fields_.__ptr_), memory_order_relaxed);
+ __shared_weak_count* __old_c = __atomic_smart_ptr_storage::__decode(
+ std::__cxx_atomic_load(__builtin_addressof(__fields_.__ctrl_), memory_order_relaxed));
+ std::__cxx_atomic_store(__builtin_addressof(__fields_.__ptr_), __desired_ptr, memory_order_relaxed);
+ __fields_.__unlock(__desired_c);
+
+ return weak_ptr<_Tp>::__create_with_control_block(__old_ptr, __old_c);
+ }
+
+ bool compare_exchange_strong(
+ weak_ptr<_Tp>& __expected, weak_ptr<_Tp> __desired, memory_order __success, memory_order __failure) noexcept
+ _LIBCPP_CHECK_EXCHANGE_MEMORY_ORDER(__success, __failure) {
+ (void)__success;
+ (void)__failure;
+ __fields_.__lock();
+ _Tp* __cur_ptr = std::__cxx_atomic_load(__builtin_addressof(__fields_.__ptr_), memory_order_relaxed);
+ __shared_weak_count* __cur_c = __atomic_smart_ptr_storage::__decode(
+ std::__cxx_atomic_load(__builtin_addressof(__fields_.__ctrl_), memory_order_relaxed));
+
+ if (__atomic_smart_ptr_equivalent(__cur_ptr, __cur_c, __expected.__ptr_, __expected.__cntrl_)) {
+ _Tp* __desired_ptr = __desired.__ptr_;
+ __shared_weak_count* __desired_c = __desired.__cntrl_;
+ __desired.__ptr_ = nullptr;
+ __desired.__cntrl_ = nullptr;
+
+ std::__cxx_atomic_store(__builtin_addressof(__fields_.__ptr_), __desired_ptr, memory_order_relaxed);
+ __fields_.__unlock(__desired_c);
+ if (__cur_c)
+ __cur_c->__release_weak();
+ return true;
+ }
+
+ if (__cur_c)
+ __cur_c->__add_weak();
+ __fields_.__unlock(__cur_c);
+ __expected = weak_ptr<_Tp>::__create_with_control_block(__cur_ptr, __cur_c);
+ return false;
+ }
+
+ bool compare_exchange_strong(
+ weak_ptr<_Tp>& __expected, weak_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) noexcept {
+ return compare_exchange_strong(__expected, std::move(__desired), __m, std::__to_failure_order(__m));
+ }
+
+ bool compare_exchange_weak(
+ weak_ptr<_Tp>& __expected, weak_ptr<_Tp> __desired, memory_order __success, memory_order __failure) noexcept
+ _LIBCPP_CHECK_EXCHANGE_MEMORY_ORDER(__success, __failure) {
+ return compare_exchange_strong(__expected, std::move(__desired), __success, __failure);
+ }
+
+ bool compare_exchange_weak(
+ weak_ptr<_Tp>& __expected, weak_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) noexcept {
+ return compare_exchange_strong(__expected, std::move(__desired), __m, std::__to_failure_order(__m));
+ }
+
+ // __ctrl_ is the wait address: __ptr_ changes are always published together
+ // with __ctrl_ through __unlock, so watching __ctrl_ is sufficient.
+ void wait(weak_ptr<_Tp> __old, memory_order __m = memory_order_seq_cst) const noexcept
+ _LIBCPP_CHECK_WAIT_MEMORY_ORDER(__m) {
+ _Tp* __old_ptr = __old.__ptr_;
+ __shared_weak_count* __old_c = __old.__cntrl_;
+
+ std::__atomic_smart_ptr_wait_on_address(__fields_.__ctrl_address(), [&] {
+ uintptr_t __word = std::__cxx_atomic_load(__builtin_addressof(__fields_.__ctrl_), __m);
+ __shared_weak_count* __cur_c = __atomic_smart_ptr_storage::__decode(__word);
+ if (__cur_c != __old_c)
+ return true;
+ _Tp* __cur_ptr = std::__cxx_atomic_load(__builtin_addressof(__fields_.__ptr_), __m);
+ return !__atomic_smart_ptr_equivalent(__cur_ptr, __cur_c, __old_ptr, __old_c);
+ });
+ }
+
+ void notify_one() noexcept { std::__atomic_smart_ptr_notify_one(__fields_.__ctrl_address()); }
+ void notify_all() noexcept { std::__atomic_smart_ptr_notify_all(__fields_.__ctrl_address()); }
+
+private:
+ __atomic_smart_ptr_fields<_Tp> __fields_;
+};
+
+#endif // _LIBCPP_STD_VER >= 20 && _LIBCPP_HAS_THREADS && _LIBCPP_HAS_ATOMIC_HEADER
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___MEMORY_ATOMIC_SHARED_PTR_LOCK_BASED_H
diff --git a/libcxx/include/__memory/atomic_shared_ptr_lock_free.h b/libcxx/include/__memory/atomic_shared_ptr_lock_free.h
new file mode 100644
index 0000000000000..354fbf579040f
--- /dev/null
+++ b/libcxx/include/__memory/atomic_shared_ptr_lock_free.h
@@ -0,0 +1,710 @@
+// -*- 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___MEMORY_ATOMIC_SHARED_PTR_LOCK_FREE_H
+#define _LIBCPP___MEMORY_ATOMIC_SHARED_PTR_LOCK_FREE_H
+
+#include <__atomic/atomic_sync_lite.h>
+#include <__atomic/check_memory_order.h>
+#include <__atomic/memory_order.h>
+#include <__atomic/support.h>
+#include <__atomic/to_failure_order.h>
+#include <__atomic/to_gcc_order.h>
+#include <__config>
+#include <__cstddef/nullptr_t.h>
+#include <__memory/shared_count.h>
+#include <__utility/move.h>
+
+#include <cstdint>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 20 && _LIBCPP_HAS_THREADS && _LIBCPP_HAS_ATOMIC_HEADER
+
+template <class _Tp>
+class shared_ptr;
+
+template <class _Tp>
+class weak_ptr;
+
+template <class _Tp>
+struct atomic;
+
+// __ctrl_ word layout (8 bytes):
+// bits 0..1 always zero (CBs are at least 4-byte aligned)
+// bits 2..47 control-block pointer
+// bits 48..63 16-bit local refcount (Williams split-refcount aspiration)
+// Bits 48..63 are available on both DWCAS-capable ISAs (x86-64 without LAMA,
+// AArch64 without LVA): user-space pointers stay within 48 bits.
+struct __atomic_smart_ptr_storage {
+ static constexpr uintptr_t __lock_bit_ = uintptr_t{1};
+ static constexpr uintptr_t __notify_bit_ = uintptr_t{2};
+ static constexpr uintptr_t __ptr_mask_ = ~(__lock_bit_ | __notify_bit_);
+ static constexpr unsigned __local_count_shift = 48;
+ static constexpr uintptr_t __local_count_unit = uintptr_t{1} << __local_count_shift;
+ static constexpr uintptr_t __local_count_mask = uintptr_t{0xFFFF} << __local_count_shift;
+ static constexpr uintptr_t __dwcas_ptr_mask = __ptr_mask_ & ~__local_count_mask;
+ static constexpr uint16_t __local_count_max = 0xFFFF;
+
+ static uintptr_t __encode_dwcas(__shared_weak_count* __ctrl, uint16_t __local) noexcept {
+ return (reinterpret_cast<uintptr_t>(__ctrl) & __dwcas_ptr_mask) |
+ (static_cast<uintptr_t>(__local) << __local_count_shift);
+ }
+
+ static __shared_weak_count* __decode_dwcas(uintptr_t __word) noexcept {
+ return reinterpret_cast<__shared_weak_count*>(__word & __dwcas_ptr_mask);
+ }
+
+ static uint16_t __decode_local(uintptr_t __word) noexcept {
+ return static_cast<uint16_t>((__word & __local_count_mask) >> __local_count_shift);
+ }
+};
+
+inline void __atomic_smart_ptr_notify_one(const void* __address) noexcept {
+# if _LIBCPP_AVAILABILITY_HAS_NEW_SYNC
+ std::__atomic_notify_one_global_table(__address);
+# else
+ std::__cxx_atomic_notify_one(reinterpret_cast<void const volatile*>(__address));
+# endif
+}
+
+inline void __atomic_smart_ptr_notify_all(const void* __address) noexcept {
+# if _LIBCPP_AVAILABILITY_HAS_NEW_SYNC
+ std::__atomic_notify_all_global_table(__address);
+# else
+ std::__cxx_atomic_notify_all(reinterpret_cast<void const volatile*>(__address));
+# endif
+}
+
+template <class _Poll>
+inline void __atomic_smart_ptr_wait_on_address(const void* __address, const _Poll& __poll) noexcept {
+ while (!__poll()) {
+# if _LIBCPP_AVAILABILITY_HAS_NEW_SYNC
+ auto __monitor_value = std::__atomic_monitor_global(__address);
+ if (__poll())
+ return;
+ std::__atomic_wait_global_table(__address, __monitor_value);
+# else
+ void const volatile* __volatile_address = reinterpret_cast<void const volatile*>(__address);
+ auto __monitor_value = std::__libcpp_atomic_monitor(__volatile_address);
+ if (__poll())
+ return;
+ std::__libcpp_atomic_wait(__volatile_address, __monitor_value);
+# endif
+ }
+}
+
+// CMPXCHG16B/CASP require 16-byte alignment; the public ABI size is 16 bytes.
+template <class _Element>
+struct alignas(16) __atomic_smart_ptr_fields {
+ mutable __cxx_atomic_impl<_Element*> __ptr_;
+ mutable __cxx_atomic_impl<uintptr_t> __ctrl_;
+
+ __atomic_smart_ptr_fields(_Element* __p, __shared_weak_count* __c) noexcept
+ : __ptr_(__p), __ctrl_(__atomic_smart_ptr_storage::__encode_dwcas(__c, 0)) {}
+
+ // DWCAS updates __ptr_ and __ctrl_ as one 16-byte pair, so waiters observe
+ // the struct base; watching only __ctrl_ would miss stores where the pointer
+ // changes but the CB stays the same (e.g., aliasing constructor).
+ const void* __wait_address() const noexcept { return static_cast<const void*>(this); }
+
+ __extension__ using __sp_u128 = unsigned __int128;
+
+ // Storage layout: low 64 bits = __ptr_, high 64 bits = __ctrl_. Going
+ // through `this` instead of __builtin_addressof(__ptr_) keeps the 16-byte alignment explicit
+ // and avoids implementation-defined aliasing between the sub-atomics and
+ // the wide view (sub-atomics are never accessed when DWCAS is enabled).
+ // TODO(review): this cast bypasses strict aliasing - valid only because
+ // __ptr_ and __ctrl_ are never touched via __cxx_atomic_* on this path.
+ __sp_u128* __dwcas_address() const noexcept {
+ return const_cast<__sp_u128*>(reinterpret_cast<const __sp_u128*>(this));
+ }
+
+ __sp_u128 __dwcas_load(memory_order __o) const noexcept {
+ __sp_u128 __word;
+ __atomic_load(__dwcas_address(), __builtin_addressof(__word), std::__to_gcc_order(__o));
+ return __word;
+ }
+
+ void __dwcas_store(__sp_u128 __word, memory_order __o) const noexcept {
+ __atomic_store(__dwcas_address(), __builtin_addressof(__word), std::__to_gcc_order(__o));
+ }
+
+ __sp_u128 __dwcas_exchange(__sp_u128 __new, memory_order __o) const noexcept {
+ __sp_u128 __old;
+ __atomic_exchange(
+ __dwcas_address(), __builtin_addressof(__new), __builtin_addressof(__old), std::__to_gcc_order(__o));
+ return __old;
+ }
+
+ // Weak CAS suffices for both strong and weak public variants - the caller
+ // controls retry policy (see __cas_dwcas below).
+ bool __dwcas_compare_exchange_weak(
+ __sp_u128& __expected, __sp_u128 __desired, memory_order __s, memory_order __f) const noexcept {
+ return __atomic_compare_exchange(
+ __dwcas_address(),
+ __builtin_addressof(__expected),
+ __builtin_addressof(__desired),
+ /*weak=*/true,
+ std::__to_gcc_order(__s),
+ std::__to_gcc_failure_order(__f));
+ }
+
+ static _Element* __pair_ptr(__sp_u128 __w) noexcept {
+ return reinterpret_cast<_Element*>(static_cast<uintptr_t>(__w));
+ }
+ static uintptr_t __pair_ctrl(__sp_u128 __w) noexcept { return static_cast<uintptr_t>(__w >> 64); }
+ static __sp_u128 __pair_make(_Element* __p, uintptr_t __c) noexcept {
+ return static_cast<__sp_u128>(reinterpret_cast<uintptr_t>(__p)) | (static_cast<__sp_u128>(__c) << 64);
+ }
+};
+
+// [util.smartptr.atomic.shared]: same stored pointer and same ownership, or both empty.
+template <class _Element>
+inline bool __atomic_smart_ptr_equivalent(
+ _Element* __ptr,
+ __shared_weak_count* __ctrl,
+ _Element* __expected_ptr,
+ __shared_weak_count* __expected_ctrl) noexcept {
+ if (__ctrl == nullptr && __expected_ctrl == nullptr)
+ return true;
+ return __ptr == __expected_ptr && __ctrl == __expected_ctrl;
+}
+
+// Pre-pay the per-in-flight-load fetch_add(1) tick that each aspirational
+// loader still owes the retiring control block; without this, the racing
+// loader would double-count its own contribution on the new block.
+template <class _Counter>
+inline void __atomic_smart_ptr_drain_shared(_Counter* __cb, uint16_t __local) noexcept {
+ for (uint16_t __i = 0; __i < __local; ++__i)
+ __cb->__add_shared();
+}
+
+template <class _Counter>
+inline void __atomic_smart_ptr_drain_weak(_Counter* __cb, uint16_t __local) noexcept {
+ for (uint16_t __i = 0; __i < __local; ++__i)
+ __cb->__add_weak();
+}
+
+template <class _Tp>
+struct atomic<shared_ptr<_Tp>> {
+ using value_type = shared_ptr<_Tp>;
+
+ // is_always_lock_free stays false - the umbrella header can dispatch to
+ // the lock-based implementation on cx16/LSE-absent builds; within this TU
+ // is_lock_free() is always true.
+ static constexpr bool is_always_lock_free = false;
+
+ atomic() noexcept : __fields_(nullptr, nullptr) {}
+ constexpr atomic(nullptr_t) noexcept : __fields_(nullptr, nullptr) {}
+ atomic(shared_ptr<_Tp> __desired) noexcept : __fields_(__desired.__ptr_, __desired.__cntrl_) {
+ __desired.__ptr_ = nullptr;
+ __desired.__cntrl_ = nullptr;
+ }
+
+ atomic(const atomic&) = delete;
+ atomic& operator=(const atomic&) = delete;
+
+ ~atomic() {
+ using __fields = __atomic_smart_ptr_fields<_Tp>;
+ auto __word = __fields_.__dwcas_load(memory_order_relaxed);
+ if (auto* __c = __atomic_smart_ptr_storage::__decode_dwcas(__fields::__pair_ctrl(__word))) {
+ // No in-flight loads can exist at destruction time (the standard
+ // forbids concurrent operations on a destroyed atomic), so local
+ // count must be zero by construction.
+ __c->__release_shared();
+ }
+ }
+
+ [[nodiscard]] bool is_lock_free() const noexcept { return true; }
+
+ void operator=(shared_ptr<_Tp> __desired) noexcept { store(std::move(__desired)); }
+ void operator=(nullptr_t) noexcept { store(nullptr); }
+ operator shared_ptr<_Tp>() const noexcept { return load(); }
+
+ void store(shared_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) noexcept
+ _LIBCPP_CHECK_STORE_MEMORY_ORDER(__m) {
+ __store_dwcas(std::move(__desired), __m);
+ }
+
+ [[nodiscard]] shared_ptr<_Tp> load(memory_order __m = memory_order_seq_cst) const noexcept
+ _LIBCPP_CHECK_LOAD_MEMORY_ORDER(__m) {
+ return __load_dwcas(__m);
+ }
+
+ shared_ptr<_Tp> exchange(shared_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) noexcept {
+ return __exchange_dwcas(std::move(__desired), __m);
+ }
+
+ bool compare_exchange_strong(
+ shared_ptr<_Tp>& __expected, shared_ptr<_Tp> __desired, memory_order __success, memory_order __failure) noexcept
+ _LIBCPP_CHECK_EXCHANGE_MEMORY_ORDER(__success, __failure) {
+ return __cas_dwcas(__expected, std::move(__desired), __success, __failure, /*__strong=*/true);
+ }
+
+ bool compare_exchange_strong(
+ shared_ptr<_Tp>& __expected, shared_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) noexcept {
+ return compare_exchange_strong(__expected, std::move(__desired), __m, std::__to_failure_order(__m));
+ }
+
+ bool compare_exchange_weak(
+ shared_ptr<_Tp>& __expected, shared_ptr<_Tp> __desired, memory_order __success, memory_order __failure) noexcept
+ _LIBCPP_CHECK_EXCHANGE_MEMORY_ORDER(__success, __failure) {
+ return __cas_dwcas(__expected, std::move(__desired), __success, __failure, /*__strong=*/false);
+ }
+
+ bool compare_exchange_weak(
+ shared_ptr<_Tp>& __expected, shared_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) noexcept {
+ return compare_exchange_weak(__expected, std::move(__desired), __m, std::__to_failure_order(__m));
+ }
+
+ void wait(shared_ptr<_Tp> __old, memory_order __m = memory_order_seq_cst) const noexcept
+ _LIBCPP_CHECK_WAIT_MEMORY_ORDER(__m) {
+ _Tp* __old_ptr = __old.__ptr_;
+ __shared_weak_count* __old_c = __old.__cntrl_;
+
+ std::__atomic_smart_ptr_wait_on_address(__fields_.__wait_address(), [&] {
+ using __fields = __atomic_smart_ptr_fields<_Tp>;
+ auto __word = __fields_.__dwcas_load(__m);
+ __shared_weak_count* __cur_c = __atomic_smart_ptr_storage::__decode_dwcas(__fields::__pair_ctrl(__word));
+ _Tp* __cur_ptr = __fields::__pair_ptr(__word);
+ return !__atomic_smart_ptr_equivalent(__cur_ptr, __cur_c, __old_ptr, __old_c);
+ });
+ }
+
+ void notify_one() noexcept { std::__atomic_smart_ptr_notify_one(__fields_.__wait_address()); }
+ void notify_all() noexcept { std::__atomic_smart_ptr_notify_all(__fields_.__wait_address()); }
+
+private:
+ __atomic_smart_ptr_fields<_Tp> __fields_;
+
+ using __fields_type = __atomic_smart_ptr_fields<_Tp>;
+ using __u128 = typename __fields_type::__sp_u128;
+
+ shared_ptr<_Tp> __load_dwcas(memory_order __m) const noexcept {
+ auto __expected = __fields_.__dwcas_load(memory_order_acquire);
+
+ __shared_weak_count* __claimed_cb;
+ _Tp* __claimed_ptr;
+
+ while (true) {
+ uintptr_t __ctrl_word = __fields_type::__pair_ctrl(__expected);
+ __claimed_cb = __atomic_smart_ptr_storage::__decode_dwcas(__ctrl_word);
+ __claimed_ptr = __fields_type::__pair_ptr(__expected);
+
+ if (__claimed_cb == nullptr)
+ return shared_ptr<_Tp>{};
+
+ uint16_t __local = __atomic_smart_ptr_storage::__decode_local(__ctrl_word);
+ if (__local == __atomic_smart_ptr_storage::__local_count_max) {
+ // Local count is saturated; yielding here is bounded because progress
+ // on another thread will lower it back. Wider counters would only push
+ // the threshold further out - they cannot eliminate the retry.
+ __expected = __fields_.__dwcas_load(memory_order_acquire);
+ continue;
+ }
+
+ // TODO(question): ABA: the 16-bit local count is not a version tag across CB
+ // lifecycles. A freed CB reallocated at the same address with the same count
+ // lets this CAS succeed spuriously. Is this risk acceptable?
+ auto __desired =
+ __fields_type::__pair_make(__claimed_ptr, __ctrl_word + __atomic_smart_ptr_storage::__local_count_unit);
+ if (__fields_.__dwcas_compare_exchange_weak(__expected, __desired, memory_order_acq_rel, memory_order_acquire))
+ break;
+ }
+
+ __claimed_cb->__add_shared();
+
+ auto __cur = __fields_.__dwcas_load(memory_order_acquire);
+ while (true) {
+ uintptr_t __cur_ctrl = __fields_type::__pair_ctrl(__cur);
+ __shared_weak_count* __cur_cb = __atomic_smart_ptr_storage::__decode_dwcas(__cur_ctrl);
+
+ // TODO(review): the store that replaced this CB pre-paid our tick via
+ // drain_shared, so __release_shared below cannot be the last ref - verify
+ // no concurrent release can create a zero-count window between drain and here.
+ if (__cur_cb != __claimed_cb) {
+ // A racing store already drained our local tick via __drain_shared,
+ // so __add_shared above became a double-count. Cancel it.
+ __claimed_cb->__release_shared();
+ break;
+ }
+
+ auto __dec = __fields_type::__pair_make(
+ __fields_type::__pair_ptr(__cur), __cur_ctrl - __atomic_smart_ptr_storage::__local_count_unit);
+ if (__fields_.__dwcas_compare_exchange_weak(__cur, __dec, memory_order_acq_rel, memory_order_acquire))
+ break;
+ }
+
+ (void)__m;
+ return shared_ptr<_Tp>::__create_with_control_block(__claimed_ptr, __claimed_cb);
+ }
+
+ void __store_dwcas(shared_ptr<_Tp> __desired, memory_order __m) noexcept {
+ _Tp* __desired_ptr = __desired.__ptr_;
+ __shared_weak_count* __desired_c = __desired.__cntrl_;
+ __desired.__ptr_ = nullptr;
+ __desired.__cntrl_ = nullptr;
+
+ auto __new_pair =
+ __fields_type::__pair_make(__desired_ptr, __atomic_smart_ptr_storage::__encode_dwcas(__desired_c, 0));
+ auto __old_pair = __fields_.__dwcas_exchange(__new_pair, memory_order_acq_rel);
+
+ __retire_old_pair(__old_pair);
+
+ (void)__m;
+ std::__atomic_smart_ptr_notify_all(__fields_.__wait_address());
+ }
+
+ shared_ptr<_Tp> __exchange_dwcas(shared_ptr<_Tp> __desired, memory_order __m) noexcept {
+ _Tp* __desired_ptr = __desired.__ptr_;
+ __shared_weak_count* __desired_c = __desired.__cntrl_;
+ __desired.__ptr_ = nullptr;
+ __desired.__cntrl_ = nullptr;
+
+ auto __new_pair =
+ __fields_type::__pair_make(__desired_ptr, __atomic_smart_ptr_storage::__encode_dwcas(__desired_c, 0));
+ auto __old_pair = __fields_.__dwcas_exchange(__new_pair, memory_order_acq_rel);
+
+ uintptr_t __old_ctrl = __fields_type::__pair_ctrl(__old_pair);
+ _Tp* __old_ptr = __fields_type::__pair_ptr(__old_pair);
+ __shared_weak_count* __old_cb = __atomic_smart_ptr_storage::__decode_dwcas(__old_ctrl);
+ uint16_t __old_local = __atomic_smart_ptr_storage::__decode_local(__old_ctrl);
+
+ if (__old_cb) {
+ // Drain in-flight loaders' aspirational ticks. After this, __old_cb's
+ // global count includes one ref per in-flight load that paired up with
+ // this CB. We then transfer ownership of the atomic's own ref to the
+ // returned shared_ptr (no add/release).
+ std::__atomic_smart_ptr_drain_shared(__old_cb, __old_local);
+ }
+
+ (void)__m;
+ std::__atomic_smart_ptr_notify_all(__fields_.__wait_address());
+
+ return shared_ptr<_Tp>::__create_with_control_block(__old_ptr, __old_cb);
+ }
+
+ // __strong: when true, retry on benign DWCAS failures (e.g., local-count
+ // changes by concurrent loads); only return false on a real
+ // ownership-equivalence mismatch.
+ bool __cas_dwcas(shared_ptr<_Tp>& __expected,
+ shared_ptr<_Tp> __desired,
+ memory_order __success,
+ memory_order __failure,
+ bool __strong) noexcept {
+ _Tp* __exp_ptr = __expected.__ptr_;
+ __shared_weak_count* __exp_c = __expected.__cntrl_;
+
+ _Tp* __des_ptr = __desired.__ptr_;
+ __shared_weak_count* __des_c = __desired.__cntrl_;
+
+ auto __cur = __fields_.__dwcas_load(memory_order_acquire);
+
+ while (true) {
+ uintptr_t __cur_ctrl = __fields_type::__pair_ctrl(__cur);
+ _Tp* __cur_ptr = __fields_type::__pair_ptr(__cur);
+ __shared_weak_count* __cur_cb = __atomic_smart_ptr_storage::__decode_dwcas(__cur_ctrl);
+
+ if (!__atomic_smart_ptr_equivalent(__cur_ptr, __cur_cb, __exp_ptr, __exp_c)) {
+ // Ownership mismatch. Update __expected to the actual current value
+ // (with a fresh shared_ptr reference). __desired is NOT consumed;
+ // its destructor will release its own ref.
+ (void)__success;
+ __expected = __load_dwcas(__failure);
+ return false;
+ }
+
+ // Equivalence holds. Try to publish __desired with local_count=0.
+ auto __desired_pair =
+ __fields_type::__pair_make(__des_ptr, __atomic_smart_ptr_storage::__encode_dwcas(__des_c, 0));
+
+ if (__fields_.__dwcas_compare_exchange_weak(__cur, __desired_pair, __success, __failure)) {
+ uint16_t __cur_local = __atomic_smart_ptr_storage::__decode_local(__cur_ctrl);
+ if (__cur_cb)
+ std::__atomic_smart_ptr_drain_shared(__cur_cb, __cur_local);
+ __desired.__ptr_ = nullptr;
+ __desired.__cntrl_ = nullptr;
+ std::__atomic_smart_ptr_notify_all(__fields_.__wait_address());
+ return true;
+ }
+
+ if (!__strong) {
+ // weak: a single underlying CAS attempt - if it failed for any
+ // reason (real ownership change or concurrent local-count update),
+ // report failure with the current pair as the new __expected.
+ __expected = __load_dwcas(__failure);
+ return false;
+ }
+ // Strong CAS: loop and re-check equivalence.
+ }
+ }
+
+ // Retire path used by __store_dwcas - drains in-flight ticks and releases
+ // the atomic's own ref on the old control block.
+ void __retire_old_pair(__u128 __old_pair) noexcept {
+ uintptr_t __old_ctrl = __fields_type::__pair_ctrl(__old_pair);
+ __shared_weak_count* __old_cb = __atomic_smart_ptr_storage::__decode_dwcas(__old_ctrl);
+ uint16_t __old_local = __atomic_smart_ptr_storage::__decode_local(__old_ctrl);
+
+ if (__old_cb == nullptr)
+ return;
+
+ // Pre-pay aspirational ticks of in-flight loaders.
+ std::__atomic_smart_ptr_drain_shared(__old_cb, __old_local);
+ // Release the ref this atomic held on the old control block.
+ __old_cb->__release_shared();
+ }
+};
+
+template <class _Tp>
+struct atomic<weak_ptr<_Tp>> {
+ using value_type = weak_ptr<_Tp>;
+
+ static constexpr bool is_always_lock_free = false;
+
+ atomic() noexcept : __fields_(nullptr, nullptr) {}
+ atomic(weak_ptr<_Tp> __desired) noexcept : __fields_(__desired.__ptr_, __desired.__cntrl_) {
+ __desired.__ptr_ = nullptr;
+ __desired.__cntrl_ = nullptr;
+ }
+
+ atomic(const atomic&) = delete;
+ atomic& operator=(const atomic&) = delete;
+
+ ~atomic() {
+ using __fields = __atomic_smart_ptr_fields<_Tp>;
+ auto __word = __fields_.__dwcas_load(memory_order_relaxed);
+ if (auto* __c = __atomic_smart_ptr_storage::__decode_dwcas(__fields::__pair_ctrl(__word)))
+ __c->__release_weak();
+ }
+
+ [[nodiscard]] bool is_lock_free() const noexcept { return true; }
+
+ void operator=(weak_ptr<_Tp> __desired) noexcept { store(std::move(__desired)); }
+ operator weak_ptr<_Tp>() const noexcept { return load(); }
+
+ void store(weak_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) noexcept
+ _LIBCPP_CHECK_STORE_MEMORY_ORDER(__m) {
+ __store_dwcas(std::move(__desired), __m);
+ }
+
+ [[nodiscard]] weak_ptr<_Tp> load(memory_order __m = memory_order_seq_cst) const noexcept
+ _LIBCPP_CHECK_LOAD_MEMORY_ORDER(__m) {
+ return __load_dwcas(__m);
+ }
+
+ weak_ptr<_Tp> exchange(weak_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) noexcept {
+ return __exchange_dwcas(std::move(__desired), __m);
+ }
+
+ bool compare_exchange_strong(
+ weak_ptr<_Tp>& __expected, weak_ptr<_Tp> __desired, memory_order __success, memory_order __failure) noexcept
+ _LIBCPP_CHECK_EXCHANGE_MEMORY_ORDER(__success, __failure) {
+ return __cas_dwcas(__expected, std::move(__desired), __success, __failure, /*__strong=*/true);
+ }
+
+ bool compare_exchange_strong(
+ weak_ptr<_Tp>& __expected, weak_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) noexcept {
+ return compare_exchange_strong(__expected, std::move(__desired), __m, std::__to_failure_order(__m));
+ }
+
+ bool compare_exchange_weak(
+ weak_ptr<_Tp>& __expected, weak_ptr<_Tp> __desired, memory_order __success, memory_order __failure) noexcept
+ _LIBCPP_CHECK_EXCHANGE_MEMORY_ORDER(__success, __failure) {
+ return __cas_dwcas(__expected, std::move(__desired), __success, __failure, /*__strong=*/false);
+ }
+
+ bool compare_exchange_weak(
+ weak_ptr<_Tp>& __expected, weak_ptr<_Tp> __desired, memory_order __m = memory_order_seq_cst) noexcept {
+ return compare_exchange_weak(__expected, std::move(__desired), __m, std::__to_failure_order(__m));
+ }
+
+ void wait(weak_ptr<_Tp> __old, memory_order __m = memory_order_seq_cst) const noexcept
+ _LIBCPP_CHECK_WAIT_MEMORY_ORDER(__m) {
+ _Tp* __old_ptr = __old.__ptr_;
+ __shared_weak_count* __old_c = __old.__cntrl_;
+
+ std::__atomic_smart_ptr_wait_on_address(__fields_.__wait_address(), [&] {
+ using __fields = __atomic_smart_ptr_fields<_Tp>;
+ auto __word = __fields_.__dwcas_load(__m);
+ __shared_weak_count* __cur_c = __atomic_smart_ptr_storage::__decode_dwcas(__fields::__pair_ctrl(__word));
+ _Tp* __cur_ptr = __fields::__pair_ptr(__word);
+ return !__atomic_smart_ptr_equivalent(__cur_ptr, __cur_c, __old_ptr, __old_c);
+ });
+ }
+
+ void notify_one() noexcept { std::__atomic_smart_ptr_notify_one(__fields_.__wait_address()); }
+ void notify_all() noexcept { std::__atomic_smart_ptr_notify_all(__fields_.__wait_address()); }
+
+private:
+ __atomic_smart_ptr_fields<_Tp> __fields_;
+
+ using __fields_type = __atomic_smart_ptr_fields<_Tp>;
+ using __u128 = typename __fields_type::__sp_u128;
+
+ weak_ptr<_Tp> __load_dwcas(memory_order __m) const noexcept {
+ auto __expected = __fields_.__dwcas_load(memory_order_acquire);
+
+ __shared_weak_count* __claimed_cb;
+ _Tp* __claimed_ptr;
+
+ while (true) {
+ uintptr_t __ctrl_word = __fields_type::__pair_ctrl(__expected);
+ __claimed_cb = __atomic_smart_ptr_storage::__decode_dwcas(__ctrl_word);
+ __claimed_ptr = __fields_type::__pair_ptr(__expected);
+
+ if (__claimed_cb == nullptr)
+ return weak_ptr<_Tp>{};
+
+ uint16_t __local = __atomic_smart_ptr_storage::__decode_local(__ctrl_word);
+ if (__local == __atomic_smart_ptr_storage::__local_count_max) {
+ __expected = __fields_.__dwcas_load(memory_order_acquire);
+ continue;
+ }
+
+ auto __desired =
+ __fields_type::__pair_make(__claimed_ptr, __ctrl_word + __atomic_smart_ptr_storage::__local_count_unit);
+ if (__fields_.__dwcas_compare_exchange_weak(__expected, __desired, memory_order_acq_rel, memory_order_acquire))
+ break;
+ }
+
+ __claimed_cb->__add_weak();
+
+ auto __cur = __fields_.__dwcas_load(memory_order_acquire);
+ while (true) {
+ uintptr_t __cur_ctrl = __fields_type::__pair_ctrl(__cur);
+ __shared_weak_count* __cur_cb = __atomic_smart_ptr_storage::__decode_dwcas(__cur_ctrl);
+
+ if (__cur_cb != __claimed_cb) {
+ __claimed_cb->__release_weak();
+ break;
+ }
+
+ auto __dec = __fields_type::__pair_make(
+ __fields_type::__pair_ptr(__cur), __cur_ctrl - __atomic_smart_ptr_storage::__local_count_unit);
+ if (__fields_.__dwcas_compare_exchange_weak(__cur, __dec, memory_order_acq_rel, memory_order_acquire))
+ break;
+ }
+
+ (void)__m;
+ return weak_ptr<_Tp>::__create_with_control_block(__claimed_ptr, __claimed_cb);
+ }
+
+ void __store_dwcas(weak_ptr<_Tp> __desired, memory_order __m) noexcept {
+ _Tp* __desired_ptr = __desired.__ptr_;
+ __shared_weak_count* __desired_c = __desired.__cntrl_;
+ __desired.__ptr_ = nullptr;
+ __desired.__cntrl_ = nullptr;
+
+ auto __new_pair =
+ __fields_type::__pair_make(__desired_ptr, __atomic_smart_ptr_storage::__encode_dwcas(__desired_c, 0));
+ auto __old_pair = __fields_.__dwcas_exchange(__new_pair, memory_order_acq_rel);
+
+ __retire_old_pair_weak(__old_pair);
+
+ (void)__m;
+ std::__atomic_smart_ptr_notify_all(__fields_.__wait_address());
+ }
+
+ weak_ptr<_Tp> __exchange_dwcas(weak_ptr<_Tp> __desired, memory_order __m) noexcept {
+ _Tp* __desired_ptr = __desired.__ptr_;
+ __shared_weak_count* __desired_c = __desired.__cntrl_;
+ __desired.__ptr_ = nullptr;
+ __desired.__cntrl_ = nullptr;
+
+ auto __new_pair =
+ __fields_type::__pair_make(__desired_ptr, __atomic_smart_ptr_storage::__encode_dwcas(__desired_c, 0));
+ auto __old_pair = __fields_.__dwcas_exchange(__new_pair, memory_order_acq_rel);
+
+ uintptr_t __old_ctrl = __fields_type::__pair_ctrl(__old_pair);
+ _Tp* __old_ptr = __fields_type::__pair_ptr(__old_pair);
+ __shared_weak_count* __old_cb = __atomic_smart_ptr_storage::__decode_dwcas(__old_ctrl);
+ uint16_t __old_local = __atomic_smart_ptr_storage::__decode_local(__old_ctrl);
+
+ if (__old_cb)
+ std::__atomic_smart_ptr_drain_weak(__old_cb, __old_local);
+
+ (void)__m;
+ std::__atomic_smart_ptr_notify_all(__fields_.__wait_address());
+
+ return weak_ptr<_Tp>::__create_with_control_block(__old_ptr, __old_cb);
+ }
+
+ bool __cas_dwcas(weak_ptr<_Tp>& __expected,
+ weak_ptr<_Tp> __desired,
+ memory_order __success,
+ memory_order __failure,
+ bool __strong) noexcept {
+ _Tp* __exp_ptr = __expected.__ptr_;
+ __shared_weak_count* __exp_c = __expected.__cntrl_;
+
+ _Tp* __des_ptr = __desired.__ptr_;
+ __shared_weak_count* __des_c = __desired.__cntrl_;
+
+ auto __cur = __fields_.__dwcas_load(memory_order_acquire);
+
+ while (true) {
+ uintptr_t __cur_ctrl = __fields_type::__pair_ctrl(__cur);
+ _Tp* __cur_ptr = __fields_type::__pair_ptr(__cur);
+ __shared_weak_count* __cur_cb = __atomic_smart_ptr_storage::__decode_dwcas(__cur_ctrl);
+
+ if (!__atomic_smart_ptr_equivalent(__cur_ptr, __cur_cb, __exp_ptr, __exp_c)) {
+ (void)__success;
+ __expected = __load_dwcas(__failure);
+ return false;
+ }
+
+ auto __desired_pair =
+ __fields_type::__pair_make(__des_ptr, __atomic_smart_ptr_storage::__encode_dwcas(__des_c, 0));
+
+ if (__fields_.__dwcas_compare_exchange_weak(__cur, __desired_pair, __success, __failure)) {
+ uint16_t __cur_local = __atomic_smart_ptr_storage::__decode_local(__cur_ctrl);
+ if (__cur_cb)
+ std::__atomic_smart_ptr_drain_weak(__cur_cb, __cur_local);
+ __desired.__ptr_ = nullptr;
+ __desired.__cntrl_ = nullptr;
+ std::__atomic_smart_ptr_notify_all(__fields_.__wait_address());
+ return true;
+ }
+
+ if (!__strong) {
+ __expected = __load_dwcas(__failure);
+ return false;
+ }
+ }
+ }
+
+ void __retire_old_pair_weak(__u128 __old_pair) noexcept {
+ uintptr_t __old_ctrl = __fields_type::__pair_ctrl(__old_pair);
+ __shared_weak_count* __old_cb = __atomic_smart_ptr_storage::__decode_dwcas(__old_ctrl);
+ uint16_t __old_local = __atomic_smart_ptr_storage::__decode_local(__old_ctrl);
+
+ if (__old_cb == nullptr)
+ return;
+
+ std::__atomic_smart_ptr_drain_weak(__old_cb, __old_local);
+ __old_cb->__release_weak();
+ }
+};
+
+#endif // _LIBCPP_STD_VER >= 20 && _LIBCPP_HAS_THREADS && _LIBCPP_HAS_ATOMIC_HEADER
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___MEMORY_ATOMIC_SHARED_PTR_LOCK_FREE_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 3238146851ede..c882b124f24db 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1692,6 +1692,8 @@ module std [system] {
}
module atomic_shared_ptr {
header "__memory/atomic_shared_ptr.h"
+ textual header "__memory/atomic_shared_ptr_lock_based.h"
+ textual header "__memory/atomic_shared_ptr_lock_free.h"
export std.memory.shared_ptr
}
module swap_allocator { header "__memory/swap_allocator.h" }
diff --git a/libcxx/test/libcxx/utilities/memory/util.smartptr/atomic_shared_ptr_dwcas.pass.cpp b/libcxx/test/libcxx/utilities/memory/util.smartptr/atomic_shared_ptr_dwcas.pass.cpp
new file mode 100644
index 0000000000000..6218a3f271f08
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/memory/util.smartptr/atomic_shared_ptr_dwcas.pass.cpp
@@ -0,0 +1,31 @@
+// REQUIRES: std-at-least-c++20
+// UNSUPPORTED: no-threads
+// ADDITIONAL_COMPILE_FLAGS: -march=x86-64-v2
+
+// Verify that with cx16/DWCAS available, is_lock_free() returns true.
+// is_always_lock_free remains false (matches standard expectations).
+
+#include <atomic>
+#include <cassert>
+#include <cstdio>
+#include <memory>
+
+int main(int, char**) {
+ using A = std::atomic<std::shared_ptr<int>>;
+
+ // is_always_lock_free must remain false to match existing libc++ tests.
+ static_assert(!A::is_always_lock_free, "is_always_lock_free should be false");
+
+ A a(std::make_shared<int>(42));
+ bool lf = a.is_lock_free();
+
+ // On x86-64 with cx16 (-march=x86-64-v2), DWCAS is available and
+ // is_lock_free() should return true.
+ std::printf("sizeof=%zu alignof=%zu is_lock_free=%d\n", sizeof(A), alignof(A), (int)lf);
+
+ assert(sizeof(A) == 16);
+ assert(alignof(A) == 16);
+ assert(lf == true);
+
+ return 0;
+}
More information about the libcxx-commits
mailing list