public inbox for libstdc++@gcc.gnu.org
 help / color / mirror / Atom feed
From: Jonathan Wakely <jwakely@redhat.com>
To: Thomas Rodgers <trodgers@redhat.com>
Cc: gcc-patches@gcc.gnu.org, libstdc++@gcc.gnu.org
Subject: Re: [PATCH] Add C++2a synchronization support
Date: Mon, 11 May 2020 15:05:04 +0100	[thread overview]
Message-ID: <20200511140504.GL2678@redhat.com> (raw)
In-Reply-To: <xkqeo8qwhd4u.fsf@trodgers.remote>

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...


  reply	other threads:[~2020-05-11 14:05 UTC|newest]

Thread overview: 43+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-05-10  0:01 Thomas Rodgers
2020-05-11 14:05 ` Jonathan Wakely [this message]
2020-05-11 15:43   ` Thomas Rodgers
2020-05-11 17:05     ` Jonathan Wakely
2020-05-11 20:59   ` Thomas Rodgers
2020-05-23 22:52     ` Thomas Rodgers
2020-05-24 17:41       ` Thomas Rodgers
2020-06-06  0:29       ` Thomas Rodgers
2020-07-08 16:43         ` Jonathan Wakely
2020-08-03 14:09         ` Jonathan Wakely
2020-08-03 20:19           ` Jonathan Wakely
2020-09-03  0:47             ` Thomas Rodgers
2020-09-03  0:54               ` Thomas Rodgers
2020-09-11 23:58                 ` [PATCH] libstdc++: " Thomas Rodgers
2020-09-28 13:25                   ` Jonathan Wakely
2020-10-01 23:37                     ` Thomas Rodgers
2020-10-02 15:40                       ` Thomas Rodgers
2020-10-05 22:54                       ` [PATCH] t/trodgers/c2a_synchronization Thomas Rodgers
2020-10-23 10:28                         ` Jonathan Wakely
2020-10-26 21:48                           ` [PATCH] libstdc++: Add C++2a synchronization support Thomas Rodgers
2020-10-27 10:23                             ` Jonathan Wakely
2020-11-20 22:44                               ` Thomas Rodgers
2020-11-22 21:13                                 ` Stephan Bergmann
2020-11-23 18:33                                   ` Jonathan Wakely
2020-11-22 21:41                                 ` Stephan Bergmann
2020-11-23 18:32                                   ` Jonathan Wakely
2020-11-21 15:16                             ` Andreas Schwab
2020-11-21 17:04                               ` Jonathan Wakely
2020-11-21 17:39                                 ` Jonathan Wakely
2020-11-22  0:36                                   ` H.J. Lu
2020-11-23 14:50                                     ` Jonathan Wakely
2020-11-24 23:45                                     ` Jonathan Wakely
2020-11-25  1:07                                       ` Jonathan Wakely
2020-11-25 10:35                                         ` Jonathan Wakely
2020-11-25 12:32                                           ` Jonathan Wakely
2020-11-25 18:39                                           ` Jonathan Wakely
2020-11-26 16:26                                             ` Jonathan Wakely
2020-11-23 16:08                                   ` Jonathan Wakely
2020-09-28 13:30                   ` Jonathan Wakely
2020-09-28 13:36                   ` Jonathan Wakely
2020-09-28 21:29                     ` Thomas Rodgers
2020-09-29  9:44                       ` Jonathan Wakely
2020-06-06 21:24 [PATCH] " Thomas Rodgers

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200511140504.GL2678@redhat.com \
    --to=jwakely@redhat.com \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=libstdc++@gcc.gnu.org \
    --cc=trodgers@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).