From: Thomas Rodgers <rodg...@twrodgers.com> Signed-off-by: Thomas Rodgers <trodg...@redhat.com>
libstdc++-v3/ChangeLog: * config/abi/pre/gnu.ver (GLIBCXX_3.4.21): Do not match new _Sp_locker constructor. (GLIBCXX_3.4.30): Export _Sp_locker::_M_wait/_M_notify and new constructor. * include/bits/shared_ptr_atomic.h: define __cpp_lib_atomic_shared_ptr feature test macro. (_Sp_locker::_Sp_locker(const void*, bool): New constructor. (_Sp_locker::_M_wait()), _Sp_locker::_M_notify()): New methods. (_Sp_impl): New type. (atomic<shared_ptr<_Tp>>): New partial template specialization. (atomic<weak_ptr<_Tp>>): New partial template specialization. * include/std/version: define __cpp_lib_atomic_shared_ptr feature test macro. * src/c++11/Makefile.am: Compile src/c++11/shared_ptr.cc -std=gnu++20. * src/c++11/Makefile.in: Regenerate. * src/c++11/shared_ptr.cc (_Sp_locker::_Sp_locker(const void*, bool), _Sp_locker::_M_wait(), _Sp_locker::_M_notify(): Implement. * testsuite/20_util/shared_ptr/atomic/4.cc: New test. * testsuite/20_util/shared_ptr/atomic/5.cc: Likewise. * testsuite/20_util/shared_ptr/atomic/atomic_shared_ptr.cc: Likewise. --- libstdc++-v3/config/abi/pre/gnu.ver | 12 +- libstdc++-v3/include/bits/shared_ptr_atomic.h | 309 ++++++++++++++++++ libstdc++-v3/include/std/version | 1 + libstdc++-v3/src/c++11/Makefile.am | 6 + libstdc++-v3/src/c++11/Makefile.in | 6 + libstdc++-v3/src/c++11/shared_ptr.cc | 86 ++++- .../testsuite/20_util/shared_ptr/atomic/4.cc | 28 ++ .../testsuite/20_util/shared_ptr/atomic/5.cc | 28 ++ .../shared_ptr/atomic/atomic_shared_ptr.cc | 159 +++++++++ 9 files changed, 632 insertions(+), 3 deletions(-) create mode 100644 libstdc++-v3/testsuite/20_util/shared_ptr/atomic/4.cc create mode 100644 libstdc++-v3/testsuite/20_util/shared_ptr/atomic/5.cc create mode 100644 libstdc++-v3/testsuite/20_util/shared_ptr/atomic/atomic_shared_ptr.cc diff --git a/libstdc++-v3/config/abi/pre/gnu.ver b/libstdc++-v3/config/abi/pre/gnu.ver index 5323c7f0604..727afd2d488 100644 --- a/libstdc++-v3/config/abi/pre/gnu.ver +++ b/libstdc++-v3/config/abi/pre/gnu.ver @@ -1705,8 +1705,9 @@ GLIBCXX_3.4.21 { # std::ctype_base::blank _ZNSt10ctype_base5blankE; - # std::_Sp_locker::* - _ZNSt10_Sp_locker[CD]*; + # std::_Sp_locker:: constructors and destructors + _ZNSt10_Sp_lockerC*[^b]; + _ZNSt10_Sp_lockerD*; # std::notify_all_at_thread_exit _ZSt25notify_all_at_thread_exitRSt18condition_variableSt11unique_lockISt5mutexE; @@ -2397,6 +2398,13 @@ GLIBCXX_3.4.29 { } GLIBCXX_3.4.28; +GLIBCXX_3.4.30 { + # std::_Sp_locker:: wait/notify support + _ZNSt10_Sp_lockerC*[b]; + _ZNSt10_Sp_locker7_M_waitEv; + _ZNSt10_Sp_locker9_M_notifyEv; +} GLIBCXX_3.4.29; + # Symbols in the support library (libsupc++) have their own tag. CXXABI_1.3 { diff --git a/libstdc++-v3/include/bits/shared_ptr_atomic.h b/libstdc++-v3/include/bits/shared_ptr_atomic.h index 6e94d83c46d..2aec3adac7c 100644 --- a/libstdc++-v3/include/bits/shared_ptr_atomic.h +++ b/libstdc++-v3/include/bits/shared_ptr_atomic.h @@ -32,6 +32,10 @@ #include <bits/atomic_base.h> +#if __cplusplus > 201703L +# define __cpp_lib_atomic_shared_ptr 201711L +#endif + namespace std _GLIBCXX_VISIBILITY(default) { _GLIBCXX_BEGIN_NAMESPACE_VERSION @@ -55,6 +59,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _Sp_locker(const void*, const void*) noexcept; ~_Sp_locker(); +#if __cpp_lib_atomic_shared_ptr + // called only by notifiers, does not acquire a lock + _Sp_locker(const void*, bool) noexcept; + + void + _M_wait() noexcept; + + void + _M_notify() noexcept; +#endif + private: unsigned char _M_key1; unsigned char _M_key2; @@ -327,6 +342,300 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } /// @} +#if __cpp_lib_atomic_shared_ptr + template<typename _Tp> + struct _Sp_impl + { + using value_type = _Tp; + + static constexpr bool + _M_is_always_lock_free = false; + + static bool + _M_is_lock_free() noexcept + { return false; } + + constexpr _Sp_impl() noexcept = default; + _Sp_impl(value_type __r) noexcept + : _M_p(move(__r)) + { } + + _Sp_impl(const _Sp_impl&) = delete; + void operator=(const _Sp_impl&) = delete; + + value_type + _M_load(memory_order) const noexcept + { + _Sp_locker __lock{&_M_p}; + return _M_p; + } + + void + _M_store(value_type __r, memory_order) noexcept + { + _Sp_locker __lock{&_M_p}; + _M_p.swap(__r); // use swap so that *__p not destroyed while lock held + } + + value_type + _M_exchange(value_type __r, memory_order) noexcept + { + _Sp_locker __lock{&_M_p}; + _M_p.swap(__r); + return __r; + } + + template<typename _Pred> + bool + _M_compare_exchange_strong(value_type& __v, value_type __w, + _Pred __pred, + memory_order, memory_order) noexcept + { + value_type __x; // goes out of scope after __lock + _Sp_locker __lock{&_M_p, &__v}; + if (__pred(_M_p, __v)) + { + __x = move(_M_p); + _M_p = move(__w); + return true; + } + __x = move(__v); + __v = _M_p; + return false; + } + + template<typename _Pred> + void + _M_wait(value_type __old, + _Pred __pred, memory_order) const noexcept + { + _Sp_locker __lock(&_M_p); + while (__pred(_M_p, __old)) + __lock._M_wait(); + } + + void + _M_notify() noexcept + { + _Sp_locker __lock(&_M_p, true); + __lock._M_notify(); + } + + private: + value_type _M_p; + }; + + template<typename _Tp> + class atomic<shared_ptr<_Tp>> + { + using __impl_t = _Sp_impl<shared_ptr<_Tp>>; + + public: + using value_type = __impl_t::value_type; + + static constexpr bool + is_always_lock_free = __impl_t::_M_is_always_lock_free; + + bool + is_lock_free() const noexcept + { return __impl_t::_M_is_lock_free(); } + + constexpr atomic() noexcept = default; + atomic(value_type __r) noexcept + : _M_impl(move(__r)) + { } + + atomic(const atomic&) = delete; + void operator=(const atomic&) = delete; + + value_type + load(memory_order __o = memory_order_seq_cst) const noexcept + { return _M_impl._M_load(__o); } + + operator shared_ptr<_Tp>() const noexcept + { return load(); } + + void + store(value_type __r, memory_order __o = memory_order_seq_cst) noexcept + { _M_impl._M_store(move(__r), __o); } + + void operator=(value_type __r) noexcept + { store(move(__r)); } + + value_type + exchange(value_type __r, memory_order __o = memory_order_seq_cst) noexcept + { return _M_impl._M_exchange(move(__r), __o); } + + bool + compare_exchange_strong(value_type& __v, value_type __w, + memory_order, memory_order) noexcept + { + return _M_impl._M_compare_exchange_strong(__v, move(__w), + [](const auto& __a, const auto& __b) { return _S_compare(__a, __b); }, + memory_order_seq_cst, memory_order_seq_cst); + } + + bool + compare_exchange_strong(value_type& __v, value_type __w, + memory_order __o = memory_order_seq_cst) noexcept + { + return _M_impl._M_compare_exchange_strong(__v, move(__w), + [](const auto& __a, const auto& __b) { return _S_compare(__a, __b); }, + __o, __o); + } + + bool + compare_exchange_weak(value_type& __v, value_type __w, + memory_order, memory_order) noexcept + { + return _M_impl._M_compare_exchange_strong(__v, move(__w), + [](const auto& __a, const auto& __b) { return _S_compare(__a, __b); }, + memory_order_seq_cst, memory_order_seq_cst); + } + + bool + compare_exchange_weak(value_type& __v, value_type __w, + memory_order __o = memory_order_seq_cst) noexcept + { + return _M_impl._M_compare_exchange_strong(__v, move(__w), + [](const auto& __a, const auto& __b) { return _S_compare(__a, __b); }, + __o, __o); + } + + void + wait(value_type __old, memory_order __o = memory_order_seq_cst) const noexcept + { + _M_impl._M_wait(move(__old), + [](const auto& __a, const auto& __b) { return _S_compare(__a, __b); }, + __o); + } + + void + notify_one() noexcept + { _M_impl._M_notify(); } + + void + notify_all() noexcept + { _M_impl._M_notify(); } + + private: + __impl_t _M_impl; + + static bool + _S_compare(const value_type& __a, const value_type& __b) noexcept + { + owner_less<value_type> __less; + return __a == __b && !__less(__a, __b) && !__less(__b, __a); + } + }; + + template<typename _Tp> + class atomic<weak_ptr<_Tp>> + { + using __impl_t = _Sp_impl<weak_ptr<_Tp>>; + + public: + using value_type = __impl_t::value_type; + + static constexpr bool + is_always_lock_free = __impl_t::_M_is_always_lock_free; + + bool + is_lock_free() const noexcept + { return __impl_t::_M_is_lock_free(); } + + constexpr atomic() noexcept = default; + atomic(value_type __r) noexcept + : _M_impl(move(__r)) + { } + + atomic(const atomic&) = delete; + void operator=(const atomic&) = delete; + + value_type + load(memory_order __o = memory_order_seq_cst) const noexcept + { return _M_impl._M_load(__o); } + + operator weak_ptr<_Tp>() const noexcept + { return load(); } + + void + store(value_type __r, memory_order __o = memory_order_seq_cst) noexcept + { _M_impl._M_store(move(__r), __o); } + + void operator=(value_type __r) noexcept + { store(move(__r)); } + + value_type + exchange(value_type __r, memory_order __o = memory_order_seq_cst) noexcept + { return _M_impl._M_exchange(move(__r), __o); } + + bool + compare_exchange_strong(value_type& __v, value_type __w, + memory_order, memory_order) noexcept + { + return _M_impl._M_compare_exchange_strong(__v, move(__w), + [](const auto& __a, const auto& __b) { return _S_compare(__a, __b); }, + memory_order_seq_cst, memory_order_seq_cst); + } + + bool + compare_exchange_strong(value_type& __v, value_type __w, + memory_order __o = memory_order_seq_cst) noexcept + { + return _M_impl._M_compare_exchange_strong(__v, move(__w), + [](const auto& __a, const auto& __b) { return _S_compare(__a, __b); }, + __o, __o); + } + + bool + compare_exchange_weak(value_type& __v, value_type __w, + memory_order, memory_order) noexcept + { + return _M_impl._M_compare_exchange_strong(__v, move(__w), + [](const auto& __a, const auto& __b) { return _S_compare(__a, __b); }, + memory_order_seq_cst, memory_order_seq_cst); + } + + bool + compare_exchange_weak(value_type& __v, value_type __w, + memory_order __o = memory_order_seq_cst) noexcept + { + return _M_impl._M_compare_exchange_strong(__v, move(__w), + [](const auto& __a, const auto& __b) { return _S_compare(__a, __b); }, + __o, __o); + } + + void + wait(value_type __old, memory_order __o = memory_order_seq_cst) const noexcept + { + _M_impl._M_wait(move(__old), + [](const auto& __a, const auto& __b) { return _S_compare(__a, __b); }, + __o); + } + + void + notify_one() noexcept + { _M_impl._M_notify(); } + + void + notify_all() noexcept + { _M_impl._M_notify(); } + + private: + __impl_t _M_impl; + + static bool + _S_compare(const value_type& __a, const value_type& __b) noexcept + { + owner_less<value_type> __less; + const auto& __sa = __a.lock(); + const auto& __sb = __b.lock(); + return __sa == __sb && !__less(__a, __b) && !__less(__b, __a); + } + }; +#endif // __cpp_lib_atomic_shared_ptr + /// @} relates shared_ptr /// @} group pointer_abstractions diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version index f41004b5911..37e5f0e5175 100644 --- a/libstdc++-v3/include/std/version +++ b/libstdc++-v3/include/std/version @@ -179,6 +179,7 @@ #define __cpp_lib_atomic_flag_test 201907L #define __cpp_lib_atomic_float 201711L #define __cpp_lib_atomic_ref 201806L +#define __cpp_lib_atomic_shared_ptr 201711L #define __cpp_lib_atomic_value_initialization 201911L #define __cpp_lib_bitops 201907L #define __cpp_lib_bounded_array_traits 201902L diff --git a/libstdc++-v3/src/c++11/Makefile.am b/libstdc++-v3/src/c++11/Makefile.am index a26903db6bc..72ab7ac22fe 100644 --- a/libstdc++-v3/src/c++11/Makefile.am +++ b/libstdc++-v3/src/c++11/Makefile.am @@ -136,6 +136,12 @@ limits.lo: limits.cc limits.o: limits.cc $(CXXCOMPILE) -fchar8_t -c $< +# Use -std=gnu++20 for shared_ptr.cc +shared_ptr.lo: shared_ptr.cc + $(LTCXXCOMPILE) -std=gnu++20 -c $< +shared_ptr.o: shared_ptr.cc + $(CXXCOMPILE) -std=gnu++20 -c $< + if ENABLE_DUAL_ABI # Rewrite the type info for __ios_failure. rewrite_ios_failure_typeinfo = sed -e '/^_*_ZTISt13__ios_failure:/,/_ZTVN10__cxxabiv120__si_class_type_infoE/s/_ZTVN10__cxxabiv120__si_class_type_infoE/_ZTVSt19__iosfail_type_info/' diff --git a/libstdc++-v3/src/c++11/shared_ptr.cc b/libstdc++-v3/src/c++11/shared_ptr.cc index 4678fbeffe2..144273e6cea 100644 --- a/libstdc++-v3/src/c++11/shared_ptr.cc +++ b/libstdc++-v3/src/c++11/shared_ptr.cc @@ -22,12 +22,56 @@ // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see // <http://www.gnu.org/licenses/>. +#include <cerrno> #include <memory> #include "mutex_pool.h" namespace __gnu_internal _GLIBCXX_VISIBILITY(hidden) { + struct __condvar + { + __condvar() noexcept + { +#ifndef __GTHREAD_COND_INIT + __GTHREAD_COND_INIT_FUNCTION(&_M_cond); +#endif + } + + ~__condvar() + { + int __e __attribute__((__unused__)) = __gthread_cond_destroy(&_M_cond); + __glibcxx_assert(__e != EBUSY); // threads are still blocked + } + + __condvar(const __condvar&) = delete; + __condvar& operator=(const __condvar&) = delete; + + void + _M_wait(__gnu_cxx::__mutex& __m) noexcept + { + int __e __attribute__((__unused__)) + = __gthread_cond_wait(&_M_cond, __m.gthread_mutex()); + __glibcxx_assert(__e == 0); + } + + void + _M_notify() noexcept + { + // we do a broadcast here because multiple threads may multiplex a wait + // against the same __condvar + int __e __attribute__((__unused__)) = __gthread_cond_broadcast(&_M_cond); + __glibcxx_assert(__e == 0); + } + + private: +#ifdef __GTHREAD_COND_INIT + __gthread_cond_t _M_cond = __GTHREAD_COND_INIT; +#else + __gthread_cond_t _M_cond; +#endif + }; + /* Returns different instances of __mutex depending on the passed index * in order to limit contention. */ @@ -39,6 +83,18 @@ namespace __gnu_internal _GLIBCXX_VISIBILITY(hidden) static M m[mask + 1]; return m[i]; } + + /* Returns different instances of __condvar depending on the passed index + * in order to limit contention + */ + __condvar& + get_condvar(unsigned char i) + { + // increase alignment to put each condvar on a separate cache line + struct alignas(64) CV : __condvar { }; + static CV cv[mask + 1]; + return cv[i]; + } } namespace std _GLIBCXX_VISIBILITY(default) @@ -90,10 +146,38 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION if (_M_key1 != __gnu_internal::invalid) { __gnu_internal::get_mutex(_M_key1).unlock(); - if (_M_key2 != _M_key1) +#if __cpp_lib_atomic_shared_ptr + if ((_M_key2 != _M_key1) && (_M_key2 != __gnu_internal::invalid)) +#else + if ((_M_key2 != _M_key1)) +#endif __gnu_internal::get_mutex(_M_key2).unlock(); } } + +#if __cpp_lib_atomic_shared_ptr + _Sp_locker::_Sp_locker(const void* p, bool) noexcept + { + _M_key1 = key(p); + _M_key2 = __gnu_internal::invalid; + } + + void + _Sp_locker::_M_wait() noexcept + { + __glibcxx_assert(_M_key1 == _M_key2); // can't hold two locks + __gnu_cxx::__mutex& m = __gnu_internal::get_mutex(_M_key1); + __gnu_internal::get_condvar(_M_key1)._M_wait(m); + } + + void + _Sp_locker::_M_notify() noexcept + { + __glibcxx_assert(_M_key2 == __gnu_internal::invalid); // can't hold a lock + __gnu_internal::get_condvar(_M_key1)._M_notify(); + } +#endif + #endif bool diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/atomic/4.cc b/libstdc++-v3/testsuite/20_util/shared_ptr/atomic/4.cc new file mode 100644 index 00000000000..aa9fce9e235 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/shared_ptr/atomic/4.cc @@ -0,0 +1,28 @@ +// Copyright (C) 2021 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +// { dg-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } +// { dg-require-effective-target gthreads } + +#include <memory> + +#ifndef __cpp_lib_atomic_shared_ptr +# error "Feature-test macro for atomic<shared_ptr<T>> missing in <memory>" +#elif __cpp_lib_atomic_shared_ptr != 201711L +# error "Feature-test macro for atomic<shared_ptr<T>> has wrong value in <memory>" +#endif diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/atomic/5.cc b/libstdc++-v3/testsuite/20_util/shared_ptr/atomic/5.cc new file mode 100644 index 00000000000..5f8791f78b5 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/shared_ptr/atomic/5.cc @@ -0,0 +1,28 @@ +// Copyright (C) 2021 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +// { dg-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } +// { dg-require-effective-target gthreads } + +#include <version> + +#ifndef __cpp_lib_atomic_shared_ptr +# error "Feature-test macro for atomic<shared_ptr<T>> missing in <version>" +#elif __cpp_lib_atomic_shared_ptr != 201711L +# error "Feature-test macro for atomic<shared_ptr<T>> has wrong value in <version>" +#endif diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/atomic/atomic_shared_ptr.cc b/libstdc++-v3/testsuite/20_util/shared_ptr/atomic/atomic_shared_ptr.cc new file mode 100644 index 00000000000..7a34148a974 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/shared_ptr/atomic/atomic_shared_ptr.cc @@ -0,0 +1,159 @@ +// Copyright (C) 2020-2021 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +// { dg-options "-std=gnu++2a" } +// { dg-do run { target c++2a } } +// { dg-additional-options "-pthread" { target pthread } } +// { dg-require-gthreads "" } + +#include <memory> +#include <thread> + +#include <testsuite_hooks.h> + +template<typename Tp> + void + test_is_lock_free() + { + using test_type = std::atomic<Tp>; + VERIFY( test_type::is_always_lock_free == false ); + + test_type p; + VERIFY( p.is_lock_free() == false ); + } + +struct A { int a; int b; }; + +template<typename Tp, typename Ta> + void + test_wait_notify(Tp& p, const Ta& a, const Ta& b) + { + p.store(a); + p.wait(b); + std::thread t([&] + { + p.store(b); + p.notify_one(); + }); + p.wait(a); + t.join(); + } + +void +test_atomic_shared_ptr() +{ + test_is_lock_free<std::shared_ptr<int>>(); + auto a = std::make_shared<A>( 0, 42 ); + using ptr_t = std::shared_ptr<A>; + { + std::atomic<ptr_t> p{ }; + VERIFY( p.load().get() == nullptr ); + } + + std::atomic<ptr_t> p{ a }; + VERIFY( p.load().get() == a.get() ); + auto b = std::make_shared<A>( 42, 0 ); + p.store(b); + VERIFY( p.load().get() != a.get() ); + VERIFY( p.load().get() == b.get() ); + p.exchange(a); + VERIFY( p.load().get() != b.get() ); + VERIFY( p.load().get() == a.get() ); + + { + ptr_t aa{ a }; + VERIFY( p.compare_exchange_strong(aa, b, + std::memory_order_seq_cst, + std::memory_order_seq_cst) == true ); + ptr_t bb{ a }; + VERIFY( p.compare_exchange_strong(bb, b, + std::memory_order_seq_cst, + std::memory_order_seq_cst) == false ); + VERIFY( bb.get() == b.get() ); + } + + { + ptr_t bb{ b }; + VERIFY( p.compare_exchange_weak(bb, a, + std::memory_order_seq_cst, + std::memory_order_seq_cst) == true ); + ptr_t aa{ b }; + VERIFY( p.compare_exchange_weak(aa, a, + std::memory_order_seq_cst, + std::memory_order_seq_cst) == false ); + VERIFY( aa.get() == a.get() ); + } + test_wait_notify(p, a, b); +} + +void +test_atomic_weak_ptr() +{ + test_is_lock_free<std::weak_ptr<int>>(); + auto a = std::make_shared<A>( 0, 42 ); + using ptr_t = std::weak_ptr<A>; + ptr_t wa{ a }; + { + std::atomic<ptr_t> p{ }; + VERIFY( p.load().lock().get() == nullptr ); + } + + std::atomic<ptr_t> p{ wa }; + VERIFY( p.load().lock().get() == a.get() ); + + auto b = std::make_shared<A>( 42, 0 ); + ptr_t wb{ b }; + p.store(wb); + VERIFY( p.load().lock().get() != a.get() ); + VERIFY( p.load().lock().get() == b.get() ); + p.exchange(wa); + VERIFY( p.load().lock().get() != b.get() ); + VERIFY( p.load().lock().get() == a.get() ); + + { + ptr_t aa{ a }; + VERIFY( p.compare_exchange_strong(aa, b, + std::memory_order_seq_cst, + std::memory_order_seq_cst) == true ); + ptr_t bb{ a }; + VERIFY( p.compare_exchange_strong(bb, b, + std::memory_order_seq_cst, + std::memory_order_seq_cst) == false ); + VERIFY( bb.lock().get() == b.get() ); + } + + { + ptr_t bb{ b }; + VERIFY( p.compare_exchange_weak(bb, a, + std::memory_order_seq_cst, + std::memory_order_seq_cst) == true ); + ptr_t aa{ b }; + VERIFY( p.compare_exchange_weak(aa, a, + std::memory_order_seq_cst, + std::memory_order_seq_cst) == false ); + VERIFY( aa.lock().get() == a.get() ); + } + test_wait_notify(p, wa, wb); +} + +int +main() +{ + test_atomic_shared_ptr(); + test_atomic_weak_ptr(); + return 0; +} -- 2.31.1