[PATCH] Add C++2a wait/notify_one/notify_all support to std::atomic<>

Thomas Rodgers trodgers@redhat.com
Tue Mar 17 20:25:14 GMT 2020


Updated patch attached, addresses some minor issues I found after
sending the original version.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0001-Add-C-2a-wait-notify_one-notify_all-support-to-std-a.patch
Type: text/x-patch
Size: 34760 bytes
Desc: le patch
URL: <http://gcc.gnu.org/pipermail/libstdc++/attachments/20200317/d33e05c6/attachment-0001.bin>
-------------- next part --------------

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;
> +}



More information about the Libstdc++ mailing list