From be8de8946ed904c92c09dadb158e76c9c5044d11 Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Wed, 12 Nov 2014 23:55:11 +0000 Subject: [PATCH] std::shared_ptr atomic operations PR libstdc++/57250 * config/abi/pre/gnu.ver: Export new symbols. * include/Makefile.am: Add new header. * include/Makefile.in: Regenerate. * include/bits/shared_ptr_atomic.h: Define atomic access functions. * include/std/memory: Include new header. * src/c++11/shared_ptr.cc (_Sp_locker): Define and use mutex pool. * testsuite/20_util/shared_ptr/atomic/1.cc: New. * testsuite/20_util/shared_ptr/atomic/2.cc: New. * testsuite/20_util/shared_ptr/atomic/3.cc: New. From-SVN: r217452 --- libstdc++-v3/ChangeLog | 13 + libstdc++-v3/config/abi/pre/gnu.ver | 3 + libstdc++-v3/include/Makefile.am | 1 + libstdc++-v3/include/Makefile.in | 1 + libstdc++-v3/include/bits/shared_ptr_atomic.h | 330 ++++++++++++++++++ libstdc++-v3/include/std/memory | 1 + libstdc++-v3/src/c++11/shared_ptr.cc | 58 +++ .../testsuite/20_util/shared_ptr/atomic/1.cc | 40 +++ .../testsuite/20_util/shared_ptr/atomic/2.cc | 40 +++ .../testsuite/20_util/shared_ptr/atomic/3.cc | 53 +++ 10 files changed, 540 insertions(+) create mode 100644 libstdc++-v3/include/bits/shared_ptr_atomic.h create mode 100644 libstdc++-v3/testsuite/20_util/shared_ptr/atomic/1.cc create mode 100644 libstdc++-v3/testsuite/20_util/shared_ptr/atomic/2.cc create mode 100644 libstdc++-v3/testsuite/20_util/shared_ptr/atomic/3.cc diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index 8960ef744cc..d6339c9f6e9 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,16 @@ +2014-11-12 Jonathan Wakely + + PR libstdc++/57250 + * config/abi/pre/gnu.ver: Export new symbols. + * include/Makefile.am: Add new header. + * include/Makefile.in: Regenerate. + * include/bits/shared_ptr_atomic.h: Define atomic access functions. + * include/std/memory: Include new header. + * src/c++11/shared_ptr.cc (_Sp_locker): Define and use mutex pool. + * testsuite/20_util/shared_ptr/atomic/1.cc: New. + * testsuite/20_util/shared_ptr/atomic/2.cc: New. + * testsuite/20_util/shared_ptr/atomic/3.cc: New. + 2014-11-12 Jonathan Wakely * include/bits/shared_ptr.h (weak_ptr): Add move constructor and diff --git a/libstdc++-v3/config/abi/pre/gnu.ver b/libstdc++-v3/config/abi/pre/gnu.ver index 4c6d994197f..bd44bcc3ba6 100644 --- a/libstdc++-v3/config/abi/pre/gnu.ver +++ b/libstdc++-v3/config/abi/pre/gnu.ver @@ -1476,6 +1476,9 @@ GLIBCXX_3.4.21 { # std::ctype_base::blank _ZNSt10ctype_base5blankE; + # std::_Sp_locker::* + _ZNSt10_Sp_locker[CD]*; + } GLIBCXX_3.4.20; diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am index 5fa243b96f4..e6edc732779 100644 --- a/libstdc++-v3/include/Makefile.am +++ b/libstdc++-v3/include/Makefile.am @@ -143,6 +143,7 @@ bits_headers = \ ${bits_srcdir}/stream_iterator.h \ ${bits_srcdir}/streambuf_iterator.h \ ${bits_srcdir}/shared_ptr.h \ + ${bits_srcdir}/shared_ptr_atomic.h \ ${bits_srcdir}/shared_ptr_base.h \ ${bits_srcdir}/slice_array.h \ ${bits_srcdir}/sstream.tcc \ diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in index 4ae4e41f3ac..2ade448de98 100644 --- a/libstdc++-v3/include/Makefile.in +++ b/libstdc++-v3/include/Makefile.in @@ -410,6 +410,7 @@ bits_headers = \ ${bits_srcdir}/stream_iterator.h \ ${bits_srcdir}/streambuf_iterator.h \ ${bits_srcdir}/shared_ptr.h \ + ${bits_srcdir}/shared_ptr_atomic.h \ ${bits_srcdir}/shared_ptr_base.h \ ${bits_srcdir}/slice_array.h \ ${bits_srcdir}/sstream.tcc \ diff --git a/libstdc++-v3/include/bits/shared_ptr_atomic.h b/libstdc++-v3/include/bits/shared_ptr_atomic.h new file mode 100644 index 00000000000..79e35ec3b2d --- /dev/null +++ b/libstdc++-v3/include/bits/shared_ptr_atomic.h @@ -0,0 +1,330 @@ +// shared_ptr atomic access -*- C++ -*- + +// Copyright (C) 2014 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. + +// Under Section 7 of GPL version 3, you are granted additional +// permissions described in the GCC Runtime Library Exception, version +// 3.1, as published by the Free Software Foundation. + +// You should have received a copy of the GNU General Public License and +// a copy of the GCC Runtime Library Exception along with this program; +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +// . + +/** @file bits/shared_ptr_atomic.h + * This is an internal header file, included by other library headers. + * Do not attempt to use it directly. @headername{memory} + */ + +#ifndef _SHARED_PTR_ATOMIC_H +#define _SHARED_PTR_ATOMIC_H 1 + +#include + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + /** + * @addtogroup pointer_abstractions + * @{ + */ + + struct _Sp_locker + { + _Sp_locker(const _Sp_locker&) = delete; + _Sp_locker& operator=(const _Sp_locker&) = delete; + +#ifdef __GTHREADS + explicit + _Sp_locker(const void*) noexcept; + _Sp_locker(const void*, const void*) noexcept; + ~_Sp_locker(); + + private: + unsigned char _M_key1; + unsigned char _M_key2; +#else + explicit _Sp_locker(const void*, const void* = nullptr) { } +#endif + }; + + /** + * @brief Report whether shared_ptr atomic operations are lock-free. + * @param __p A non-null pointer to a shared_ptr object. + * @return True if atomic access to @c *__p is lock-free, false otherwise. + * @{ + */ + template + inline bool + atomic_is_lock_free(const __shared_ptr<_Tp, _Lp>* __p) + { +#ifdef __GTHREADS + return __gthread_active_p() == 0; +#else + return true; +#endif + } + + template + inline bool + atomic_is_lock_free(const shared_ptr<_Tp>* __p) + { return std::atomic_is_lock_free<_Tp, __default_lock_policy>(__p); } + + // @} + + /** + * @brief Atomic load for shared_ptr objects. + * @param __p A non-null pointer to a shared_ptr object. + * @return @c *__p + * + * The memory order shall not be @c memory_order_release or + * @c memory_order_acq_rel. + * @{ + */ + template + inline shared_ptr<_Tp> + atomic_load_explicit(const shared_ptr<_Tp>* __p, memory_order) + { + _Sp_locker __lock{__p}; + return *__p; + } + + template + inline shared_ptr<_Tp> + atomic_load(const shared_ptr<_Tp>* __p) + { return std::atomic_load_explicit(__p, memory_order_seq_cst); } + + template + inline __shared_ptr<_Tp, _Lp> + atomic_load_explicit(const __shared_ptr<_Tp, _Lp>* __p, memory_order) + { + _Sp_locker __lock{__p}; + return *__p; + } + + template + inline __shared_ptr<_Tp, _Lp> + atomic_load(const __shared_ptr<_Tp, _Lp>* __p) + { return std::atomic_load_explicit(__p, memory_order_seq_cst); } + // @} + + /** + * @brief Atomic store for shared_ptr objects. + * @param __p A non-null pointer to a shared_ptr object. + * @param __r The value to store. + * + * The memory order shall not be @c memory_order_acquire or + * @c memory_order_acq_rel. + * @{ + */ + template + inline void + atomic_store_explicit(shared_ptr<_Tp>* __p, shared_ptr<_Tp> __r, + memory_order) + { + _Sp_locker __lock{__p}; + __p->swap(__r); // use swap so that **__p not destroyed while lock held + } + + template + inline void + atomic_store(shared_ptr<_Tp>* __p, shared_ptr<_Tp> __r) + { std::atomic_store_explicit(__p, std::move(__r), memory_order_seq_cst); } + + template + inline void + atomic_store_explicit(__shared_ptr<_Tp, _Lp>* __p, + __shared_ptr<_Tp, _Lp> __r, + memory_order) + { + _Sp_locker __lock{__p}; + __p->swap(__r); // use swap so that **__p not destroyed while lock held + } + + template + inline void + atomic_store(__shared_ptr<_Tp, _Lp>* __p, __shared_ptr<_Tp, _Lp> __r) + { std::atomic_store_explicit(__p, std::move(__r), memory_order_seq_cst); } + // @} + + /** + * @brief Atomic exchange for shared_ptr objects. + * @param __p A non-null pointer to a shared_ptr object. + * @param __r New value to store in @c *__p. + * @return The original value of @c *__p + * @{ + */ + template + inline shared_ptr<_Tp> + atomic_exchange_explicit(shared_ptr<_Tp>* __p, shared_ptr<_Tp> __r, + memory_order) + { + _Sp_locker __lock{__p}; + __p->swap(__r); + return __r; + } + + template + inline shared_ptr<_Tp> + atomic_exchange(shared_ptr<_Tp>* __p, shared_ptr<_Tp> __r) + { + return std::atomic_exchange_explicit(__p, std::move(__r), + memory_order_seq_cst); + } + + template + inline __shared_ptr<_Tp, _Lp> + atomic_exchange_explicit(__shared_ptr<_Tp, _Lp>* __p, + __shared_ptr<_Tp, _Lp> __r, + memory_order) + { + _Sp_locker __lock{__p}; + __p->swap(__r); + return __r; + } + + template + inline __shared_ptr<_Tp, _Lp> + atomic_exchange(__shared_ptr<_Tp, _Lp>* __p, __shared_ptr<_Tp, _Lp> __r) + { + return std::atomic_exchange_explicit(__p, std::move(__r), + memory_order_seq_cst); + } + // @} + + /** + * @brief Atomic compare-and-swap for shared_ptr objects. + * @param __p A non-null pointer to a shared_ptr object. + * @param __v A non-null pointer to a shared_ptr object. + * @param __w A non-null pointer to a shared_ptr object. + * @return True if @c *__p was equivalent to @c *__v, false otherwise. + * + * The memory order for failure shall not be @c memory_order_release or + * @c memory_order_acq_rel, or stronger than the memory order for success. + * @{ + */ + template + bool + atomic_compare_exchange_strong_explicit(shared_ptr<_Tp>* __p, + shared_ptr<_Tp>* __v, + shared_ptr<_Tp> __w, + memory_order, + memory_order) + { + shared_ptr<_Tp> __x; // goes out of scope after __lock + _Sp_locker __lock{__p, __v}; + owner_less> __less; + if (*__p == *__v && !__less(*__p, *__v) && !__less(*__v, *__p)) + { + __x = std::move(*__p); + *__p = std::move(__w); + return true; + } + __x = std::move(*__v); + *__v = *__p; + return false; + } + + template + inline bool + atomic_compare_exchange_strong(shared_ptr<_Tp>* __p, shared_ptr<_Tp>* __v, + shared_ptr<_Tp> __w) + { + return std::atomic_compare_exchange_strong_explicit(__p, __v, + std::move(__w), memory_order_seq_cst, memory_order_seq_cst); + } + + template + inline bool + atomic_compare_exchange_weak_explicit(shared_ptr<_Tp>* __p, + shared_ptr<_Tp>* __v, + shared_ptr<_Tp> __w, + memory_order __success, + memory_order __failure) + { + return std::atomic_compare_exchange_strong_explicit(__p, __v, + std::move(__w), __success, __failure); + } + + template + inline bool + atomic_compare_exchange_weak(shared_ptr<_Tp>* __p, shared_ptr<_Tp>* __v, + shared_ptr<_Tp> __w) + { + return std::atomic_compare_exchange_weak_explicit(__p, __v, + std::move(__w), memory_order_seq_cst, memory_order_seq_cst); + } + + template + bool + atomic_compare_exchange_strong_explicit(__shared_ptr<_Tp, _Lp>* __p, + __shared_ptr<_Tp, _Lp>* __v, + __shared_ptr<_Tp, _Lp> __w, + memory_order, + memory_order) + { + __shared_ptr<_Tp, _Lp> __x; // goes out of scope after __lock + _Sp_locker __lock{__p, __v}; + owner_less<__shared_ptr<_Tp, _Lp>> __less; + if (*__p == *__v && !__less(*__p, *__v) && !__less(*__v, *__p)) + { + __x = std::move(*__p); + *__p = std::move(__w); + return true; + } + __x = std::move(*__v); + *__v = *__p; + return false; + } + + template + inline bool + atomic_compare_exchange_strong(__shared_ptr<_Tp, _Lp>* __p, + __shared_ptr<_Tp, _Lp>* __v, + __shared_ptr<_Tp, _Lp> __w) + { + return std::atomic_compare_exchange_strong_explicit(__p, __v, + std::move(__w), memory_order_seq_cst, memory_order_seq_cst); + } + + template + inline bool + atomic_compare_exchange_weak_explicit(__shared_ptr<_Tp, _Lp>* __p, + __shared_ptr<_Tp, _Lp>* __v, + __shared_ptr<_Tp, _Lp> __w, + memory_order __success, + memory_order __failure) + { + return std::atomic_compare_exchange_strong_explicit(__p, __v, + std::move(__w), __success, __failure); + } + + template + inline bool + atomic_compare_exchange_weak(__shared_ptr<_Tp, _Lp>* __p, + __shared_ptr<_Tp, _Lp>* __v, + __shared_ptr<_Tp, _Lp> __w) + { + return std::atomic_compare_exchange_weak_explicit(__p, __v, + std::move(__w), memory_order_seq_cst, memory_order_seq_cst); + } + // @} + + // @} group pointer_abstractions + +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace + +#endif // _SHARED_PTR_ATOMIC_H diff --git a/libstdc++-v3/include/std/memory b/libstdc++-v3/include/std/memory index b5792ad78b7..3d1c8a9d2e3 100644 --- a/libstdc++-v3/include/std/memory +++ b/libstdc++-v3/include/std/memory @@ -80,6 +80,7 @@ # include # include # include +# include # if _GLIBCXX_USE_DEPRECATED # include # endif diff --git a/libstdc++-v3/src/c++11/shared_ptr.cc b/libstdc++-v3/src/c++11/shared_ptr.cc index 53b3452f093..924753258ff 100644 --- a/libstdc++-v3/src/c++11/shared_ptr.cc +++ b/libstdc++-v3/src/c++11/shared_ptr.cc @@ -34,5 +34,63 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION bad_weak_ptr::what() const noexcept { return "bad_weak_ptr"; } +#ifdef __GTHREADS + namespace + { + const unsigned char mask = 0xf; + const unsigned char invalid = mask + 1; + + inline unsigned char key(const void* addr) + { return _Hash_impl::hash(addr) & mask; } + + /* Returns different instances of __mutex depending on the passed address + * in order to limit contention. + */ + __gnu_cxx::__mutex& + get_mutex(unsigned char i) + { + static __gnu_cxx::__mutex m[mask + 1]; + return m[i]; + } + } + + _Sp_locker::_Sp_locker(const void* p) + { + if (__gthread_active_p()) + { + _M_key1 = _M_key2 = key(p); + get_mutex(_M_key1).lock(); + } + else + _M_key1 = _M_key2 = invalid; + } + + _Sp_locker::_Sp_locker(const void* p1, const void* p2) + { + if (__gthread_active_p()) + { + _M_key1 = key(p1); + _M_key2 = key(p2); + if (_M_key2 < _M_key1) + get_mutex(_M_key2).lock(); + get_mutex(_M_key1).lock(); + if (_M_key2 > _M_key1) + get_mutex(_M_key2).lock(); + } + else + _M_key1 = _M_key2 = invalid; + } + + _Sp_locker::~_Sp_locker() + { + if (_M_key1 != invalid) + { + get_mutex(_M_key1).unlock(); + if (_M_key2 != _M_key1) + get_mutex(_M_key2).unlock(); + } + } +#endif + _GLIBCXX_END_NAMESPACE_VERSION } // namespace diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/atomic/1.cc b/libstdc++-v3/testsuite/20_util/shared_ptr/atomic/1.cc new file mode 100644 index 00000000000..37ab333f287 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/shared_ptr/atomic/1.cc @@ -0,0 +1,40 @@ +// Copyright (C) 2014 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++11" } +// { dg-require-gthreads "" } + +#include + +int main() +{ + using test_type = std::shared_ptr; + bool test __attribute__ ((unused)) = false; + constexpr auto mo = std::memory_order_seq_cst; + const test_type p; + test = std::atomic_is_lock_free(&p); + test_type p2 = std::atomic_load(&p); + test_type p3 = std::atomic_load_explicit(&p, mo); + std::atomic_store(&p2, p); + std::atomic_store_explicit(&p2, p, mo); + test_type p4 = std::atomic_exchange(&p2, p); + p4 = std::atomic_exchange_explicit(&p2, p, mo); + test = std::atomic_compare_exchange_weak(&p2, &p3, p); + test = std::atomic_compare_exchange_strong(&p2, &p3, p); + test = std::atomic_compare_exchange_weak_explicit(&p2, &p3, p, mo, mo); + test = std::atomic_compare_exchange_strong_explicit(&p2, &p3, p, mo, mo); +} diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/atomic/2.cc b/libstdc++-v3/testsuite/20_util/shared_ptr/atomic/2.cc new file mode 100644 index 00000000000..846a6633784 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/shared_ptr/atomic/2.cc @@ -0,0 +1,40 @@ +// Copyright (C) 2014 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++11" } +// { dg-require-gthreads "" } + +#include + +int main() +{ + using test_type = std::__shared_ptr; + bool test __attribute__ ((unused)) = false; + constexpr auto mo = std::memory_order_seq_cst; + const test_type p; + test = std::atomic_is_lock_free(&p); + test_type p2 = std::atomic_load(&p); + test_type p3 = std::atomic_load_explicit(&p, mo); + std::atomic_store(&p2, p); + std::atomic_store_explicit(&p2, p, mo); + test_type p4 = std::atomic_exchange(&p2, p); + p4 = std::atomic_exchange_explicit(&p2, p, mo); + test = std::atomic_compare_exchange_weak(&p2, &p3, p); + test = std::atomic_compare_exchange_strong(&p2, &p3, p); + test = std::atomic_compare_exchange_weak_explicit(&p2, &p3, p, mo, mo); + test = std::atomic_compare_exchange_strong_explicit(&p2, &p3, p, mo, mo); +} diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/atomic/3.cc b/libstdc++-v3/testsuite/20_util/shared_ptr/atomic/3.cc new file mode 100644 index 00000000000..f9648062bd8 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/shared_ptr/atomic/3.cc @@ -0,0 +1,53 @@ +// { dg-do run { target *-*-freebsd* *-*-dragonfly* *-*-netbsd* *-*-linux* *-*-gnu* *-*-solaris* *-*-cygwin *-*-darwin* powerpc-ibm-aix* } } +// { dg-options " -std=gnu++11 -pthread" { target *-*-freebsd* *-*-dragonfly* *-*-netbsd* *-*-linux* *-*-gnu* powerpc-ibm-aix* } } +// { dg-options " -std=gnu++11 -pthreads" { target *-*-solaris* } } +// { dg-options " -std=gnu++11 " { target *-*-cygwin *-*-darwin* } } +// { dg-require-cstdint "" } +// { dg-require-gthreads "" } + +// Copyright (C) 2014 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 +// . + +#include +#include +#include + +struct leaddock +{ + ~leaddock(); +}; + +std::shared_ptr global; + +leaddock::~leaddock() +{ + // If this destructor is called "inside" an atomic operation on global it + // will deadlock, so this checks that the atomic_store is done atomically. + auto copy = std::atomic_load(&global); + VERIFY( !copy ); +} + +void f() +{ + std::atomic_store(&global, std::make_shared()); + std::atomic_store(&global, {}); +} + +int main() +{ + std::thread{ f }.join(); +} -- 2.30.2