@@ -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 {
@@ -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
@@ -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
@@ -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/'
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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
new file mode 100644
@@ -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;
+}