[PATCH] Add C++2a synchronization support

Thomas Rodgers rodgert@appliantology.com
Sun May 24 17:41:36 GMT 2020


The previous patch had a Makefile.in included, should be a property filtered patch attached now.

-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0001-Add-C-2a-synchronization-support_f.patch
Type: application/octet-stream
Size: 82762 bytes
Desc: not available
URL: <https://gcc.gnu.org/pipermail/libstdc++/attachments/20200524/d1d212dc/attachment-0001.obj>
-------------- next part --------------

> On May 23, 2020, at 3:52 PM, Thomas Rodgers <rodgert@appliantology.com> wrote:
> 
> Updated patch.
> 
> <0001-Add-C-2a-synchronization-support.patch>
> 
>> On May 11, 2020, at 1:59 PM, Thomas Rodgers via Libstdc++ <libstdc++@gcc.gnu.org> wrote:
>> 
>> I *think* I have addressed everything in the attached patch.
>> <0001-Add-C-2a-synchronization-support.patch>
>> Jonathan Wakely writes:
>> 
>>> On 09/05/20 17:01 -0700, Thomas Rodgers via Libstdc++ wrote:
>>>> * Note, this patch supersedes my previous atomic wait and semaphore
>>>> patches.
>>>> 
>>>> Add support for -
>>>>      atomic wait/notify_one/notify_all
>>>>      counting_semaphore
>>>>      binary_semaphore
>>>>      latch
>>>> 
>>>>      * include/Makefile.am (bits_headers): Add new header.
>>>>      * include/Makefile.in: Regenerate.
>>>>      * include/bits/atomic_base.h (__atomic_base<_Itp>:wait): Define.
>>> 
>>> Should be two colons before wait.
>>> 
>>>> diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in
>>>> index eb437ad8d8d..e73ff8b3e64 100644
>>>> --- a/libstdc++-v3/include/Makefile.in
>>>> +++ b/libstdc++-v3/include/Makefile.in
>>> 
>>> Generated files don't need to be in the patch.
>>> 
>>>> diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h
>>>> index 87fe0bd6000..b2cec0f1722 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>
>>> 
>>> <iostream> shouldn't be here (it adds runtime cost, as well as
>>> compile-time).
>>> 
>>>> @@ -542,6 +546,30 @@ _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
>>> 
>>> Please format everything to <= 80 columns (ideally < 80).
>>> 
>>>> +      wait(__pointer_type __old, memory_order __m = memory_order_seq_cst) noexcept
>>>> +      {
>>>> +	__atomic_wait(&_M_p, __old,
>>> 
>>> This should be qualified to prevent ADL.
>>> 
>>>> +		      [__m, this, __old]()
>>>> +		      { return this->load(__m) != __old; });
>>>> +      }
>>>> +
>>>> +      // TODO add const volatile overload
>>>> +
>>>> +      _GLIBCXX_ALWAYS_INLINE void
>>>> +      notify_one() const noexcept
>>>> +      { __atomic_notify(&_M_p, false); }
>>> 
>>> Qualify to prevent ADL here too, and all similar calls.
>>> 
>>>> +#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
>>>> +      {
>>>> +	__atomic_wait(__ptr, *std::__addressof(__old),
>>> 
>>> Can't this just be __old instead of *std::__addressof(__old) ?
>>> 
>>>> +		      [=]()
>>>> +		      { return load(__ptr, __m) == *std::__addressof(__old); });
>>> 
>>> Same here?
>>> 
>>>> diff --git a/libstdc++-v3/include/bits/atomic_timed_wait.h b/libstdc++-v3/include/bits/atomic_timed_wait.h
>>>> new file mode 100644
>>>> index 00000000000..10f0fe50ed9
>>>> --- /dev/null
>>>> +++ b/libstdc++-v3/include/bits/atomic_timed_wait.h
>>>> @@ -0,0 +1,270 @@
>>>> +// -*- 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_timed_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_TIMED_WAIT_H
>>>> +#define _GLIBCXX_ATOMIC_TIMED_WAIT_H 1
>>>> +
>>>> +#pragma GCC system_header
>>>> +
>>>> +#include <bits/c++config.h>
>>>> +#include <bits/functional_hash.h>
>>>> +#include <bits/atomic_wait.h>
>>>> +
>>>> +#include <chrono>
>>>> +
>>>> +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
>>>> +#include <sys/time.h>
>>>> +#endif
>>>> +
>>>> +namespace std _GLIBCXX_VISIBILITY(default)
>>>> +{
>>>> +  _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>> +  enum class __atomic_wait_status { __no_timeout, __timeout };
>>> 
>>> Blank line before and after this enum definition please.
>>> 
>>>> +  namespace __detail
>>>> +  {
>>>> +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
>>>> +    enum
>>>> +    {
>>>> +      __futex_wait_bitset_private = __futex_wait_bitset | __futex_private_flag,
>>>> +      __futex_wake_bitset_private = __futex_wake_bitset | __futex_private_flag,
>>>> +      __futex_bitset_match_any = 0xffffffff
>>>> +    };
>>>> +
>>>> +    using __platform_wait_clock_t = chrono::steady_clock;
>>> 
>>> Blank line after this using-decl please.
>>> 
>>>> +    template<typename _Duration>
>>>> +      __atomic_wait_status
>>>> +      __platform_wait_until_impl(__platform_wait_t* __addr, __platform_wait_t __val,
>>>> +				 const chrono::time_point<__platform_wait_clock_t, _Duration>& __atime) noexcept
>>>> +      {
>>>> +	auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
>>>> +	auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
>>>> +
>>> 
>>> Eventually we'll want to move the rest of this function (which doesn't
>>> depend on the template argument) into the compiled library, but it's
>>> better to be header-only for now.
>>> 
>>>> +	struct timespec __rt =
>>>> +	{
>>>> +	  static_cast<std::time_t>(__s.time_since_epoch().count()),
>>>> +	  static_cast<long>(__ns.count())
>>>> +	};
>>>> +
>>>> +	auto __e = syscall (SYS_futex, __addr, __futex_wait_bitset_private, __val, &__rt,
>>>> +			    nullptr, __futex_bitset_match_any);
>>>> +	if (__e && !(errno == EINTR || errno == EAGAIN || errno == ETIMEDOUT))
>>>> +	    std::terminate();
>>>> +	return (__platform_wait_clock_t::now() < __atime)
>>>> +	       ? __atomic_wait_status::__no_timeout : __atomic_wait_status::__timeout;
>>>> +      }
>>>> +
>>>> +    template<typename _Clock, typename _Duration>
>>>> +      __atomic_wait_status
>>>> +      __platform_wait_until(__platform_wait_t* __addr, __platform_wait_t __val,
>>>> +			    const chrono::time_point<_Clock, _Duration>& __atime)
>>>> +      {
>>>> +	if constexpr (std::is_same<__platform_wait_clock_t, _Clock>::value)
>>> 
>>> This is C++20 so you can use is_same_v here, which uses the intrinsic
>>> directly and avoids instantiating the is_same class template.
>>> 
>>>> +	  {
>>>> +	    return __platform_wait_until_impl(__addr, __val, __atime);
>>>> +	  }
>>>> +	else
>>>> +	  {
>>>> +	    const typename _Clock::time_point __c_entry = _Clock::now();
>>>> +	    const __platform_wait_clock_t::time_point __s_entry =
>>>> +		    __platform_wait_clock_t::now();
>>>> +	    const auto __delta = __atime - __c_entry;
>>>> +	    const auto __s_atime = __s_entry + __delta;
>>>> +	    if (__platform_wait_until_impl(__addr, __val, __s_atime) == __atomic_wait_status::__no_timeout)
>>>> +	      return __atomic_wait_status::__no_timeout;
>>>> +
>>>> +	    // We got a timeout when measured against __clock_t but
>>>> +	    // we need to check against the caller-supplied clock
>>>> +	    // to tell whether we should return a timeout.
>>>> +	    if (_Clock::now() < __atime)
>>>> +	      return __atomic_wait_status::__no_timeout;
>>>> +	    return __atomic_wait_status::__timeout;
>>>> +	  }
>>>> +      }
>>>> +#endif
>>>> +
>>>> +#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT
>>>> +    template<typename _Duration>
>>>> +      __atomic_wait_status
>>>> +      __cond_wait_until_impl(__gthread_cond_t* __cv, std::unique_lock<std::mutex>& __lock,
>>>> +			     const chrono::time_point<std::chrono::steady_clock, _Duration>& __atime)
>>> 
>>> The std:: qualification here isn't needed (and doesn't help with
>>> keeping the line below 80 cols).
>>> 
>>>> +      {
>>>> +	auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
>>>> +	auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
>>>> +
>>>> +	__gthread_time_t __ts =
>>>> +	  {
>>>> +	    static_cast<std::time_t>(__s.time_since_epoch().count()),
>>>> +	    static_cast<long>(__ns.count())
>>>> +	  };
>>>> +
>>>> +	pthread_cond_clockwait(__cv, __lock.mutex()->native_handle(),
>>>> +			       CLOCK_MONOTONIC,
>>>> +			       &__ts);
>>>> +	return (chrono::steady_clock::now() < __atime)
>>>> +	       ? __atomic_wait_status::__no_timeout : __atomic_wait_status::__timeout;
>>>> +      }
>>>> +#endif
>>>> +
>>>> +      template<typename _Duration>
>>>> +	__atomic_wait_status
>>>> +	__cond_wait_until_impl(__gthread_cond_t* __cv, std::unique_lock<std::mutex>& __lock,
>>>> +			       const chrono::time_point<chrono::system_clock, _Duration>& __atime)
>>>> +	{
>>>> +	  auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
>>>> +	  auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
>>>> +
>>>> +	  __gthread_time_t __ts =
>>>> +	  {
>>>> +	    static_cast<std::time_t>(__s.time_since_epoch().count()),
>>>> +	    static_cast<long>(__ns.count())
>>>> +	  };
>>>> +
>>>> +	  __gthread_cond_timedwait(__cv, __lock.mutex()->native_handle(),
>>>> +				   &__ts);
>>>> +	  return (chrono::system_clock::now() < __atime)
>>>> +		 ? __atomic_wait_status::__no_timeout : __atomic_wait_status::__timeout;
>>>> +	}
>>>> +
>>>> +      // return true if timeout
>>>> +      template<typename _Clock, typename _Duration>
>>>> +	__atomic_wait_status
>>>> +	__cond_wait_until(__gthread_cond_t* __cv, std::unique_lock<std::mutex>& __lock,
>>>> +			  const chrono::time_point<_Clock, _Duration>& __atime)
>>>> +	{
>>>> +#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT
>>>> +	  using __clock_t = chrono::steady_clock;
>>>> +#else
>>>> +	  using __clock_t = chrono::system_clock;
>>>> +#endif
>>>> +	  const typename _Clock::time_point __c_entry = _Clock::now();
>>>> +	  const __clock_t::time_point __s_entry = __clock_t::now();
>>>> +	  const auto __delta = __atime - __c_entry;
>>>> +	  const auto __s_atime = __s_entry + __delta;
>>>> +	  if (__cond_wait_until_impl(__cv, __lock, __s_atime))
>>>> +	    return __atomic_wait_status::__no_timeout;
>>>> +	  // We got a timeout when measured against __clock_t but
>>>> +	  // we need to check against the caller-supplied clock
>>>> +	  // to tell whether we should return a timeout.
>>>> +	  if (_Clock::now() < __atime)
>>>> +	    return __atomic_wait_status::__no_timeout;
>>>> +	  return __atomic_wait_status::__timeout;
>>>> +	}
>>>> +
>>>> +    struct __timed_waiters : __waiters
>>>> +    {
>>>> +      template<typename _Clock, typename _Duration>
>>>> +	__atomic_wait_status
>>>> +	_M_do_wait_until(int32_t __version,
>>>> +			 const chrono::time_point<_Clock, _Duration>& __atime)
>>>> +	{
>>>> +	  int32_t __cur = 0;
>>>> +	  __waiters::__lock_t __l(_M_mtx);
>>>> +	  while (__cur <= __version)
>>>> +	    {
>>>> +	      if (__cond_wait_until(&_M_cv, __l, __atime) == __atomic_wait_status::__timeout)
>>>> +		return __atomic_wait_status::__timeout;
>>>> +
>>>> +	      int32_t __last = __cur;
>>>> +	      __atomic_load(&_M_ver, &__cur, __ATOMIC_ACQUIRE);
>>>> +	      if (__cur < __last)
>>>> +		break; // break the loop if version overflows
>>>> +	    }
>>>> +	  return __atomic_wait_status::__no_timeout;
>>>> +	}
>>>> +
>>>> +      static __timed_waiters&
>>>> +      _S_timed_for(void* __t)
>>>> +      {
>>>> +	static_assert(sizeof(__timed_waiters) == sizeof(__waiters));
>>>> +	return (__timed_waiters&) __waiters::_S_for(__t);
>>>> +      }
>>>> +    };
>>>> +  } // namespace __detail
>>>> +
>>>> +  template<typename _Tp, typename _Pred,
>>>> +	   typename _Clock, typename _Duration>
>>>> +    bool
>>>> +    __atomic_wait_until(const _Tp* __addr, _Tp __old, _Pred __pred,
>>>> +			const chrono::time_point<_Clock, _Duration>& __atime) noexcept
>>>> +    {
>>>> +      using namespace __detail;
>>>> +
>>>> +      if (__atomic_spin(__pred))
>>> 
>>> Qualify to prevent ADL.
>>> 
>>>> +	return true;
>>>> +
>>>> +      auto& __w = __timed_waiters::_S_timed_for((void*)__addr);
>>>> +      auto __version = __w._M_enter_wait();
>>>> +      do
>>>> +	{
>>>> +	  __atomic_wait_status __res;
>>>> +	  if constexpr (__platform_wait_uses_type<_Tp>::__value)
>>>> +	    {
>>>> +	      __res = __platform_wait_until((__platform_wait_t*)(void*) __addr, __old,
>>>> +					     __atime);
>>>> +	    }
>>>> +	  else
>>>> +	    {
>>>> +	      __res = __w._M_do_wait_until(__version, __atime);
>>>> +	    }
>>>> +	  if (__res == __atomic_wait_status::__timeout)
>>>> +	    return false;
>>>> +	}
>>>> +      while (!__pred() && __atime < _Clock::now());
>>>> +      __w._M_leave_wait();
>>>> +
>>>> +      // if timed out, return false
>>>> +      return (_Clock::now() < __atime);
>>>> +    }
>>>> +
>>>> +  template<typename _Tp, typename _Pred,
>>>> +	   typename _Rep, typename _Period>
>>>> +    bool
>>>> +    __atomic_wait_for(const _Tp* __addr, _Tp __old, _Pred __pred,
>>>> +		      const chrono::duration<_Rep, _Period>& __rtime) noexcept
>>>> +    {
>>>> +      using namespace __detail;
>>>> +
>>>> +      if (__atomic_spin(__pred))
>>>> +	return true;
>>>> +
>>>> +      if (!__rtime.count())
>>>> +	return false; // no rtime supplied, and spin did not acquire
>>>> +
>>>> +      using __dur = chrono::steady_clock::duration;
>>>> +      auto __reltime = chrono::duration_cast<__dur>(__rtime);
>>>> +      if (__reltime < __rtime)
>>>> +	++__reltime;
>>>> +
>>>> +
>>>> +      return __atomic_wait_until(__addr, __old, std::move(__pred),
>>>> +				 chrono::steady_clock::now() + __reltime);
>>>> +    }
>>>> +_GLIBCXX_END_NAMESPACE_VERSION
>>>> +} // namespace std
>>>> +#endif
>>>> diff --git a/libstdc++-v3/include/bits/atomic_wait.h b/libstdc++-v3/include/bits/atomic_wait.h
>>>> new file mode 100644
>>>> index 00000000000..32070a54f40
>>>> --- /dev/null
>>>> +++ b/libstdc++-v3/include/bits/atomic_wait.h
>>>> @@ -0,0 +1,280 @@
>>>> +// -*- 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>
>>> 
>>> This should be typename not class.
>>> 
>>>> +      struct __platform_wait_uses_type
>>>> +      {
>>>> +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
>>>> +	enum { __value = std::is_same<typename std::remove_cv<_Tp>::type,
>>> 
>>> This should be remove_cv_t.
>>> 
>>>> +				      __platform_wait_t>::value };
>>>> +#else
>>>> +	enum { __value = std::false_type::value };
>>>> +#endif
>>> 
>>> There's no need to use the C++03 enum hack here, it should just derive
>>> from true_type or false_type.
>>> 
>>>   template<typename _Tp>
>>>     struct __platform_wait_uses_type
>>> #ifdef _GLIBCXX_HAVE_LINUX_FUTEX
>>>     : is_same<std::remove_cv_t<_Tp>, __platform_wait_t>
>>> #else
>>>     : false_type
>>> #endif
>>>     { };
>>> 
>>> Or better yet, just use a variable template:
>>> 
>>>   template<typename _Tp>
>>>     inline constexpr bool __platform_wait_uses_type
>>> #ifdef _GLIBCXX_HAVE_LINUX_FUTEX
>>>     = is_same_v<std::remove_cv_t<_Tp>, __platform_wait_t>;
>>> #else
>>>     = false;
>>> #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_bitset = 9,
>>>> +      __futex_wake_bitset = 10,
>>>> +      __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
>>> 
>>> Isn't alignas(64) already implied by the first data member?
>>> 
>>>> +    {
>>>> +      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
>>> 
>>> Don't we always need these even with futexes, for the types that don't
>>> use a futex?
>>> 
>>>> +      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;
>>> 
>>> If we moved std::condition_variable into its own header (or
>>> <bits/std_mutex.h>, could we reuse that here instead of using
>>> __gthread_cond_t directly?
>>> 
>>>> +#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);
>>>> +	    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];
>>>> +      }
>>>> +    };
>>>> +
>>>> +    struct __waiter
>>>> +    {
>>>> +      __waiters& _M_w;
>>>> +      int32_t _M_version;
>>>> +
>>>> +      template<typename _Tp>
>>>> +	__waiter(const _Tp* __addr) noexcept
>>>> +	  : _M_w(__waiters::_S_for((void*) __addr))
>>>> +	  , _M_version(_M_w._M_enter_wait())
>>>> +	{ }
>>>> +
>>>> +      ~__waiter()
>>>> +      { _M_w._M_leave_wait(); }
>>>> +
>>>> +      void _M_do_wait() noexcept
>>>> +      { _M_w._M_do_wait(_M_version); }
>>>> +    };
>>>> +
>>>> +    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
>>>> +    }
>>>> +
>>>> +  } // namespace __detail
>>>> +
>>>> +  template<class _Pred>
>>> 
>>> s/class/template/
>>> 
>>>> +    bool
>>>> +    __atomic_spin(_Pred __pred) noexcept
>>>> +    {
>>>> +      for (auto __i = 0; __i < _GLIBCXX_SPIN_COUNT_1; ++__i)
>>>> +	{
>>>> +	  if (__pred())
>>>> +	    return true;
>>>> +
>>>> +	  if (__i < _GLIBCXX_SPIN_COUNT_2)
>>>> +	    __detail::__thread_relax();
>>>> +	  else
>>>> +	    __detail::__thread_yield();
>>>> +	}
>>>> +      return false;
>>>> +    }
>>>> +
>>>> +  template<class _Tp, class _Pred>
>>> 
>>> s/class/template/
>>> 
>>>> +    void
>>>> +    __atomic_wait(const _Tp* __addr, _Tp __old, _Pred __pred) noexcept
>>>> +    {
>>>> +      using namespace __detail;
>>>> +      if (__atomic_spin(__pred))
>>>> +	return;
>>>> +
>>>> +      __waiter __w(__addr);
>>>> +      while (!__pred())
>>>> +	{
>>>> +	  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();
>>>> +	    }
>>>> +	}
>>>> +    }
>>>> +
>>>> +  template<class _Tp>
>>> 
>>> s/class/template/
>>> 
>>>> +    void
>>>> +    __atomic_notify(const _Tp* __addr, bool __all) noexcept
>>>> +    {
>>>> +      using namespace __detail;
>>>> +      auto& __w = __waiters::_S_for((void*)__addr);
>>>> +      if (!__w._M_waiting())
>>> 
>>> When __platform_wait_uses_type<_Tp> is true, will __w._M_waiting()
>>> ever be true? Won't this always return before notifying?
>>> 
>>> Is there meant to be a __waiter constructed here?
>>> 
>>>> +	return;
>>>> +
>>>> +      if constexpr (__platform_wait_uses_type<_Tp>::__value)
>>>> +	{
>>>> +	  __platform_notify((__platform_wait_t*)(void*) __addr, __all);
>>>> +	}
>>>> +      else
>>>> +	{
>>>> +	  __w._M_notify(__all);
>>>> +	}
>>>> +    }
>>>> +_GLIBCXX_END_NAMESPACE_VERSION
>>>> +} // namespace std
>>>> +#endif
>>>> diff --git a/libstdc++-v3/include/bits/semaphore_base.h b/libstdc++-v3/include/bits/semaphore_base.h
>>>> new file mode 100644
>>>> index 00000000000..b3c83bbc70b
>>>> --- /dev/null
>>>> +++ b/libstdc++-v3/include/bits/semaphore_base.h
>>>> @@ -0,0 +1,270 @@
>>>> +// -*- 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/semaphore.h
>>> 
>>> Should be bits/semaphore_base.h
>>> 
>>>> + *  This is an internal header file, included by other library headers.
>>>> + *  Do not attempt to use it directly. @headername{atomic}
>>> 
>>> Should be @headername{semaphore}
>>> 
>>>> + */
>>>> +
>>>> +#ifndef _GLIBCXX_SEMAPHORE_BASE_H
>>>> +#define _GLIBCXX_SEMAPHORE_BASE_H 1
>>>> +
>>>> +#pragma GCC system_header
>>>> +
>>>> +#include <bits/c++config.h>
>>>> +#include <bits/atomic_base.h>
>>>> +#include <bits/atomic_timed_wait.h>
>>>> +
>>>> +#if defined _POSIX_SEMAPHORES  && __has_include(<semaphore.h>)
>>>> +#define _GLIBCXX_HAVE_POSIX_SEMAPHORE 1
>>>> +#include <semaphore.h>
>>>> +#endif
>>>> +
>>>> +#include <chrono>
>>>> +#include <type_traits>
>>>> +#include <limits>
>>> 
>>> <ext/numeric_traits.h> is much smaller than <limits> and should be
>>> used for limits of integer types. (I recently added
>>> <bits/int_limits.h> too but that was a mistake that I need to fix).
>>> 
>>> 
>>>> +namespace std _GLIBCXX_VISIBILITY(default)
>>>> +{
>>>> +_GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>> +
>>>> +#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
>>>> +  template<ptrdiff_t __least_max_t>
>>> 
>>> __least_max_t isn't a type so shouldn't have the _t suffix.
>>> 
>>>> +    struct __platform_semaphore
>>>> +    {
>>>> +      using __clock_t = chrono::system_clock;
>>>> +
>>>> +      __platform_semaphore(ptrdiff_t __count) noexcept
>>> 
>>> Should this constructor be explicit?
>>> 
>>>> +      {
>>>> +	static_assert( __least_max_t <= SEM_VALUE_MAX, "__least_max_t > SEM_VALUE_MAX");
>>> 
>>> Our static_assert messages should state the positive condition, not
>>> the negative one. So it should be "__least_max_t <= SEM_VALUE_MAX",
>>> which is what the real condition is anyway, so you might as well omit
>>> the string literal.
>>> 
>>>> +	auto __e = sem_init(&_M_semaphore, 0, __count);
>>>> +	if (__e)
>>>> +	  std::terminate();
>>>> +      }
>>>> +
>>>> +      ~__platform_semaphore()
>>>> +      {
>>>> +	auto __e = sem_destroy(&_M_semaphore);
>>>> +	if (__e)
>>>> +	  std::terminate();
>>>> +      }
>>>> +
>>>> +      _GLIBCXX_ALWAYS_INLINE void
>>>> +      acquire() noexcept
>>>> +      {
>>>> +	auto __err = sem_wait(&_M_semaphore);
>>>> +	if (__err)
>>>> +	  std::terminate();
>>>> +      }
>>>> +
>>>> +      template<typename _Duration>
>>>> +	_GLIBCXX_ALWAYS_INLINE bool
>>> 
>>> Do we really need this to be always_inline?
>>> 
>>>> +	__try_acquire_until_impl(const chrono::time_point<__clock_t>& __atime) noexcept
>>>> +	{
>>>> +	  auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
>>>> +	  auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
>>>> +
>>>> +	  struct timespec __ts =
>>>> +	  {
>>>> +	    static_cast<std::time_t>(__s.time_since_epoch().count()),
>>>> +	    static_cast<long>(__ns.count())
>>>> +	  };
>>>> +
>>>> +	  auto __err = sem_timedwait(&_M_semaphore, &__ts);
>>>> +	  if (__err && (errno == ETIMEDOUT))
>>>> +	      return false;
>>>> +	  else if (__err)
>>>> +	      std::terminate();
>>>> +	  return true;
>>>> +	}
>>>> +
>>>> +      template<typename _Clock, typename _Duration>
>>>> +	_GLIBCXX_ALWAYS_INLINE bool
>>> 
>>> always_inline?
>>> 
>>>> +	try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept
>>>> +	{
>>>> +	  if constexpr (std::is_same<__clock_t, _Clock>::value)
>>> 
>>> is_same_v
>>> 
>>>> +	    {
>>>> +	      return __try_acquire_until_impl(__atime);
>>>> +	    }
>>>> +	  else
>>>> +	    {
>>>> +	      const typename _Clock::time_point __c_entry = _Clock::now();
>>>> +	      const __clock_t __s_entry = __clock_t::now();
>>>> +	      const auto __delta = __atime - __c_entry;
>>>> +	      const auto __s_atime = __s_entry + __delta;
>>>> +	      if (__try_acquire_until_impl(__s_atime))
>>>> +		return true;
>>>> +
>>>> +	      // We got a timeout when measured against __clock_t but
>>>> +	      // we need to check against the caller-supplied clock
>>>> +	      // to tell whether we should return a timeout.
>>>> +	      return (_Clock::now() < __atime);
>>>> +	    }
>>>> +	}
>>>> +
>>>> +      template<typename _Rep, typename _Period>
>>>> +	_GLIBCXX_ALWAYS_INLINE bool
>>>> +	try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) noexcept
>>>> +	{ return try_acquire_until(__clock_t::now() + __rtime); }
>>>> +
>>>> +      template<typename _Clock, typename _Duration>
>>>> +	_GLIBCXX_ALWAYS_INLINE void
>>>> +	release(ptrdiff_t __update) noexcept
>>>> +	{
>>>> +	  do
>>>> +	    {
>>>> +	      auto __err = sem_post(&_M_semaphore);
>>>> +	      if (__err)
>>>> +		std::terminate();
>>>> +	    } while (--__update);
>>>> +	}
>>>> +
>>>> +      private:
>>>> +	sem_t _M_semaphore;
>>>> +      };
>>>> +#endif // _GLIBCXX_HAVE_POSIX_SEMAPHORE
>>>> +
>>>> +    template<typename _Tp>
>>>> +      struct __atomic_semaphore
>>>> +      {
>>>> +	static constexpr size_t _S_alignment = __alignof__(_Tp);
>>>> +
>>>> +	__atomic_semaphore(_Tp __count)
>>> 
>>> Should this be explicit?
>>> 
>>>> +	  : _M_a(__count)
>>>> +	{ }
>>>> +
>>>> +	_GLIBCXX_ALWAYS_INLINE void
>>>> +	acquire() noexcept
>>>> +	{
>>>> +	  auto const __pred = [this]
>>>> +	    {
>>>> +	      auto __old = __atomic_impl::load(&this->_M_a, memory_order::acquire);
>>>> +	      if (__old == 0)
>>>> +		return false;
>>>> +	      return __atomic_impl::compare_exchange_strong(&this->_M_a,
>>>> +							    __old, __old - 1,
>>>> +							    memory_order::acquire,
>>>> +							    memory_order::release);
>>>> +	    };
>>>> +	  auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed);
>>>> +	  __atomic_wait(&_M_a, __old, __pred);
>>>> +	}
>>>> +
>>>> +	bool
>>>> +	try_acquire() noexcept
>>>> +	{
>>>> +	  auto __old = __atomic_impl::load(&_M_a, memory_order::acquire);
>>>> +	  if (__old == 0)
>>>> +	    return false;
>>>> +
>>>> +	  return __atomic_spin([this, &__old]
>>>> +	    {
>>>> +	      return __atomic_impl::compare_exchange_weak(&this->_M_a,
>>>> +							  __old, __old - 1,
>>>> +							  memory_order::acquire,
>>>> +							  memory_order::release);
>>>> +	    });
>>>> +	}
>>>> +
>>>> +	template<typename _Clock, typename _Duration>
>>>> +	  _GLIBCXX_ALWAYS_INLINE bool
>>>> +	  try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept
>>>> +	  {
>>>> +	    auto const __pred = [this]
>>>> +	      {
>>>> +		auto __old = __atomic_impl::load(&this->_M_a, memory_order::acquire);
>>>> +		if (__old == 0)
>>>> +		  return false;
>>>> +		return __atomic_impl::compare_exchange_strong(&this->_M_a,
>>>> +							       __old, __old - 1,
>>>> +							       memory_order::acquire,
>>>> +							       memory_order::release);
>>>> +	      };
>>>> +
>>>> +	    auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed);
>>>> +	    return __atomic_wait_until(&_M_a, __old, __pred, __atime);
>>>> +	}
>>>> +
>>>> +      template<typename _Rep, typename _Period>
>>>> +	_GLIBCXX_ALWAYS_INLINE bool
>>>> +	try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) noexcept
>>>> +	{
>>>> +	  auto const __pred = [this]
>>>> +	    {
>>>> +	      auto __old = __atomic_impl::load(&this->_M_a, memory_order::acquire);
>>>> +	      if (__old == 0)
>>>> +		return false;
>>>> +	      return  __atomic_impl::compare_exchange_strong(&this->_M_a,
>>>> +							     __old, __old - 1,
>>>> +							     memory_order::acquire,
>>>> +							     memory_order::release);
>>>> +	    };
>>>> +
>>>> +	  auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed);
>>>> +	  return __atomic_wait_for(&_M_a, __old, __pred, __rtime);
>>>> +	}
>>>> +
>>>> +      _GLIBCXX_ALWAYS_INLINE void
>>>> +      release(ptrdiff_t __update) noexcept
>>>> +      {
>>>> +	if (0 < __atomic_impl::fetch_add(&_M_a, __update, memory_order_release))
>>>> +	  return;
>>>> +	if (__update > 1)
>>>> +	  __atomic_impl::notify_all(&_M_a);
>>>> +	else
>>>> +	  __atomic_impl::notify_one(&_M_a);
>>>> +      }
>>>> +
>>>> +    private:
>>>> +      alignas(_S_alignment) _Tp _M_a;
>>> 
>>> Could this just use alignas(__alignof__(_Tp)) _Tp here? There's no
>>> need for the _S_alignment constant if it's only used in one place.
>>> 
>>>> +    };
>>>> +
>>>> +#ifdef _GLIBCXX_REQUIRE_POSIX_SEMAPHORE
>>>> +  template<ptrdiff_t __least_max_t>
>>> 
>>> Rename __least_max_t here too.
>>> 
>>>> +    using __semaphore_base = __platform_semaphore<__least_max_t>;
>>>> +#else
>>>> +#  ifdef _GLIBCXX_HAVE_LINUX_FUTEX
>>>> +  template<ptrdiff_t __least_max_t>
>>>> +    using __semaphore_base = std::conditional<(__least_max_t > 0
>>> 
>>> This should use conditional_t<> not conditional<>::type.
>>> 
>>> The least-max_value can't be negative. If it's zero, can't we use a
>>> futex or semaphore? So the '__least_max_t > 0' condition is wrong?
>>> 
>>>> +					      && __least_max_t < std::numeric_limits<__detail::__platform_wait_t>::max()),
>>> 
>>> Should that be <= rather than < ?
>>> 
>>>> +					      __atomic_semaphore<__detail::__platform_wait_t>,
>>>> +					      __atomic_semaphore<ptrdiff_t>>::type;
>>>> +				            // __platform_semaphore
>>>> +#  else
>>>> +#    ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
>>> 
>>> Please use '#elif defined _GLIBCXX_HAVE_POSIX_SEMAPHORE' here to avoid
>>> an extra level of #if nesting.
>>> 
>>>> +  template<ptrdiff_t __least_max_t>
>>>> +    using __semaphore_base = std::conditional<(__least_max_t > 0 && __least_max_t <= SEM_VALUE_MAX),
>>>> +					      __platform_semaphore<__least_max_t>,
>>>> +					      __atomic_semaphore<ptrdiff_t>>::type;
>>>> +#    else
>>>> +  template<ptrdiff_t __least_max_t>
>>>> +    using __semaphore_base = __atomic_semaphore<ptrdiff_t>;
>>>> +#    endif
>>>> +#  endif
>>>> +#endif
>>>> +
>>>> +_GLIBCXX_END_NAMESPACE_VERSION
>>>> +} // namespace std
>>>> +
>>>> +#endif
>>>> diff --git a/libstdc++-v3/include/std/atomic b/libstdc++-v3/include/std/atomic
>>>> index a455286a784..3f18774031d 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/include/std/latch b/libstdc++-v3/include/std/latch
>>>> new file mode 100644
>>>> index 00000000000..0099877416e
>>>> --- /dev/null
>>>> +++ b/libstdc++-v3/include/std/latch
>>>> @@ -0,0 +1,91 @@
>>>> +//<latch> -*- C++ -*-
>>> 
>>> A space before <latch>.
>>> 
>>>> +
>>>> +// 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 include/latch
>>>> + *	This is a Standard C++ Library header.
>>> 
>>> Align "This" with "@file" here.
>>> 
>>>> + */
>>>> +
>>>> +#ifndef _GLIBCXX_LATCH
>>>> +#define _GLIBCXX_LATCH
>>>> +
>>>> +#pragma GCC system_header
>>>> +
>>>> +#if __cplusplus > 201703L
>>>> +#define __cpp_lib_latch 201907L
>>>> +
>>>> +#include <bits/atomic_base.h>
>>>> +#include <limits>
>>> 
>>> Use <ext/numeric_traits.h> here too.
>>> 
>>>> +namespace std _GLIBCXX_VISIBILITY(default)
>>>> +{
>>>> +_GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>> +
>>>> +  class latch
>>>> +  {
>>>> +    static constexpr size_t _S_alignment = __alignof__(ptrdiff_t);
>>>> +  public:
>>>> +    static constexpr
>>>> +    _GLIBCXX_ALWAYS_INLINE ptrdiff_t
>>>> +    max() noexcept
>>>> +    { return numeric_limits<ptrdiff_t>::max(); }
>>>> +
>>>> +    constexpr explicit latch(ptrdiff_t __expected) : _M_a(__expected) { }
>>>> +
>>>> +    ~latch() = default;
>>>> +    latch(const latch&) = delete;
>>>> +    latch& operator=(const latch&) = delete;
>>>> +
>>>> +    _GLIBCXX_ALWAYS_INLINE void
>>>> +    count_down(ptrdiff_t __update = 1)
>>>> +    {
>>>> +      auto const __old = __atomic_impl::fetch_sub(&_M_a, __update, memory_order::release);
>>>> +      if (__old == __update)
>>>> +	__atomic_impl::notify_all(&_M_a);
>>>> +    }
>>>> +
>>>> +    _GLIBCXX_ALWAYS_INLINE bool
>>>> +    try_wait() const noexcept
>>>> +    { return __atomic_impl::load(&_M_a, memory_order::acquire) == 0; }
>>>> +
>>>> +    _GLIBCXX_ALWAYS_INLINE void
>>>> +    wait() const
>>>> +    {
>>>> +      auto const __old = __atomic_impl::load(&_M_a, memory_order::acquire);
>>>> +      __atomic_wait(&_M_a, __old, [this] { return this->try_wait(); });
>>>> +    }
>>>> +
>>>> +    _GLIBCXX_ALWAYS_INLINE void
>>>> +    arrive_and_wait(ptrdiff_t __update = 1)
>>>> +    {
>>>> +      count_down();
>>>> +      wait();
>>>> +    }
>>>> +
>>>> +  private:
>>>> +    alignas(_S_alignment) ptrdiff_t _M_a;
>>> 
>>> Just use __alignof__ directly here and get rid of _S_alignment?
>>> 
>>>> +  };
>>>> +_GLIBCXX_END_NAMESPACE_VERSION
>>>> +} // namespace
>>>> +#endif // __cplusplus > 201703L
>>>> +#endif // _GLIBCXX_LATCH
>>>> diff --git a/libstdc++-v3/include/std/semaphore b/libstdc++-v3/include/std/semaphore
>>>> new file mode 100644
>>>> index 00000000000..b51940b46ac
>>>> --- /dev/null
>>>> +++ b/libstdc++-v3/include/std/semaphore
>>>> @@ -0,0 +1,81 @@
>>>> +//<semaphore> -*- C++ -*-
>>> 
>>> A space before <semaphore>.
>>> 
>>>> +
>>>> +// 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 include/semaphore
>>>> + *	This is a Standard C++ Library header.
>>> 
>>> Align "This" with "@file" here.
>>> 
>>>> + */
>>>> +
>>>> +#ifndef _GLIBCXX_SEMAPHORE
>>>> +#define _GLIBCXX_SEMAPHORE
>>>> +
>>>> +#pragma GCC system_header
>>>> +
>>>> +#if __cplusplus > 201703L
>>>> +#define __cpp_lib_semaphore 201907L
>>>> +#include <bits/semaphore_base.h>
>>>> +
>>>> +namespace std _GLIBCXX_VISIBILITY(default)
>>>> +{
>>>> +_GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>> +
>>>> +  template<ptrdiff_t __least_max_value = std::numeric_limits<ptrdiff_t>::max()>
>>>> +    class counting_semaphore
>>>> +    {
>>> 
>>> I don't see a static_assert making it ill-formed to use a negative
>>> value for __least_max_value. Is that enforced somewhere else?
>>> 
>>> The standard says it's ill-formed, so we should also have a _neg.cc
>>> test checking that we reject it.
>>> 
>>>> +      __semaphore_base<__least_max_value> _M_sem;
>>> 
>>> Blank line after this please.
>>> 
>>>> +    public:
>>>> +      explicit counting_semaphore(ptrdiff_t __desired) noexcept
>>>> +	: _M_sem(__desired)
>>>> +      { }
>>>> +
>>>> +      ~counting_semaphore() = default;
>>>> +
>>>> +      counting_semaphore(const counting_semaphore&) = delete;
>>>> +      counting_semaphore& operator=(const counting_semaphore&) = delete;
>>>> +
>>>> +      static constexpr ptrdiff_t max() noexcept
>>>> +      { return __least_max_value; }
>>>> +
>>>> +      void release(ptrdiff_t __update = 1)
>>>> +      { _M_sem.release(__update); }
>>>> +
>>>> +      void acquire()
>>>> +      { _M_sem.acquire(); }
>>>> +
>>>> +      bool try_acquire() noexcept
>>>> +      { return _M_sem.try_acquire(); }
>>>> +
>>>> +      template<class _Rep, class _Period>
>>> 
>>> s/class/template/
>>> 
>>>> +	bool try_acquire_for(const std::chrono::duration<_Rep, _Period>& __rel_time)
>>>> +	{ return _M_sem.try_acquire_for(__rel_time); }
>>>> +
>>>> +      template<class _Clock, class _Duration>
>>> 
>>> s/class/template/
>>> 
>>>> +	bool try_acquire_until(const std::chrono::time_point<_Clock, _Duration>& __abs_time)
>>>> +	{ return _M_sem.try_acquire_until(__abs_time); }
>>>> +    };
>>>> +
>>>> + using binary_semaphore = std::counting_semaphore<1>;
>>>> +_GLIBCXX_END_NAMESPACE_VERSION
>>>> +} // namespace
>>>> +#endif // __cplusplus > 201703L
>>>> +#endif // _GLIBCXX_SEMAPHORE
>>>> diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version
>>>> index c3a5bd26e63..390990282b0 100644
>>>> --- a/libstdc++-v3/include/std/version
>>>> +++ b/libstdc++-v3/include/std/version
>>>> @@ -188,6 +188,8 @@
>>>> #endif
>>>> #define __cpp_lib_type_identity 201806L
>>>> #define __cpp_lib_unwrap_ref 201811L
>>>> +#define __cpp_lib_semaphore 201907L
>>>> +#define __cpp_lib_latch 201907L
>>> 
>>> These features aren't supported in a freestanding implementation, so
>>> should be in the #if _GLIBCXX_HOSTED block (and the macros should be
>>> in alphabetical order).
>>> 
>>>> 
>>>> #if _GLIBCXX_HOSTED
>>>> #undef __cpp_lib_array_constexpr
>>>> 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" }
>>> 
>>> Use { dg-add-options libatomic } instead of adding -latomic -L...
>> 
> 



More information about the Libstdc++ mailing list