Updated patch, fixes some whitespace issues along with ensuring that libstdc++-v3/include/Makefile.in is regenerated.
>From 2f707faab97abde776bc7c6e06f7a7c471711962 Mon Sep 17 00:00:00 2001 From: Thomas Rodgers <rodg...@appliantology.com> Date: Thu, 12 Mar 2020 17:50:09 -0700 Subject: [PATCH] Add C++2a wait/notify_one/notify_all support to std::atomic<>
* include/Makefile.am (bits_headers): Add new header. * include/Makefile.in: Regenerate. * include/bits/atomic_base.h (__atomic_base<_Itp>:wait): Define. (__atomic_base<_Itp>::notify_one): Likewise. (__atomic_base<_Itp>::notify_all): Likewise. (__atomic_base<_Ptp*>::wait): Likewise. (__atomic_base<_Ptp*>::notify_one): Likewise. (__atomic_base<_Ptp*>::notify_all): Likewise. (__atomic_impl::wait): Likewise. (__atomic_impl::notify_one): Likewise. (__atomic_impl::notify_all): Likewise. (__atomic_float<_Fp>::wait): Likewise. (__atomic_float<_Fp>::notify_one): Likewise. (__atomic_float<_Fp>::notify_all): Likewise. (__atomic_ref<_Tp>::wait): Likewise. (__atomic_ref<_Tp>::notify_one): Likewise. (__atomic_ref<_Tp>::notify_all): Likewise. (atomic_wait<_Tp>): Likewise. (atomic_wait_explicit<_Tp>): Likewise. (atomic_notify_one<_Tp>): Likewise. (atomic_notify_all<_Tp>): Likewise. * include/bits/atomic_wait.h: New file. * include/std/atomic (atomic<bool>::wait): Define. (atomic<bool>::wait_one): Likewise. (atomic<bool>::wait_all): Likewise. (atomic<_Tp>::wait): Likewise. (atomic<_Tp>::wait_one): Likewise. (atomic<_Tp>::wait_all): Likewise. (atomic<_Tp*>::wait): Likewise. (atomic<_Tp*>::wait_one): Likewise. (atomic<_Tp*>::wait_all): Likewise. * testsuite/29_atomic/atomic/wait_notify/atomic_refs.cc: New test. * testsuite/29_atomic/atomic/wait_notify/bool.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/integrals.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/floats.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/pointers.cc: Likewise. * testsuite/29_atomic/atomic/wait_notify/generic.h: New File. Tested x86_64-pc-linux-gnu. --- libstdc++-v3/include/Makefile.am | 1 + libstdc++-v3/include/Makefile.in | 1 + libstdc++-v3/include/bits/atomic_base.h | 178 ++++++++++- libstdc++-v3/include/bits/atomic_wait.h | 284 ++++++++++++++++++ libstdc++-v3/include/std/atomic | 61 ++++ .../atomic/wait_notify/atomic_refs.cc | 103 +++++++ .../29_atomics/atomic/wait_notify/bool.cc | 57 ++++ .../29_atomics/atomic/wait_notify/floats.cc | 32 ++ .../29_atomics/atomic/wait_notify/generic.h | 88 ++++++ .../atomic/wait_notify/integrals.cc | 56 ++++ .../29_atomics/atomic/wait_notify/pointers.cc | 59 ++++ 11 files changed, 919 insertions(+), 1 deletion(-) create mode 100644 libstdc++-v3/include/bits/atomic_wait.h create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am index 80aeb3f8959..d195a721fd5 100644 --- a/libstdc++-v3/include/Makefile.am +++ b/libstdc++-v3/include/Makefile.am @@ -100,6 +100,7 @@ bits_headers = \ ${bits_srcdir}/allocated_ptr.h \ ${bits_srcdir}/allocator.h \ ${bits_srcdir}/atomic_base.h \ + ${bits_srcdir}/atomic_wait.h \ ${bits_srcdir}/atomic_futex.h \ ${bits_srcdir}/basic_ios.h \ ${bits_srcdir}/basic_ios.tcc \ diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in index eb437ad8d8d..f19c3342f06 100644 --- a/libstdc++-v3/include/Makefile.in +++ b/libstdc++-v3/include/Makefile.in @@ -445,6 +445,7 @@ bits_headers = \ ${bits_srcdir}/allocated_ptr.h \ ${bits_srcdir}/allocator.h \ ${bits_srcdir}/atomic_base.h \ + ${bits_srcdir}/atomic_wait.h \ ${bits_srcdir}/atomic_futex.h \ ${bits_srcdir}/basic_ios.h \ ${bits_srcdir}/basic_ios.tcc \ diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h index 87fe0bd6000..b4fbe2c6eb3 100644 --- a/libstdc++-v3/include/bits/atomic_base.h +++ b/libstdc++-v3/include/bits/atomic_base.h @@ -37,6 +37,11 @@ #include <bits/atomic_lockfree_defines.h> #include <bits/move.h> +#if __cplusplus > 201703L +#include <bits/atomic_wait.h> +#include <iostream> +#endif + #ifndef _GLIBCXX_ALWAYS_INLINE #define _GLIBCXX_ALWAYS_INLINE inline __attribute__((__always_inline__)) #endif @@ -134,7 +139,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return __ret; } - // Base types for atomics. template<typename _IntTp> struct __atomic_base; @@ -542,6 +546,35 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__m)); } +#if __cplusplus > 201703L + _GLIBCXX_ALWAYS_INLINE void + wait(__int_type __old, memory_order __m = memory_order_seq_cst) const noexcept + { + __detail::__atomic_wait(&_M_i, __old, [__m, this](__int_type __o) + { + return this->load(__m) == __o; + }); + } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { + __detail::__atomic_notify(&_M_i, false); + } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { + __detail::__atomic_notify(&_M_i, true); + } + + // TODO add const volatile overload +#endif // C++2a + _GLIBCXX_ALWAYS_INLINE __int_type fetch_add(__int_type __i, memory_order __m = memory_order_seq_cst) noexcept @@ -803,6 +836,35 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION int(__m1), int(__m2)); } +#if __cplusplus > 201703L + _GLIBCXX_ALWAYS_INLINE void + wait(__pointer_type __old, memory_order __m = memory_order_seq_cst) noexcept + { + __detail::__atomic_wait(&_M_p, __old, [__m, this](__pointer_type __o) + { + return this->load(__m) == __o; + }); + } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { + __detail::__atomic_notify(&_M_p, false); + } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { + __detail::__atomic_notify(&_M_p, true); + } + + // TODO add const volatile overload +#endif // C++2a + _GLIBCXX_ALWAYS_INLINE __pointer_type fetch_add(ptrdiff_t __d, memory_order __m = memory_order_seq_cst) noexcept @@ -891,6 +953,39 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION int(__success), int(__failure)); } +#if __cplusplus > 201703L + template<typename _Tp> + _GLIBCXX_ALWAYS_INLINE void + wait(const _Tp* __ptr, _Val<_Tp> __old, memory_order __m = memory_order_seq_cst) noexcept + { + __detail::__atomic_wait_ptr(__ptr, std::__addressof(__old), + [=](_Tp* __o) + { + return load(__ptr, __m) == *__o; + }); + } + + // TODO add const volatile overload + + template<typename _Tp> + _GLIBCXX_ALWAYS_INLINE void + notify_one(const _Tp* __ptr) noexcept + { + __detail::__atomic_notify(__ptr, false); + } + + // TODO add const volatile overload + + template<typename _Tp> + _GLIBCXX_ALWAYS_INLINE void + notify_all(const _Tp* __ptr) noexcept + { + __detail::__atomic_notify(__ptr, true); + } + + // TODO add const volatile overload +#endif // C++2a + template<typename _Tp> _GLIBCXX_ALWAYS_INLINE _Tp fetch_add(_Tp* __ptr, _Diff<_Tp> __i, memory_order __m) noexcept @@ -1144,6 +1239,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Fp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(&_M_fp, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(&_M_fp); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(&_M_fp); } + + // TODO add const volatile overload value_type fetch_add(value_type __i, memory_order __m = memory_order_seq_cst) noexcept @@ -1281,6 +1393,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + private: _Tp* _M_ptr; }; @@ -1376,6 +1504,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + value_type fetch_add(value_type __i, memory_order __m = memory_order_seq_cst) const noexcept @@ -1531,6 +1675,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Fp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + value_type fetch_add(value_type __i, memory_order __m = memory_order_seq_cst) const noexcept @@ -1640,6 +1800,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__order)); } + _GLIBCXX_ALWAYS_INLINE void + wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept + { __atomic_impl::wait(_M_ptr, __old, __m); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_one() const noexcept + { __atomic_impl::notify_one(_M_ptr); } + + // TODO add const volatile overload + + _GLIBCXX_ALWAYS_INLINE void + notify_all() const noexcept + { __atomic_impl::notify_all(_M_ptr); } + _GLIBCXX_ALWAYS_INLINE value_type fetch_add(difference_type __d, memory_order __m = memory_order_seq_cst) const noexcept diff --git a/libstdc++-v3/include/bits/atomic_wait.h b/libstdc++-v3/include/bits/atomic_wait.h new file mode 100644 index 00000000000..c33e25e5ddf --- /dev/null +++ b/libstdc++-v3/include/bits/atomic_wait.h @@ -0,0 +1,284 @@ +// -*- C++ -*- header. + +// Copyright (C) 2020 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 +// <http://www.gnu.org/licenses/>. + +/** @file bits/atomic_wait.h + * This is an internal header file, included by other library headers. + * Do not attempt to use it directly. @headername{atomic} + */ + +#ifndef _GLIBCXX_ATOMIC_WAIT_H +#define _GLIBCXX_ATOMIC_WAIT_H 1 + +#pragma GCC system_header + +#include <bits/c++config.h> +#include <bits/functional_hash.h> +#include <bits/gthr.h> +#include <bits/std_mutex.h> +#include <bits/unique_lock.h> +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX +#include <climits> +#include <unistd.h> +#include <syscall.h> +#endif + +#define _GLIBCXX_SPIN_COUNT_1 16 +#define _GLIBCXX_SPIN_COUNT_2 12 + +// TODO get this from Autoconf +#define _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE 1 + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + namespace __detail + { + using __platform_wait_t = int; + + template<class _Tp> + struct __platform_wait_uses_type + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + enum { __value = std::is_same<typename std::remove_cv<_Tp>::type, + __platform_wait_t>::value }; +#else + enum { __value = std::false_type::value }; +#endif + }; + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + enum + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE + __futex_private_flag = 128, +#else + __futex_private_flag = 0, +#endif + __futex_wait = 0, + __futex_wake = 1, + __futex_wait_private = __futex_wait | __futex_private_flag, + __futex_wake_private = __futex_wake | __futex_private_flag, + }; + + void + __platform_wait(__platform_wait_t* __addr, __platform_wait_t __val) noexcept + { + auto __e = syscall (SYS_futex, __addr, __futex_wait_private, __val, nullptr); + if (__e && (errno != EINTR || errno != EAGAIN)) + std::terminate(); + } + + void + __platform_notify(__platform_wait_t* __addr, bool __all) noexcept + { + syscall (SYS_futex, __addr, __futex_wake_private, __all ? INT_MAX : 1); + } +#endif + + struct alignas(64) __waiters + { + int32_t alignas(64) _M_ver = 0; + int32_t alignas(64) _M_wait = 0; + + // TODO make this used only where we don't have futexes + using __lock_t = std::unique_lock<std::mutex>; + mutable __lock_t::mutex_type _M_mtx; + +#ifdef __GTHREAD_COND_INIT + mutable __gthread_cond_t _M_cv = __GTHREAD_COND_INIT; + __waiters() noexcept = default; +#else + mutable __gthread_cond_t _M_cv; + __waiters() noexcept + { + __GTHREAD_COND_INIT_FUNCTION(&_M_cond); + } +#endif + + int32_t + _M_enter_wait() noexcept + { + int32_t __res; + __atomic_load(&_M_ver, &__res, __ATOMIC_ACQUIRE); + __atomic_fetch_add(&_M_wait, 1, __ATOMIC_ACQ_REL); + return __res; + } + + void + _M_leave_wait() noexcept + { + __atomic_fetch_sub(&_M_wait, 1, __ATOMIC_ACQ_REL); + } + + void + _M_do_wait(int32_t __version) const noexcept + { + int32_t __cur = 0; + while (__cur <= __version) + { + __waiters::__lock_t __l(_M_mtx); + auto __e = __gthread_cond_wait(&_M_cv, __l.mutex()->native_handle()); + if (__e) + std::terminate(); + int32_t __last = __cur; + __atomic_load(&_M_ver, &__cur, __ATOMIC_ACQUIRE); + // protect if version overflows + if (__cur < __last) + break; // break the loop if version overflows + } + } + + int32_t + _M_waiting() const noexcept + { + int32_t __res; + __atomic_load(&_M_wait, &__res, __ATOMIC_ACQUIRE); + return __res; + } + + void + _M_notify(bool __all) noexcept + { + __atomic_fetch_add(&_M_ver, 1, __ATOMIC_ACQ_REL); + auto __e = __gthread_cond_broadcast(&_M_cv); + if (__e) + __throw_system_error(__e); + } + + static __waiters& + _S_for(void* __t) + { + const unsigned char __mask = 0xf; + static __waiters __w[__mask + 1]; + + auto __key = _Hash_impl::hash(__t) & __mask; + return __w[__key]; + } + }; + + void + __thread_relax() noexcept + { +#if defined __i386__ || defined __x86_64__ + __builtin_ia32_pause(); +#elif defined _GLIBCXX_USE_SCHED_YIELD + __gthread_yield(); +#endif + } + + void + __thread_yield() noexcept + { +#if defined _GLIBCXX_USE_SCHED_YIELD + __gthread_yield(); +#endif + } + + template<class _Tp, class _Pred> + bool + __atomic_spin1(_Tp __old, _Pred __pred) noexcept + { + for (auto __i = 0; __i < _GLIBCXX_SPIN_COUNT_1; ++__i) + { + if (!__pred(__old)) + return true; + + if (__i < _GLIBCXX_SPIN_COUNT_2) + __thread_relax(); + else + __thread_yield(); + } + return false; + } + + template<class _Tp, class _Pred> + void + __atomic_wait(const _Tp* __addr, _Tp __old, _Pred __pred) noexcept + { + if (__atomic_spin1(__old, __pred)) + return; + + auto& __w = __waiters::_S_for((void*)__addr); + auto __version = __w._M_enter_wait(); + while (__pred(__old)) + { + if constexpr (__platform_wait_uses_type<_Tp>::__value) + { + __platform_wait((__platform_wait_t*)(void*) __addr, __old); + } + else + { + // TODO support timed backoff when this can be moved into the lib + __w._M_do_wait(__version); + } + } + __w._M_leave_wait(); + } + + template<class _Tp, class _Pred> + void + __atomic_wait_ptr(const _Tp* __addr, _Tp* __old, _Pred __pred) noexcept + { + if (__atomic_spin1(__old, __pred)) + return; + + auto& __w = __waiters::_S_for((void*)__addr); + auto __version = __w._M_enter_wait(); + while (__pred(__old)) + { + if constexpr (__platform_wait_uses_type<_Tp>::__value) + { + __platform_wait((__platform_wait_t*)(void*) __addr, __old); + } + else + { + // TODO support timed backoff when this can be moved into the lib + __w._M_do_wait(__version); + } + } + __w._M_leave_wait(); + } + + template<class _Tp> + void + __atomic_notify(const _Tp* __addr, bool __all) noexcept + { + auto& __w = __waiters::_S_for((void*)__addr); + if (!__w._M_waiting()) + return; + + if constexpr (__platform_wait_uses_type<_Tp>::__value) + { + __platform_notify((__platform_wait_t*)(void*) __addr, __all); + } + else + { + __w._M_notify(__all); + } + } + } // namespace __detail + +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std +#endif diff --git a/libstdc++-v3/include/std/atomic b/libstdc++-v3/include/std/atomic index 40f23bdfc96..cb2308b0cdf 100644 --- a/libstdc++-v3/include/std/atomic +++ b/libstdc++-v3/include/std/atomic @@ -163,6 +163,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION compare_exchange_strong(bool& __i1, bool __i2, memory_order __m = memory_order_seq_cst) volatile noexcept { return _M_base.compare_exchange_strong(__i1, __i2, __m); } + +#if __cplusplus > 201703L + void wait(bool __old, memory_order __m = memory_order_seq_cst) const noexcept + { _M_base.wait(__old, __m); } + + // TODO add const volatile overload + + void notify_one() const noexcept + { _M_base.notify_one(); } + + void notify_all() const noexcept + { _M_base.notify_all(); } +#endif }; #if __cplusplus <= 201703L @@ -352,6 +365,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION memory_order __m = memory_order_seq_cst) volatile noexcept { return compare_exchange_strong(__e, __i, __m, __cmpexch_failure_order(__m)); } +#if __cplusplus > 201703L + void wait(_Tp __old, memory_order __m = memory_order_seq_cst) noexcept + { _M_i.wait(__old, __m); } + + // TODO add const volatile overload + + void notify_one() const noexcept + { _M_i.notify_one(); } + + void notify_all() const noexcept + { _M_i.notify_all(); } +#endif + }; #undef _GLIBCXX20_INIT @@ -590,6 +616,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __cmpexch_failure_order(__m)); } +#if __cplusplus > 201703L + void wait(__pointer_type __old, memory_order __m = memory_order_seq_cst) noexcept + { _M_b.wait(__old, __m); } + + // TODO add const volatile overload + + void notify_one() const noexcept + { _M_b.notify_one(); } + + void notify_all() const noexcept + { _M_b.notify_all(); } +#endif __pointer_type fetch_add(ptrdiff_t __d, memory_order __m = memory_order_seq_cst) noexcept @@ -1342,6 +1380,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION memory_order_seq_cst); } + +#if __cplusplus > 201703L + template<typename _Tp> + inline void atomic_wait(const atomic<_Tp>* __a, + typename std::atomic<_Tp>::value_type __old) noexcept + { __a->wait(__old); } + + template<typename _Tp> + inline void atomic_wait_explicit(const atomic<_Tp>* __a, + typename std::atomic<_Tp>::value_type __old, + std::memory_order __m) noexcept + { __a->wait(__old, __m); } + + template<typename _Tp> + inline void atomic_notify_one(atomic<_Tp>* __a) noexcept + { __a->notify_one(); } + + template<typename _Tp> + inline void atomic_notify_all(atomic<_Tp>* __a) noexcept + { __a->notify_all(); } + +#endif // C++2a + // Function templates for atomic_integral and atomic_pointer operations only. // Some operations (and, or, xor) are only available for atomic integrals, // which is implemented by taking a parameter of type __atomic_base<_ITp>*. diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc new file mode 100644 index 00000000000..1ced9d44b20 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc @@ -0,0 +1,103 @@ +// { dg-options "-std=gnu++2a -pthread -latomic -L../../libatomic/.libs" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <thread> +#include <mutex> +#include <condition_variable> +#include <chrono> +#include <type_traits> + +#include <testsuite_hooks.h> + +template<typename Tp> +Tp check_wait_notify(Tp val1, Tp val2) +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + Tp aa = val1; + std::atomic_ref<Tp> a(aa); + std::thread t([&] + { + cv.notify_one(); + a.wait(val1); + if (a.load() != val2) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + a.notify_one(); + t.join(); + return a.load(); +} + +template<typename Tp, + bool = std::is_integral_v<Tp> + || std::is_floating_point_v<Tp>> +struct check; + +template<typename Tp> +struct check<Tp, true> +{ + check() + { + Tp a = 0; + Tp b = 42; + VERIFY(check_wait_notify(a, b) == b); + } +}; + +template<typename Tp> +struct check<Tp, false> +{ + check(Tp b) + { + Tp a; + VERIFY(check_wait_notify(a, b) == b); + } +}; + +struct foo +{ + long a = 0; + long b = 0; + + foo& operator=(foo const&) = default; + + friend bool + operator==(foo const& rhs, foo const& lhs) + { return rhs.a == lhs.a && rhs.b == lhs.b; } +}; + +int +main () +{ + check<long>(); + check<double>(); + check<foo>({42, 48}); + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc new file mode 100644 index 00000000000..3ebf50bb9a5 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc @@ -0,0 +1,57 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <thread> +#include <mutex> +#include <condition_variable> +#include <type_traits> +#include <chrono> + +#include <testsuite_hooks.h> + +int +main () +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<bool> a(false); + std::atomic<bool> b(false); + std::thread t([&] + { + cv.notify_one(); + a.wait(false); + if (a.load()) + b.store(true); + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(true); + a.notify_one(); + t.join(); + VERIFY( b.load() ); + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc new file mode 100644 index 00000000000..1d032085752 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc @@ -0,0 +1,32 @@ +// { dg-options "-std=gnu++2a -pthread -latomic -L../../libatomic/.libs" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include "generic.h" + +int +main () +{ + check<float> f; + check<double> d; + check<long double> l; + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h new file mode 100644 index 00000000000..0da374ece87 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h @@ -0,0 +1,88 @@ +// -*- C++ -*- header. + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <thread> +#include <mutex> +#include <condition_variable> +#include <chrono> + +#include <testsuite_hooks.h> + +template<typename Tp> +Tp check_wait_notify(Tp val1, Tp val2) +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<Tp> a(val1); + std::thread t([&] + { + cv.notify_one(); + a.wait(val1); + if (a.load() != val2) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + a.notify_one(); + t.join(); + return a.load(); +} + +template<typename Tp> +Tp check_atomic_wait_notify(Tp val1, Tp val2) +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + std::atomic<Tp> a(val1); + std::thread t([&] + { + cv.notify_one(); + std::atomic_wait(&a, val1); + if (a.load() != val2) + a = val1; + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(val2); + std::atomic_notify_one(&a); + t.join(); + return a.load(); +} + +template<typename Tp> +struct check +{ + check() + { + Tp a = 0; + Tp b = 42; + VERIFY(check_wait_notify(a, b) == b); + VERIFY(check_atomic_wait_notify(a, b) == b); + } +}; diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc new file mode 100644 index 00000000000..2afd19a7d14 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc @@ -0,0 +1,56 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include "generic.h" + +int +main () +{ + // check<bool> bb; + check<char> ch; + check<signed char> sch; + check<unsigned char> uch; + check<short> s; + check<unsigned short> us; + check<int> i; + check<unsigned int> ui; + check<long> l; + check<unsigned long> ul; + check<long long> ll; + check<unsigned long long> ull; + + check<wchar_t> wch; + check<char8_t> ch8; + check<char16_t> ch16; + check<char32_t> ch32; + + check<int8_t> i8; + check<int16_t> i16; + check<int32_t> i32; + check<int64_t> i64; + + check<uint8_t> u8; + check<uint16_t> u16; + check<uint32_t> u32; + check<uint64_t> u64; + return 0; +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc new file mode 100644 index 00000000000..8531bb2e788 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc @@ -0,0 +1,59 @@ +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +// Copyright (C) 2020 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/>. + +#include <atomic> +#include <thread> +#include <mutex> +#include <condition_variable> +#include <type_traits> +#include <chrono> + +#include <testsuite_hooks.h> + +int +main () +{ + using namespace std::literals::chrono_literals; + + std::mutex m; + std::condition_variable cv; + + long aa; + long bb; + + std::atomic<long*> a(nullptr); + std::thread t([&] + { + cv.notify_one(); + a.wait(nullptr); + if (a.load() == &aa) + a.store(&bb); + }); + std::unique_lock<std::mutex> l(m); + cv.wait(l); + std::this_thread::sleep_for(100ms); + a.store(&aa); + a.notify_one(); + t.join(); + VERIFY( a.load() == &bb); + return 0; +} -- 2.25.1
Thomas Rodgers via Libstdc++ writes: > Updated patch attached, addresses some minor issues I found after > sending the original version. > > Thomas Rodgers writes: > >> This patch adds support for wait/notify_one/notify_all to std::atomic<>. >> Support for the volatile overloads will be added in a subsequent patch. >> >> * include/Makefile.am (bits_headers): Add new header. >> * include/Makefile.in: Regenerate. >> * include/bits/atomic_base.h (__atomic_base<_Itp>:wait): Define. >> (__atomic_base<_Itp>::notify_one): Likewise. >> (__atomic_base<_Itp>::notify_all): Likewise. >> (__atomic_base<_Ptp*>::wait): Likewise. >> (__atomic_base<_Ptp*>::notify_one): Likewise. >> (__atomic_base<_Ptp*>::notify_all): Likewise. >> (__atomic_impl::wait): Likewise. >> (__atomic_impl::notify_one): Likewise. >> (__atomic_impl::notify_all): Likewise. >> (__atomic_float<_Fp>::wait): Likewise. >> (__atomic_float<_Fp>::notify_one): Likewise. >> (__atomic_float<_Fp>::notify_all): Likewise. >> (__atomic_ref<_Tp>::wait): Likewise. >> (__atomic_ref<_Tp>::notify_one): Likewise. >> (__atomic_ref<_Tp>::notify_all): Likewise. >> (atomic_wait<_Tp>): Likewise. >> (atomic_wait_explicit<_Tp>): Likewise. >> (atomic_notify_one<_Tp>): Likewise. >> (atomic_notify_all<_Tp>): Likewise. >> * include/bits/atomic_wait.h: New file. >> * include/std/atomic (atomic<bool>::wait): Define. >> (atomic<bool>::wait_one): Likewise. >> (atomic<bool>::wait_all): Likewise. >> (atomic<_Tp>::wait): Likewise. >> (atomic<_Tp>::wait_one): Likewise. >> (atomic<_Tp>::wait_all): Likewise. >> (atomic<_Tp*>::wait): Likewise. >> (atomic<_Tp*>::wait_one): Likewise. >> (atomic<_Tp*>::wait_all): Likewise. >> * testsuite/29_atomic/atomic/wait_notify/atomic_refs.cc: New test. >> * testsuite/29_atomic/atomic/wait_notify/bool.cc: Likewise. >> * testsuite/29_atomic/atomic/wait_notify/integrals.cc: Likewise. >> * testsuite/29_atomic/atomic/wait_notify/floats.cc: Likewise. >> * testsuite/29_atomic/atomic/wait_notify/pointers.cc: Likewise. >> * testsuite/29_atomic/atomic/wait_notify/generic.h: New File. >> --- >> libstdc++-v3/include/Makefile.am | 1 + >> libstdc++-v3/include/Makefile.in | 2 + >> libstdc++-v3/include/bits/atomic_base.h | 178 ++++++++++- >> libstdc++-v3/include/bits/atomic_wait.h | 295 ++++++++++++++++++ >> libstdc++-v3/include/std/atomic | 61 ++++ >> .../atomic/wait_notify/atomic_refs.cc | 103 ++++++ >> .../29_atomics/atomic/wait_notify/bool.cc | 57 ++++ >> .../29_atomics/atomic/wait_notify/floats.cc | 32 ++ >> .../29_atomics/atomic/wait_notify/generic.h | 88 ++++++ >> .../atomic/wait_notify/integrals.cc | 56 ++++ >> .../29_atomics/atomic/wait_notify/pointers.cc | 59 ++++ >> 11 files changed, 931 insertions(+), 1 deletion(-) >> create mode 100644 libstdc++-v3/include/bits/atomic_wait.h >> create mode 100644 >> libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc >> create mode 100644 >> libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc >> create mode 100644 >> libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc >> create mode 100644 >> libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h >> create mode 100644 >> libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc >> create mode 100644 >> libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc >> >> diff --git a/libstdc++-v3/include/Makefile.am >> b/libstdc++-v3/include/Makefile.am >> index 80aeb3f8959..d195a721fd5 100644 >> --- a/libstdc++-v3/include/Makefile.am >> +++ b/libstdc++-v3/include/Makefile.am >> @@ -100,6 +100,7 @@ bits_headers = \ >> ${bits_srcdir}/allocated_ptr.h \ >> ${bits_srcdir}/allocator.h \ >> ${bits_srcdir}/atomic_base.h \ >> + ${bits_srcdir}/atomic_wait.h \ >> ${bits_srcdir}/atomic_futex.h \ >> ${bits_srcdir}/basic_ios.h \ >> ${bits_srcdir}/basic_ios.tcc \ >> diff --git a/libstdc++-v3/include/Makefile.in >> b/libstdc++-v3/include/Makefile.in >> index eb437ad8d8d..4faaac5fb8d 100644 >> --- a/libstdc++-v3/include/Makefile.in >> +++ b/libstdc++-v3/include/Makefile.in >> @@ -445,6 +445,7 @@ bits_headers = \ >> ${bits_srcdir}/allocated_ptr.h \ >> ${bits_srcdir}/allocator.h \ >> ${bits_srcdir}/atomic_base.h \ >> + ${bits_srcdir}/atomic_wait.h \ >> ${bits_srcdir}/atomic_futex.h \ >> ${bits_srcdir}/basic_ios.h \ >> ${bits_srcdir}/basic_ios.tcc \ >> @@ -526,6 +527,7 @@ bits_headers = \ >> ${bits_srcdir}/specfun.h \ >> ${bits_srcdir}/sstream.tcc \ >> ${bits_srcdir}/std_abs.h \ >> + ${bits_srcdir}/std_condvar.h \ >> ${bits_srcdir}/std_function.h \ >> ${bits_srcdir}/std_mutex.h \ >> ${bits_srcdir}/stl_algo.h \ >> diff --git a/libstdc++-v3/include/bits/atomic_base.h >> b/libstdc++-v3/include/bits/atomic_base.h >> index 87fe0bd6000..b4fbe2c6eb3 100644 >> --- a/libstdc++-v3/include/bits/atomic_base.h >> +++ b/libstdc++-v3/include/bits/atomic_base.h >> @@ -37,6 +37,11 @@ >> #include <bits/atomic_lockfree_defines.h> >> #include <bits/move.h> >> >> +#if __cplusplus > 201703L >> +#include <bits/atomic_wait.h> >> +#include <iostream> >> +#endif >> + >> #ifndef _GLIBCXX_ALWAYS_INLINE >> #define _GLIBCXX_ALWAYS_INLINE inline __attribute__((__always_inline__)) >> #endif >> @@ -134,7 +139,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> return __ret; >> } >> >> - >> // Base types for atomics. >> template<typename _IntTp> >> struct __atomic_base; >> @@ -542,6 +546,35 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> __cmpexch_failure_order(__m)); >> } >> >> +#if __cplusplus > 201703L >> + _GLIBCXX_ALWAYS_INLINE void >> + wait(__int_type __old, memory_order __m = memory_order_seq_cst) const >> noexcept >> + { >> + __detail::__atomic_wait(&_M_i, __old, [__m, this](__int_type __o) >> + { >> + return this->load(__m) == __o; >> + }); >> + } >> + >> + // TODO add const volatile overload >> + >> + _GLIBCXX_ALWAYS_INLINE void >> + notify_one() const noexcept >> + { >> + __detail::__atomic_notify(&_M_i, false); >> + } >> + >> + // TODO add const volatile overload >> + >> + _GLIBCXX_ALWAYS_INLINE void >> + notify_all() const noexcept >> + { >> + __detail::__atomic_notify(&_M_i, true); >> + } >> + >> + // TODO add const volatile overload >> +#endif // C++2a >> + >> _GLIBCXX_ALWAYS_INLINE __int_type >> fetch_add(__int_type __i, >> memory_order __m = memory_order_seq_cst) noexcept >> @@ -803,6 +836,35 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> int(__m1), int(__m2)); >> } >> >> +#if __cplusplus > 201703L >> + _GLIBCXX_ALWAYS_INLINE void >> + wait(__pointer_type __old, memory_order __m = memory_order_seq_cst) >> noexcept >> + { >> + __detail::__atomic_wait(&_M_p, __old, [__m, this](__pointer_type __o) >> + { >> + return this->load(__m) == __o; >> + }); >> + } >> + >> + // TODO add const volatile overload >> + >> + _GLIBCXX_ALWAYS_INLINE void >> + notify_one() const noexcept >> + { >> + __detail::__atomic_notify(&_M_p, false); >> + } >> + >> + // TODO add const volatile overload >> + >> + _GLIBCXX_ALWAYS_INLINE void >> + notify_all() const noexcept >> + { >> + __detail::__atomic_notify(&_M_p, true); >> + } >> + >> + // TODO add const volatile overload >> +#endif // C++2a >> + >> _GLIBCXX_ALWAYS_INLINE __pointer_type >> fetch_add(ptrdiff_t __d, >> memory_order __m = memory_order_seq_cst) noexcept >> @@ -891,6 +953,39 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> int(__success), int(__failure)); >> } >> >> +#if __cplusplus > 201703L >> + template<typename _Tp> >> + _GLIBCXX_ALWAYS_INLINE void >> + wait(const _Tp* __ptr, _Val<_Tp> __old, memory_order __m = >> memory_order_seq_cst) noexcept >> + { >> + __detail::__atomic_wait_ptr(__ptr, std::__addressof(__old), >> + [=](_Tp* __o) >> + { >> + return load(__ptr, __m) == *__o; >> + }); >> + } >> + >> + // TODO add const volatile overload >> + >> + template<typename _Tp> >> + _GLIBCXX_ALWAYS_INLINE void >> + notify_one(const _Tp* __ptr) noexcept >> + { >> + __detail::__atomic_notify(__ptr, false); >> + } >> + >> + // TODO add const volatile overload >> + >> + template<typename _Tp> >> + _GLIBCXX_ALWAYS_INLINE void >> + notify_all(const _Tp* __ptr) noexcept >> + { >> + __detail::__atomic_notify(__ptr, true); >> + } >> + >> + // TODO add const volatile overload >> +#endif // C++2a >> + >> template<typename _Tp> >> _GLIBCXX_ALWAYS_INLINE _Tp >> fetch_add(_Tp* __ptr, _Diff<_Tp> __i, memory_order __m) noexcept >> @@ -1144,6 +1239,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> __cmpexch_failure_order(__order)); >> } >> >> + _GLIBCXX_ALWAYS_INLINE void >> + wait(_Fp __old, memory_order __m = memory_order_seq_cst) const >> noexcept >> + { __atomic_impl::wait(&_M_fp, __old, __m); } >> + >> + // TODO add const volatile overload >> + >> + _GLIBCXX_ALWAYS_INLINE void >> + notify_one() const noexcept >> + { __atomic_impl::notify_one(&_M_fp); } >> + >> + // TODO add const volatile overload >> + >> + _GLIBCXX_ALWAYS_INLINE void >> + notify_all() const noexcept >> + { __atomic_impl::notify_all(&_M_fp); } >> + >> + // TODO add const volatile overload >> value_type >> fetch_add(value_type __i, >> memory_order __m = memory_order_seq_cst) noexcept >> @@ -1281,6 +1393,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> __cmpexch_failure_order(__order)); >> } >> >> + _GLIBCXX_ALWAYS_INLINE void >> + wait(_Tp __old, memory_order __m = memory_order_seq_cst) const >> noexcept >> + { __atomic_impl::wait(_M_ptr, __old, __m); } >> + >> + // TODO add const volatile overload >> + >> + _GLIBCXX_ALWAYS_INLINE void >> + notify_one() const noexcept >> + { __atomic_impl::notify_one(_M_ptr); } >> + >> + // TODO add const volatile overload >> + >> + _GLIBCXX_ALWAYS_INLINE void >> + notify_all() const noexcept >> + { __atomic_impl::notify_all(_M_ptr); } >> + >> private: >> _Tp* _M_ptr; >> }; >> @@ -1376,6 +1504,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> __cmpexch_failure_order(__order)); >> } >> >> + _GLIBCXX_ALWAYS_INLINE void >> + wait(_Tp __old, memory_order __m = memory_order_seq_cst) const >> noexcept >> + { __atomic_impl::wait(_M_ptr, __old, __m); } >> + >> + // TODO add const volatile overload >> + >> + _GLIBCXX_ALWAYS_INLINE void >> + notify_one() const noexcept >> + { __atomic_impl::notify_one(_M_ptr); } >> + >> + // TODO add const volatile overload >> + >> + _GLIBCXX_ALWAYS_INLINE void >> + notify_all() const noexcept >> + { __atomic_impl::notify_all(_M_ptr); } >> + >> value_type >> fetch_add(value_type __i, >> memory_order __m = memory_order_seq_cst) const noexcept >> @@ -1531,6 +1675,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> __cmpexch_failure_order(__order)); >> } >> >> + _GLIBCXX_ALWAYS_INLINE void >> + wait(_Fp __old, memory_order __m = memory_order_seq_cst) const >> noexcept >> + { __atomic_impl::wait(_M_ptr, __old, __m); } >> + >> + // TODO add const volatile overload >> + >> + _GLIBCXX_ALWAYS_INLINE void >> + notify_one() const noexcept >> + { __atomic_impl::notify_one(_M_ptr); } >> + >> + // TODO add const volatile overload >> + >> + _GLIBCXX_ALWAYS_INLINE void >> + notify_all() const noexcept >> + { __atomic_impl::notify_all(_M_ptr); } >> + >> value_type >> fetch_add(value_type __i, >> memory_order __m = memory_order_seq_cst) const noexcept >> @@ -1640,6 +1800,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> __cmpexch_failure_order(__order)); >> } >> >> + _GLIBCXX_ALWAYS_INLINE void >> + wait(_Tp __old, memory_order __m = memory_order_seq_cst) const >> noexcept >> + { __atomic_impl::wait(_M_ptr, __old, __m); } >> + >> + // TODO add const volatile overload >> + >> + _GLIBCXX_ALWAYS_INLINE void >> + notify_one() const noexcept >> + { __atomic_impl::notify_one(_M_ptr); } >> + >> + // TODO add const volatile overload >> + >> + _GLIBCXX_ALWAYS_INLINE void >> + notify_all() const noexcept >> + { __atomic_impl::notify_all(_M_ptr); } >> + >> _GLIBCXX_ALWAYS_INLINE value_type >> fetch_add(difference_type __d, >> memory_order __m = memory_order_seq_cst) const noexcept >> diff --git a/libstdc++-v3/include/bits/atomic_wait.h >> b/libstdc++-v3/include/bits/atomic_wait.h >> new file mode 100644 >> index 00000000000..fa2650afc39 >> --- /dev/null >> +++ b/libstdc++-v3/include/bits/atomic_wait.h >> @@ -0,0 +1,295 @@ >> +// -*- C++ -*- header. >> + >> +// Copyright (C) 2020 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 >> +// <http://www.gnu.org/licenses/>. >> + >> +/** @file bits/atomic_wait.h >> + * This is an internal header file, included by other library headers. >> + * Do not attempt to use it directly. @headername{atomic} >> + */ >> + >> +#ifndef _GLIBCXX_ATOMIC_WAIT_H >> +#define _GLIBCXX_ATOMIC_WAIT_H 1 >> + >> +#pragma GCC system_header >> + >> +#include <bits/c++config.h> >> +#include <bits/functional_hash.h> >> +#include <bits/gthr.h> >> +#include <bits/std_mutex.h> >> +#include <bits/unique_lock.h> >> +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >> +#include <climits> >> +#include <unistd.h> >> +#include <syscall.h> >> +#endif >> + >> +#define _GLIBCXX_SPIN_COUNT_1 16 >> +#define _GLIBCXX_SPIN_COUNT_2 12 >> + >> +// TODO get this from Autoconf >> +#define _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE 1 >> + >> +namespace std _GLIBCXX_VISIBILITY(default) >> +{ >> +_GLIBCXX_BEGIN_NAMESPACE_VERSION >> + namespace __detail >> + { >> + using __platform_wait_t = int; >> + >> + template<class _Tp> >> + struct __platform_wait_uses_type >> + { >> +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >> + enum { __value = std::is_same<typename std::remove_cv<_Tp>::type, >> + __platform_wait_t>::value }; >> +#else >> + enum { __value = std::false_type::value }; >> +#endif >> + }; >> + >> +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX >> + enum >> + { >> +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE >> + __futex_private_flag = 128, >> +#else >> + __futex_private_flag = 0, >> +#endif >> + __futex_wait = 0, >> + __futex_wake = 1, >> + __futex_wait_private = __futex_wait | __futex_private_flag, >> + __futex_wake_private = __futex_wake | __futex_private_flag, >> + }; >> + >> + void >> + __platform_wait(__platform_wait_t* __addr, __platform_wait_t __val) >> noexcept >> + { >> + auto __e = syscall (SYS_futex, __addr, __futex_wait_private, __val, >> nullptr); >> + if (__e && (errno != EINTR || errno != EAGAIN)) >> + std::terminate(); >> + } >> + >> + void >> + __platform_notify(__platform_wait_t* __addr, bool __all) noexcept >> + { >> + syscall (SYS_futex, __addr, __futex_wake_private, __all ? INT_MAX : >> 1); >> + } >> +#endif >> + >> + struct alignas(64) __waiter >> + { >> + bool _M_signaled = false; >> + void* _M_t; >> + __waiter* _M_prev = nullptr; >> + __waiter* _M_next = nullptr; >> + >> + __waiter(void* __t) noexcept >> + : _M_t(__t) >> + { } >> + }; >> + >> + struct alignas(64) __waiters >> + { >> + int32_t alignas(64) _M_ver = 0; >> + int32_t alignas(64) _M_wait = 0; >> + >> + // TODO make this used only where we don't have futexes >> + using __lock_t = std::unique_lock<std::mutex>; >> + mutable __lock_t::mutex_type _M_mtx; >> + >> +#ifdef __GTHREAD_COND_INIT >> + mutable __gthread_cond_t _M_cv = __GTHREAD_COND_INIT; >> + __waiters() noexcept = default; >> +#else >> + mutable __gthread_cond_t _M_cv; >> + __waiters() noexcept >> + { >> + __GTHREAD_COND_INIT_FUNCTION(&_M_cond); >> + } >> +#endif >> + >> + int32_t >> + _M_enter_wait() noexcept >> + { >> + int32_t __res; >> + __atomic_load(&_M_ver, &__res, __ATOMIC_ACQUIRE); >> + __atomic_fetch_add(&_M_wait, 1, __ATOMIC_ACQ_REL); >> + return __res; >> + } >> + >> + void >> + _M_leave_wait() noexcept >> + { >> + __atomic_fetch_sub(&_M_wait, 1, __ATOMIC_ACQ_REL); >> + } >> + >> + void >> + _M_do_wait(int32_t __version) const noexcept >> + { >> + int32_t __cur = 0; >> + while (__cur <= __version) >> + { >> + __waiters::__lock_t __l(_M_mtx); >> + auto __e = __gthread_cond_wait(&_M_cv, >> __l.mutex()->native_handle()); >> + if (__e) >> + std::terminate(); >> + int32_t __last = __cur; >> + __atomic_load(&_M_ver, &__cur, __ATOMIC_ACQUIRE); >> + // protect if version overflows >> + if (__cur < __last) >> + break; // break the loop if version overflows >> + } >> + } >> + >> + int32_t >> + _M_waiting() const noexcept >> + { >> + int32_t __res; >> + __atomic_load(&_M_wait, &__res, __ATOMIC_ACQUIRE); >> + return __res; >> + } >> + >> + void >> + _M_notify(bool __all) noexcept >> + { >> + __atomic_fetch_add(&_M_ver, 1, __ATOMIC_ACQ_REL); >> + auto __e = __gthread_cond_broadcast(&_M_cv); >> + if (__e) >> + __throw_system_error(__e); >> + } >> + >> + static __waiters& _S_for(void* __t) { >> + const unsigned char __mask = 0xf; >> + static __waiters __w[__mask + 1]; >> + >> + auto __key = _Hash_impl::hash(__t) & __mask; >> + return __w[__key]; >> + } >> + }; >> + >> + void >> + __thread_relax() noexcept >> + { >> +#if defined __i386__ || defined __x86_64__ >> + __builtin_ia32_pause(); >> +#elif defined _GLIBCXX_USE_SCHED_YIELD >> + __gthread_yield(); >> +#endif >> + } >> + >> + void >> + __thread_yield() noexcept >> + { >> +#if defined _GLIBCXX_USE_SCHED_YIELD >> + __gthread_yield(); >> +#endif >> + } >> + >> + template<class _Tp, class _Pred> >> + void >> + __atomic_wait(const _Tp* __addr, _Tp __old, _Pred __pred) noexcept >> + { >> + for (auto __i = 0; __i < _GLIBCXX_SPIN_COUNT_1; ++__i) >> + { >> + if (!__pred(__old)) >> + return; >> + >> + if (__i < _GLIBCXX_SPIN_COUNT_2) >> + __thread_relax(); >> + else >> + __thread_yield(); >> + } >> + >> + auto& __w = __waiters::_S_for((void*)__addr); >> + auto __version = __w._M_enter_wait(); >> + while (auto __res = __pred(__old)) >> + { >> + if constexpr (__platform_wait_uses_type<_Tp>::__value) >> + { >> + __platform_wait((__platform_wait_t*)(void*) __addr, __old); >> + } >> + else >> + { >> + // TODO support timed backoff when this can be moved into the >> lib >> + __w._M_do_wait(__version); >> + } >> + } >> + __w._M_leave_wait(); >> + } >> + >> + template<class _Tp, class _Pred> >> + void >> + __atomic_wait_ptr(const _Tp* __addr, _Tp* __old, _Pred __pred) >> noexcept >> + { >> + for (auto __i = 0; __i < _GLIBCXX_SPIN_COUNT_1; ++__i) >> + { >> + if (!__pred(__old)) >> + return; >> + >> + if (__i < _GLIBCXX_SPIN_COUNT_2) >> + __thread_relax(); >> + else >> + __thread_yield(); >> + } >> + >> + auto& __w = __waiters::_S_for((void*)__addr); >> + auto __version = __w._M_enter_wait(); >> + while (auto __res = __pred(__old)) >> + { >> + if constexpr (__platform_wait_uses_type<_Tp>::__value) >> + { >> + __platform_wait((__platform_wait_t*)(void*) __addr, __old); >> + } >> + else >> + { >> + // TODO support timed backoff when this can be moved into the >> lib >> + __w._M_do_wait(__version); >> + } >> + } >> + __w._M_leave_wait(); >> + } >> + >> + template<class _Tp> >> + void >> + __atomic_notify(const _Tp* __addr, bool __all) noexcept >> + { >> + auto& __w = __waiters::_S_for((void*)__addr); >> + if (!__w._M_waiting()) >> + return; >> + >> + if constexpr (__platform_wait_uses_type<_Tp>::__value) >> + { >> + __platform_notify((__platform_wait_t*)(void*) __addr, __all); >> + } >> + else >> + { >> + __w._M_notify(__all); >> + } >> + } >> + } // namespace __detail >> + >> +_GLIBCXX_END_NAMESPACE_VERSION >> +} // namespace std >> + >> +#endif >> + >> diff --git a/libstdc++-v3/include/std/atomic >> b/libstdc++-v3/include/std/atomic >> index 40f23bdfc96..cb2308b0cdf 100644 >> --- a/libstdc++-v3/include/std/atomic >> +++ b/libstdc++-v3/include/std/atomic >> @@ -163,6 +163,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> compare_exchange_strong(bool& __i1, bool __i2, >> memory_order __m = memory_order_seq_cst) volatile noexcept >> { return _M_base.compare_exchange_strong(__i1, __i2, __m); } >> + >> +#if __cplusplus > 201703L >> + void wait(bool __old, memory_order __m = memory_order_seq_cst) const >> noexcept >> + { _M_base.wait(__old, __m); } >> + >> + // TODO add const volatile overload >> + >> + void notify_one() const noexcept >> + { _M_base.notify_one(); } >> + >> + void notify_all() const noexcept >> + { _M_base.notify_all(); } >> +#endif >> }; >> >> #if __cplusplus <= 201703L >> @@ -352,6 +365,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> memory_order __m = memory_order_seq_cst) volatile noexcept >> { return compare_exchange_strong(__e, __i, __m, >> __cmpexch_failure_order(__m)); } >> +#if __cplusplus > 201703L >> + void wait(_Tp __old, memory_order __m = memory_order_seq_cst) noexcept >> + { _M_i.wait(__old, __m); } >> + >> + // TODO add const volatile overload >> + >> + void notify_one() const noexcept >> + { _M_i.notify_one(); } >> + >> + void notify_all() const noexcept >> + { _M_i.notify_all(); } >> +#endif >> + >> }; >> #undef _GLIBCXX20_INIT >> >> @@ -590,6 +616,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> __cmpexch_failure_order(__m)); >> } >> >> +#if __cplusplus > 201703L >> + void wait(__pointer_type __old, memory_order __m = >> memory_order_seq_cst) noexcept >> + { _M_b.wait(__old, __m); } >> + >> + // TODO add const volatile overload >> + >> + void notify_one() const noexcept >> + { _M_b.notify_one(); } >> + >> + void notify_all() const noexcept >> + { _M_b.notify_all(); } >> +#endif >> __pointer_type >> fetch_add(ptrdiff_t __d, >> memory_order __m = memory_order_seq_cst) noexcept >> @@ -1342,6 +1380,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION >> memory_order_seq_cst); >> } >> >> + >> +#if __cplusplus > 201703L >> + template<typename _Tp> >> + inline void atomic_wait(const atomic<_Tp>* __a, >> + typename std::atomic<_Tp>::value_type __old) >> noexcept >> + { __a->wait(__old); } >> + >> + template<typename _Tp> >> + inline void atomic_wait_explicit(const atomic<_Tp>* __a, >> + typename std::atomic<_Tp>::value_type >> __old, >> + std::memory_order __m) noexcept >> + { __a->wait(__old, __m); } >> + >> + template<typename _Tp> >> + inline void atomic_notify_one(atomic<_Tp>* __a) noexcept >> + { __a->notify_one(); } >> + >> + template<typename _Tp> >> + inline void atomic_notify_all(atomic<_Tp>* __a) noexcept >> + { __a->notify_all(); } >> + >> +#endif // C++2a >> + >> // Function templates for atomic_integral and atomic_pointer operations >> only. >> // Some operations (and, or, xor) are only available for atomic integrals, >> // which is implemented by taking a parameter of type >> __atomic_base<_ITp>*. >> diff --git >> a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc >> b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc >> new file mode 100644 >> index 00000000000..1d8d13621cc >> --- /dev/null >> +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc >> @@ -0,0 +1,103 @@ >> +// { dg-options "-std=gnu++2a -pthread -latomic -L../../libatomic/.libs" } >> +// { dg-do run { target c++2a } } >> +// { dg-require-effective-target pthread } >> +// { dg-require-gthreads "" } >> + >> +// Copyright (C) 2020 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/>. >> + >> +#include <atomic> >> +#include <thread> >> +#include <mutex> >> +#include <condition_variable> >> +#include <chrono> >> +#include <type_traits> >> + >> +#include <testsuite_hooks.h> >> + >> +template<typename Tp> >> +Tp check_wait_notify(Tp val1, Tp val2) >> +{ >> + using namespace std::literals::chrono_literals; >> + >> + std::mutex m; >> + std::condition_variable cv; >> + >> + Tp aa = val1; >> + std::atomic_ref<Tp> a(aa); >> + std::thread t([&] >> + { >> + cv.notify_one(); >> + a.wait(val1); >> + if (a.load() != val2) >> + a = val1; >> + }); >> + std::unique_lock<std::mutex> l(m); >> + cv.wait(l); >> + std::this_thread::sleep_for(100ms); >> + a.store(val2); >> + a.notify_one(); >> + t.join(); >> + return a.load(); >> +} >> + >> +template<typename Tp, >> + bool = std::is_integral_v<Tp> >> + || std::is_floating_point_v<Tp>> >> +struct check; >> + >> +template<typename Tp> >> +struct check<Tp, true> >> +{ >> + check() >> + { >> + Tp a = 0; >> + Tp b = 42; >> + VERIFY(check_wait_notify(a, b) == b); >> + } >> +}; >> + >> +template<typename Tp> >> +struct check<Tp, false> >> +{ >> + check(Tp b) >> + { >> + Tp a; >> + VERIFY(check_wait_notify(a, b) == b); >> + } >> +}; >> + >> +struct foo >> +{ >> + long a = 0; >> + long b = 0; >> + >> + foo& operator=(foo const&) = default; >> + >> + friend bool >> + operator==(foo const& rhs, foo const& lhs) >> + { return rhs.a == lhs.a && rhs.b == lhs.b; } >> +}; >> + >> +int >> +main () >> +{ >> + check<long>(); >> + check<double>(); >> + check<foo>({42, 48}); >> + return 0; >> +} >> diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc >> b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc >> new file mode 100644 >> index 00000000000..98258686945 >> --- /dev/null >> +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc >> @@ -0,0 +1,57 @@ >> +// { dg-options "-std=gnu++2a -pthread" } >> +// { dg-do run { target c++2a } } >> +// { dg-require-effective-target pthread } >> +// { dg-require-gthreads "" } >> + >> +// Copyright (C) 2020 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/>. >> + >> +#include <atomic> >> +#include <thread> >> +#include <mutex> >> +#include <condition_variable> >> +#include <type_traits> >> +#include <chrono> >> + >> +#include <testsuite_hooks.h> >> + >> +int >> +main () >> +{ >> + using namespace std::literals::chrono_literals; >> + >> + std::mutex m; >> + std::condition_variable cv; >> + >> + std::atomic<bool> a(false); >> + std::atomic<bool> b(false); >> + std::thread t([&] >> + { >> + cv.notify_one(); >> + a.wait(false); >> + if (a.load()) >> + b.store(true); >> + }); >> + std::unique_lock<std::mutex> l(m); >> + cv.wait(l); >> + std::this_thread::sleep_for(100ms); >> + a.store(true); >> + a.notify_one(); >> + t.join(); >> + VERIFY( b.load() ); >> + return 0; >> +} >> diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc >> b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc >> new file mode 100644 >> index 00000000000..1d032085752 >> --- /dev/null >> +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc >> @@ -0,0 +1,32 @@ >> +// { dg-options "-std=gnu++2a -pthread -latomic -L../../libatomic/.libs" } >> +// { dg-do run { target c++2a } } >> +// { dg-require-effective-target pthread } >> +// { dg-require-gthreads "" } >> + >> +// Copyright (C) 2020 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/>. >> + >> +#include "generic.h" >> + >> +int >> +main () >> +{ >> + check<float> f; >> + check<double> d; >> + check<long double> l; >> + return 0; >> +} >> diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h >> b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h >> new file mode 100644 >> index 00000000000..e6eeebf4ab2 >> --- /dev/null >> +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h >> @@ -0,0 +1,88 @@ >> +// -*- C++ -*- header. >> + >> +// Copyright (C) 2020 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/>. >> + >> +#include <atomic> >> +#include <thread> >> +#include <mutex> >> +#include <condition_variable> >> +#include <chrono> >> + >> +#include <testsuite_hooks.h> >> + >> +template<typename Tp> >> +Tp check_wait_notify(Tp val1, Tp val2) >> +{ >> + using namespace std::literals::chrono_literals; >> + >> + std::mutex m; >> + std::condition_variable cv; >> + >> + std::atomic<Tp> a(val1); >> + std::thread t([&] >> + { >> + cv.notify_one(); >> + a.wait(val1); >> + if (a.load() != val2) >> + a = val1; >> + }); >> + std::unique_lock<std::mutex> l(m); >> + cv.wait(l); >> + std::this_thread::sleep_for(100ms); >> + a.store(val2); >> + a.notify_one(); >> + t.join(); >> + return a.load(); >> +} >> + >> +template<typename Tp> >> +Tp check_atomic_wait_notify(Tp val1, Tp val2) >> +{ >> + using namespace std::literals::chrono_literals; >> + >> + std::mutex m; >> + std::condition_variable cv; >> + >> + std::atomic<Tp> a(val1); >> + std::thread t([&] >> + { >> + cv.notify_one(); >> + std::atomic_wait(&a, val1); >> + if (a.load() != val2) >> + a = val1; >> + }); >> + std::unique_lock<std::mutex> l(m); >> + cv.wait(l); >> + std::this_thread::sleep_for(100ms); >> + a.store(val2); >> + std::atomic_notify_one(&a); >> + t.join(); >> + return a.load(); >> +} >> + >> +template<typename Tp> >> +struct check >> +{ >> + check() >> + { >> + Tp a = 0; >> + Tp b = 42; >> + VERIFY(check_wait_notify(a, b) == b); >> + VERIFY(check_atomic_wait_notify(a, b) == b); >> + } >> +}; >> diff --git >> a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc >> b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc >> new file mode 100644 >> index 00000000000..2afd19a7d14 >> --- /dev/null >> +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc >> @@ -0,0 +1,56 @@ >> +// { dg-options "-std=gnu++2a -pthread" } >> +// { dg-do run { target c++2a } } >> +// { dg-require-effective-target pthread } >> +// { dg-require-gthreads "" } >> + >> +// Copyright (C) 2020 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/>. >> + >> +#include "generic.h" >> + >> +int >> +main () >> +{ >> + // check<bool> bb; >> + check<char> ch; >> + check<signed char> sch; >> + check<unsigned char> uch; >> + check<short> s; >> + check<unsigned short> us; >> + check<int> i; >> + check<unsigned int> ui; >> + check<long> l; >> + check<unsigned long> ul; >> + check<long long> ll; >> + check<unsigned long long> ull; >> + >> + check<wchar_t> wch; >> + check<char8_t> ch8; >> + check<char16_t> ch16; >> + check<char32_t> ch32; >> + >> + check<int8_t> i8; >> + check<int16_t> i16; >> + check<int32_t> i32; >> + check<int64_t> i64; >> + >> + check<uint8_t> u8; >> + check<uint16_t> u16; >> + check<uint32_t> u32; >> + check<uint64_t> u64; >> + return 0; >> +} >> diff --git >> a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc >> b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc >> new file mode 100644 >> index 00000000000..fe0b8de9f3f >> --- /dev/null >> +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc >> @@ -0,0 +1,59 @@ >> +// { dg-options "-std=gnu++2a -pthread" } >> +// { dg-do run { target c++2a } } >> +// { dg-require-effective-target pthread } >> +// { dg-require-gthreads "" } >> + >> +// Copyright (C) 2020 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/>. >> + >> +#include <atomic> >> +#include <thread> >> +#include <mutex> >> +#include <condition_variable> >> +#include <type_traits> >> +#include <chrono> >> + >> +#include <testsuite_hooks.h> >> + >> +int >> +main () >> +{ >> + using namespace std::literals::chrono_literals; >> + >> + std::mutex m; >> + std::condition_variable cv; >> + >> + long aa; >> + long bb; >> + >> + std::atomic<long*> a(nullptr); >> + std::thread t([&] >> + { >> + cv.notify_one(); >> + a.wait(nullptr); >> + if (a.load() == &aa) >> + a.store(&bb); >> + }); >> + std::unique_lock<std::mutex> l(m); >> + cv.wait(l); >> + std::this_thread::sleep_for(100ms); >> + a.store(&aa); >> + a.notify_one(); >> + t.join(); >> + VERIFY( a.load() == &bb); >> + return 0; >> +}