From patchwork Sat Sep 18 04:01:57 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Rodgers X-Patchwork-Id: 1529593 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; dkim=pass (4096-bit key; secure) header.d=kolabnow.com header.i=@kolabnow.com header.a=rsa-sha256 header.s=dkim20160331 header.b=YfT48Gjw; dkim-atps=neutral Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=sourceware.org; envelope-from=gcc-patches-bounces+incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=) Received: from sourceware.org (server2.sourceware.org [IPv6:2620:52:3:1:0:246e:9693:128c]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4HBHSg5nj5z9sSn for ; Sat, 18 Sep 2021 14:11:31 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 2005C385C405 for ; Sat, 18 Sep 2021 04:11:29 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from ext-mx-out003.mykolab.com (mx.kolabnow.com [95.128.36.41]) by sourceware.org (Postfix) with ESMTPS id 7517E3858428; Sat, 18 Sep 2021 04:11:10 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 7517E3858428 Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=appliantology.com Authentication-Results: sourceware.org; spf=none smtp.mailfrom=appliantology.com Received: from localhost (unknown [127.0.0.1]) by ext-mx-out003.mykolab.com (Postfix) with ESMTP id CBD7140AE3; Sat, 18 Sep 2021 06:11:08 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kolabnow.com; h= content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:date:subject:subject:from:from:received :received:received; s=dkim20160331; t=1631938266; x=1633752667; bh=1wvdkKGVrYKMHWKcw/9H+iIFDISP6fhx5nARBBfEgxU=; b=YfT48GjwBWIm loQQgsf4Qtx9rj6HdgooVj2xU8ENfUGL3L83swQWh6MqPBIYPQIJgjnBGRrAdnWo db8u520TLuTmZ/AOgvRTxVu4QvB2XloziKeTPzeDc1G9QxjSU3LpXBSgwiML9Wrv w7NlhF1W0YRiSu2oGkucf3Q4hT/C0gPIaM0in2sHXOV2n55Hqiz14vFlUn/ci1Wf xZUHMXcg04c5TLD0dSoa+xe02XQOKT4uspCrAHo+FiGd6gLBSoQxBycR/Rj4w7qh enr6qYnJ9+8UVjm2fCMGgMvoxVvWN1OSoiTC4enFKsYMykZE3wWubuSNebmNgIOd tM+4tmnkoiI4/Esg0ncWdygpV3rLUgaks1g0kRb/NV8FO0KIfmNcXODzMEu4C0et sBqLYw/WAkljJPtcqffkeHFuZkkbHePn7MaFNvL8nMnfGBbU7tRDeiol0tCewIcP G0lYQ/OsHqk1enie4E8Ll8ZIDsCWOBY1mb2K82xQMVarKwD8NOU1Bp7xvoi5ZzZ4 wXwuO/tBMjc0E2UA1N8cLRUJEoU16nUHNoFGYhcbUHrPg8ND2aI9AfOUdkwmeYGF mariza9b3TYql+aqJDmVOpX6FUZExoYPCLf9/69snIos0nzpuv7trOL+cIPGlaf+ LxQZYr27nrNnwZvlZGBJWHFaOzuJNtA= X-Virus-Scanned: amavisd-new at mykolab.com X-Spam-Score: -1.9 X-Spam-Level: X-Spam-Status: No, score=-12.0 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, GIT_PATCH_0, KAM_SHORT, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.4 Received: from ext-mx-out003.mykolab.com ([127.0.0.1]) by localhost (ext-mx-out003.mykolab.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id h7tl7G8kJHew; Sat, 18 Sep 2021 06:11:06 +0200 (CEST) Received: from int-mx002.mykolab.com (unknown [10.9.13.2]) by ext-mx-out003.mykolab.com (Postfix) with ESMTPS id CA07940948; Sat, 18 Sep 2021 06:11:06 +0200 (CEST) Received: from ext-subm002.mykolab.com (unknown [10.9.6.2]) by int-mx002.mykolab.com (Postfix) with ESMTPS id E14842F36; Sat, 18 Sep 2021 06:02:04 +0200 (CEST) From: Thomas Rodgers To: gcc-patches@gcc.gnu.org, libstdc++@gcc.gnu.org Subject: [PATCH] libstdc++: Implement C++20 atomic and atomic Date: Fri, 17 Sep 2021 21:01:57 -0700 Message-Id: <20210918040157.608691-1-rodgert@appliantology.com> In-Reply-To: <20210917175152.606596-1-rodgert@appliantology.com> References: <20210917175152.606596-1-rodgert@appliantology.com> MIME-Version: 1.0 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: trodgers@redhat.com, Thomas Rodgers Errors-To: gcc-patches-bounces+incoming=patchwork.ozlabs.org@gcc.gnu.org Sender: "Gcc-patches" From: Thomas Rodgers Let's try this one instead. Signed-off-by: Thomas Rodgers libstdc++-v3/ChangeLog: * acinclude.m4: Update ABI version. * 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>): New partial template specialization. (atomic>): New partial template specialization. * include/std/version: define __cpp_lib_atomic_shared_ptr feature test macro. * doc/xml/manual/abi.xml: New ABI version. * 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. * testuite/util/testsuite_abi.cc: New ABI version. --- libstdc++-v3/acinclude.m4 | 2 +- libstdc++-v3/config/abi/pre/gnu.ver | 12 +- libstdc++-v3/configure | 2 +- libstdc++-v3/doc/xml/manual/abi.xml | 1 + 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 +++++++++ libstdc++-v3/testsuite/util/testsuite_abi.cc | 3 +- 13 files changed, 637 insertions(+), 6 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/acinclude.m4 b/libstdc++-v3/acinclude.m4 index 90ecc4a87a2..30a4abb98b3 100644 --- a/libstdc++-v3/acinclude.m4 +++ b/libstdc++-v3/acinclude.m4 @@ -3798,7 +3798,7 @@ changequote([,])dnl fi # For libtool versioning info, format is CURRENT:REVISION:AGE -libtool_VERSION=6:29:0 +libtool_VERSION=6:30:0 # Everything parsed; figure out what files and settings to use. case $enable_symvers in 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/configure b/libstdc++-v3/configure index 13d52eb0c0e..67ee6db1bea 100755 --- a/libstdc++-v3/configure +++ b/libstdc++-v3/configure @@ -74684,7 +74684,7 @@ $as_echo "$as_me: WARNING: === Symbol versioning will be disabled." >&2;} fi # For libtool versioning info, format is CURRENT:REVISION:AGE -libtool_VERSION=6:29:0 +libtool_VERSION=6:30:0 # Everything parsed; figure out what files and settings to use. case $enable_symvers in diff --git a/libstdc++-v3/doc/xml/manual/abi.xml b/libstdc++-v3/doc/xml/manual/abi.xml index c2c0c028a8b..10bef12c768 100644 --- a/libstdc++-v3/doc/xml/manual/abi.xml +++ b/libstdc++-v3/doc/xml/manual/abi.xml @@ -348,6 +348,7 @@ compatible. GCC 9.3.0: GLIBCXX_3.4.28, CXXABI_1.3.12 GCC 10.1.0: GLIBCXX_3.4.28, CXXABI_1.3.12 GCC 11.1.0: GLIBCXX_3.4.29, CXXABI_1.3.13 + GCC 12.1.0: GLIBCXX_3.4.30, CXXABI_1.3.13 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 +#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 + 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 + 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 + 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 + class atomic> + { + using __impl_t = _Sp_impl>; + + 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 __less; + return __a == __b && !__less(__a, __b) && !__less(__b, __a); + } + }; + + template + class atomic> + { + using __impl_t = _Sp_impl>; + + 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 __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 // . +#include #include #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 +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } +// { dg-require-effective-target gthreads } + +#include + +#ifndef __cpp_lib_atomic_shared_ptr +# error "Feature-test macro for atomic> missing in " +#elif __cpp_lib_atomic_shared_ptr != 201711L +# error "Feature-test macro for atomic> has wrong value in " +#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 +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } +// { dg-require-effective-target gthreads } + +#include + +#ifndef __cpp_lib_atomic_shared_ptr +# error "Feature-test macro for atomic> missing in " +#elif __cpp_lib_atomic_shared_ptr != 201711L +# error "Feature-test macro for atomic> has wrong value in " +#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 +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do run { target c++2a } } +// { dg-additional-options "-pthread" { target pthread } } +// { dg-require-gthreads "" } + +#include +#include + +#include + +template + void + test_is_lock_free() + { + using test_type = std::atomic; + VERIFY( test_type::is_always_lock_free == false ); + + test_type p; + VERIFY( p.is_lock_free() == false ); + } + +struct A { int a; int b; }; + +template + 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>(); + auto a = std::make_shared( 0, 42 ); + using ptr_t = std::shared_ptr; + { + std::atomic p{ }; + VERIFY( p.load().get() == nullptr ); + } + + std::atomic p{ a }; + VERIFY( p.load().get() == a.get() ); + auto b = std::make_shared( 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>(); + auto a = std::make_shared( 0, 42 ); + using ptr_t = std::weak_ptr; + ptr_t wa{ a }; + { + std::atomic p{ }; + VERIFY( p.load().lock().get() == nullptr ); + } + + std::atomic p{ wa }; + VERIFY( p.load().lock().get() == a.get() ); + + auto b = std::make_shared( 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; +} diff --git a/libstdc++-v3/testsuite/util/testsuite_abi.cc b/libstdc++-v3/testsuite/util/testsuite_abi.cc index 3af5dc593c2..1ca7da4fcd0 100644 --- a/libstdc++-v3/testsuite/util/testsuite_abi.cc +++ b/libstdc++-v3/testsuite/util/testsuite_abi.cc @@ -210,6 +210,7 @@ check_version(symbol& test, bool added) known_versions.push_back("GLIBCXX_3.4.27"); known_versions.push_back("GLIBCXX_3.4.28"); known_versions.push_back("GLIBCXX_3.4.29"); + known_versions.push_back("GLIBCXX_3.4.30"); known_versions.push_back("GLIBCXX_LDBL_3.4.29"); known_versions.push_back("GLIBCXX_IEEE128_3.4.29"); known_versions.push_back("CXXABI_1.3"); @@ -245,7 +246,7 @@ check_version(symbol& test, bool added) test.version_status = symbol::incompatible; // Check that added symbols are added in the latest pre-release version. - bool latestp = (test.version_name == "GLIBCXX_3.4.29" + bool latestp = (test.version_name == "GLIBCXX_3.4.30" // XXX remove next 3 lines when baselines have been regenerated // to include {IEEE128,LDBL} symbols: || test.version_name == "GLIBCXX_LDBL_3.4.29"