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 via email to