diff mbox series

libstdc++: Implement C++20 atomic<shared_ptr> and atomic<weak_ptr>

Message ID 20210917175152.606596-1-rodgert@appliantology.com
State New
Headers show
Series libstdc++: Implement C++20 atomic<shared_ptr> and atomic<weak_ptr> | expand

Commit Message

Thomas Rodgers Sept. 17, 2021, 5:51 p.m. UTC
From: Thomas Rodgers <rodgert@twrodgers.com>

Signed-off-by: Thomas Rodgers <trodgers@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 mbox series

Patch

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;
+}