From patchwork Fri Sep 17 17:51:52 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Thomas Rodgers X-Patchwork-Id: 45139 X-Patchwork-Delegate: jwakely.gcc@gmail.com Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 80D36385C400 for ; Fri, 17 Sep 2021 17:52:27 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from ext-mx-out001.mykolab.com (mx.kolabnow.com [95.128.36.42]) by sourceware.org (Postfix) with ESMTPS id 093633858420; Fri, 17 Sep 2021 17:52:09 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 093633858420 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-out001.mykolab.com (Postfix) with ESMTP id 5923012AF; Fri, 17 Sep 2021 19:52:07 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kolabnow.com; h= content-transfer-encoding:mime-version:message-id:date:date :subject:subject:from:from:received:received:received; s= dkim20160331; t=1631901125; x=1633715526; bh=RJo8/hrZrOora53drHs VtVbqepiJLQkDmoiXjgYK6uI=; b=hlQoVcE9YU/q6ZzajDDVTfVC0oo6r+wnWiX qy68mI1WRZLFc9Q3ZeKhdI3hYf54RFWqi0JC8qKvYW+4qNCm15THWBDtgWcxudp8 n8vrnbLZXVAspPF0l7FzdIbkG9u6FXe+WogPlNln+4ERmZrNQsOJtiw9yylW0zX4 tqmQsSUAl/P1ttKP5o3pt6fmNsyO1n2AUaFHGfw9aX1BMiimh0LxFS/w23wIyHX4 6o3HkSM8OxMl60p5SxaffxdDMl2MM6FgSCgnBe1W+JLAwH1tD5iBNbLSmLS93lOA ZgKTnfw52PuqGJNUk3ctHR1sdEGvMR7CazCpo/Tk9TUBX7ijS0GeDPcc0oSWFUcs jJJ9Z8wIgT1lFspHpyGhId3hV1LGiYgxvKiVO31sORX25s4aYixAfG3XDYFwlxBT WJcBr4QCM6QazxXt3YvKtgSA1+NCRFeH9G+URVq8sNFTfQ0pGZvEDFItoEtS+5jE KroB93FoCl1mXTw139NU1Rjfr1Up+TMHMuqSleBk1s3g9wxF1cnOi8juQo1ZXxL8 kEZMUAdzfLSFOuA3TMmRRwblISfqReWN/ebEVpWc0sTV3vUJ9kAGcX8I34V9pvly vYy3V5UO1bBEhyst9OY3Zl/6lr/D4DlS7VUP+eXdo1gkBC+IJ3AZZrzxz0Z6iNaB yURFNcko= X-Virus-Scanned: amavisd-new at mykolab.com X-Spam-Score: -1.899 X-Spam-Level: X-Spam-Status: No, score=-11.9 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-out001.mykolab.com ([127.0.0.1]) by localhost (ext-mx-out001.mykolab.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id sl7bPsqWO6f7; Fri, 17 Sep 2021 19:52:05 +0200 (CEST) Received: from int-mx002.mykolab.com (unknown [10.9.13.2]) by ext-mx-out001.mykolab.com (Postfix) with ESMTPS id 190EAF76; Fri, 17 Sep 2021 19:52:05 +0200 (CEST) Received: from ext-subm002.mykolab.com (unknown [10.9.6.2]) by int-mx002.mykolab.com (Postfix) with ESMTPS id 0D219261D; Fri, 17 Sep 2021 19:52:03 +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 10:51:52 -0700 Message-Id: <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+patchwork=sourceware.org@gcc.gnu.org Sender: "Gcc-patches" From: Thomas Rodgers Signed-off-by: Thomas Rodgers 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>): New partial template specialization. (atomic>): 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 +#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; +}