On 05/06/20 17:29 -0700, Thomas Rodgers wrote:
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.
        (__atomic_base<_Itp>::notify_one): Likewise.
        (__atomic_base<_Itp>::notify_all): Likewise.
        (__atomic_base<_Ptp*>::wait): Likewise.
        (__atomic_base<_Ptp*>::notify_one): Likewise.
        (__atomic_base<_Ptp*>::notify_all): Likewise.
        (__atomic_impl::wait): Likewise.
        (__atomic_impl::notify_one): Likewise.
        (__atomic_impl::notify_all): Likewise.
        (__atomic_float<_Fp>::wait): Likewise.
        (__atomic_float<_Fp>::notify_one): Likewise.
        (__atomic_float<_Fp>::notify_all): Likewise.
        (__atomic_ref<_Tp>::wait): Likewise.
        (__atomic_ref<_Tp>::notify_one): Likewise.
        (__atomic_ref<_Tp>::notify_all): Likewise.
        (atomic_wait<_Tp>): Likewise.
        (atomic_wait_explicit<_Tp>): Likewise.
        (atomic_notify_one<_Tp>): Likewise.
        (atomic_notify_all<_Tp>): Likewise.
        * include/bits/atomic_wait.h: New file.
       * include/bits/atomic_timed_wait.h: New file.
       * include/bits/semaphore_base.h: New file.
        * include/std/atomic (atomic<bool>::wait): Define.
        (atomic<bool>::wait_one): Likewise.
        (atomic<bool>::wait_all): Likewise.
        (atomic<_Tp>::wait): Likewise.
        (atomic<_Tp>::wait_one): Likewise.
        (atomic<_Tp>::wait_all): Likewise.
        (atomic<_Tp*>::wait): Likewise.
        (atomic<_Tp*>::wait_one): Likewise.
        (atomic<_Tp*>::wait_all): Likewise.
       * include/std/latch: New file.
       * include/std/semaphore: New file.
       * include/std/version: Add __cpp_lib_semaphore and
       __cpp_lib_latch defines.
        * testsuite/29_atomic/atomic/wait_notify/atomic_refs.cc: New test.
        * testsuite/29_atomic/atomic/wait_notify/bool.cc: Likewise.
        * testsuite/29_atomic/atomic/wait_notify/integrals.cc: Likewise.
        * testsuite/29_atomic/atomic/wait_notify/floats.cc: Likewise.
        * testsuite/29_atomic/atomic/wait_notify/pointers.cc: Likewise.
        * testsuite/29_atomic/atomic/wait_notify/generic.h: New File.
       * testsuite/30_thread/semaphore/1.cc: New test.
       * testsuite/30_thread/semaphore/2.cc: Likewise.
       * testsuite/30_thread/semaphore/least_max_value_neg.cc: Likewise.
       * testsuite/30_thread/semaphore/try_acquire.cc: Likewise.
       * testsuite/30_thread/semaphore/try_acquire_for.cc: Likewise.
       * testsuite/30_thread/semaphore/try_acquire_futex.cc: Likewise.
       * testsuite/30_thread/semaphore/try_acquire_posix.cc: Likewise.
       * testsuite/30_thread/semaphore/try_acquire_until.cc: Likewise.
       * testsuite/30_thread/latch/1.cc: New test.
       * testsuite/30_thread/latch/2.cc: New test.
       * testsuite/30_thread/latch/3.cc: New test.


diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
index 80aeb3f8959..b3ac1a3365f 100644
--- a/libstdc++-v3/include/Makefile.am
+++ b/libstdc++-v3/include/Makefile.am
@@ -52,6 +52,7 @@ std_headers = \
        ${std_srcdir}/iostream \
        ${std_srcdir}/istream \
        ${std_srcdir}/iterator \
+       ${std_srcdir}/latch\

Missing space before the backslash here.

        ${std_srcdir}/limits \
        ${std_srcdir}/list \
        ${std_srcdir}/locale \

--- a/libstdc++-v3/include/bits/atomic_base.h
+++ b/libstdc++-v3/include/bits/atomic_base.h
@@ -823,6 +851,30 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
                                           int(__m1), int(__m2));
      }

+#if __cplusplus > 201703L
+      _GLIBCXX_ALWAYS_INLINE void
+      wait(__pointer_type __old, memory_order __m = memory_order_seq_cst) 
noexcept

This line should be < 80 cols.

+      {
@@ -911,6 +963,32 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
                                         int(__success), int(__failure));
      }

+#if __cplusplus > 201703L
+    template<typename _Tp>
+      _GLIBCXX_ALWAYS_INLINE void
+      wait(const _Tp* __ptr, _Val<_Tp> __old, memory_order __m = 
memory_order_seq_cst) noexcept

And this one.


@@ -1164,6 +1242,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
                                       __cmpexch_failure_order(__order));
      }

+      _GLIBCXX_ALWAYS_INLINE void
+      wait(_Fp __old, memory_order __m = memory_order_seq_cst) const noexcept
+      { __atomic_impl::wait(&_M_fp, __old, __m); }
+
+      // TODO add const volatile overload
+
+      _GLIBCXX_ALWAYS_INLINE void
+      notify_one() const noexcept
+      { __atomic_impl::notify_one(&_M_fp); }
+
+      // TODO add const volatile overload
+
+      _GLIBCXX_ALWAYS_INLINE void
+      notify_all() const noexcept
+      { __atomic_impl::notify_all(&_M_fp); }
+
+      // TODO add const volatile overload

Please add a newline after this comment.

      value_type
      fetch_add(value_type __i,
                memory_order __m = memory_order_seq_cst) noexcept
@@ -1301,6 +1396,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
                                       __cmpexch_failure_order(__order));
      }

+      _GLIBCXX_ALWAYS_INLINE void
+      wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept
+      { __atomic_impl::wait(_M_ptr, __old, __m); }
+
+      // TODO add const volatile overload
+
+      _GLIBCXX_ALWAYS_INLINE void
+      notify_one() const noexcept
+      { __atomic_impl::notify_one(_M_ptr); }
+
+      // TODO add const volatile overload
+
+      _GLIBCXX_ALWAYS_INLINE void
+      notify_all() const noexcept
+      { __atomic_impl::notify_all(_M_ptr); }
+

The TODO comment seems to be missing here, and after some notify_all
cases below. Please either add one after every non-volatile function
that's missing a volatile overload, or just put one "TODO volatile
overloads of wait and notify_{one,all}" comment in each class.

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..adef80aca61
--- /dev/null
+++ b/libstdc++-v3/include/bits/atomic_timed_wait.h
@@ -0,0 +1,282 @@
+// -*- 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

No indentation for these BEGIN/END macros.

+
+  enum class __atomic_wait_status { __no_timeout, __timeout };

It seems a shame to have yet another status macro when we already have
cv_status and future_status which define enumerators with the same
names, but I suppose it's a bit confusing to reuse them. Maybe you
could just do:

using __atomic_wait_status = cv_status;

and then refer to __atomic_wait_status::no_timeout and
__atomic_wait_status::timeout, what do you think?

Even if it's a new distinct enum type, you could use no_timeout and
timeout as the enumerator names, because those are reserved names
anyway.

+  namespace __detail
+  {
+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
+    enum

I think unnamed enum types in headers cause problems for modules.

+    {
+      __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;
+
+    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);
+
+       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_v<__platform_wait_clock_t, _Clock>)
+         {
+           return __platform_wait_until_impl(__addr, __val, __atime);

Since this is calling a free function with arguments of
program-defined types (the clock and duration), it needs to be
qualified (whereas the is_same_v above doesn't need to be qualified,
although that's harmless).

+         }
+       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,
+         unique_lock<mutex>& __lock,
+         const chrono::time_point<chrono::steady_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())
+         };
+
+       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,
+           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,
+           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))

__cond_wait_until_impl should be qualified.

+           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(__platform_wait_t __version,
+                        const chrono::time_point<_Clock, _Duration>& __atime)
+       {
+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
+         return __platform_wait_until(&_M_ver, __version, __atime);
+#else
+         __platform_wait_t __cur = 0;
+         __waiters::__lock_t __l(_M_mtx);
+         while (__cur <= __version)
+           {
+             if (__cond_wait_until(&_M_cv, __l, __atime) == 
__atomic_wait_status::__timeout)

Qualify.

+               return __atomic_wait_status::__timeout;
+
+             __platform_wait_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;
+#endif
+       }
+
+      static __timed_waiters&
+      _S_timed_for(void* __t)
+      {
+       static_assert(sizeof(__timed_waiters) == sizeof(__waiters));
+       return (__timed_waiters&) __waiters::_S_for(__t);

I'd be more comfortable with a static_cast here.

+      }
+    };
+  } // 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 (std::__atomic_spin(__pred))
+       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>)
+           {
+             __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 (std::__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..92c1e2526ed
--- /dev/null
+++ b/libstdc++-v3/include/bits/atomic_wait.h
@@ -0,0 +1,291 @@
+// -*- 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>
+#include <ext/numeric_traits.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
+

Yuck, do these have to be macros?

+// 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;
+
+    inline constexpr
+    auto __platform_wait_max_value =
+               __gnu_cxx::__numeric_traits<__platform_wait_t>::__max;

You can usee the new __gnu_cxx::__int_traits alias here, since you
know __platform_wait_t is an integer, not a floating-point type. That
alias just saves instantiating __numeric_traits to figure out whether
to use __numeric_traits_integer or __numeric_traits_floating.

+
+    template<typename _Tp>
+      inline constexpr bool __platform_wait_uses_type
+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
+       = is_same_v<remove_cv_t<_Tp>, __platform_wait_t>;
+#else
+       = false;
+#endif
+
+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
+    enum

See earlier comment about unnamed enum types.


+    {
+#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 __waiters
+    {
+      __platform_wait_t alignas(64) _M_ver = 0;
+      __platform_wait_t alignas(64) _M_wait = 0;
+
+#ifndef _GLIBCXX_HAVE_LINUX_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;

Not being able to use std::condition_variable here makes me sad.

+      __waiters() noexcept = default;
+#  else
+      mutable __gthread_cond_t _M_cv;
+      __waiters() noexcept
+      {
+       __GTHREAD_COND_INIT_FUNCTION(&_M_cond);
+      }
+#  endif
+#endif
+
+      __platform_wait_t
+      _M_enter_wait() noexcept
+      {
+       __platform_wait_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(__platform_wait_t __version) noexcept
+      {
+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
+       __platform_wait(&_M_ver, __version);
+#else
+       __platform_wait_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();
+           __platform_wait_t __last = __cur;
+           __atomic_load(&_M_ver, &__cur, __ATOMIC_ACQUIRE);
+           if (__cur < __last)
+             break; // break the loop if version overflows
+         }
+#endif
+      }
+
+      __platform_wait_t
+      _M_waiting() const noexcept
+       {
+         __platform_wait_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);
+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
+       __platform_notify(&_M_ver, __all);
+#else
+       auto __e = __gthread_cond_broadcast(&_M_cv);
+       if (__e)
+         __throw_system_error(__e);
+#endif
+      }
+
+      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;
+      __platform_wait_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

Should this and the identical code in <stop_token> be consolidated
somewhere?

+    }
+
+    void
+    __thread_yield() noexcept
+   {
+#if defined _GLIBCXX_USE_SCHED_YIELD
+     __gthread_yield();
+#endif
+    }
+
+  } // namespace __detail
+
+  template<typename _Pred>
+    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<typename _Tp, typename _Pred>
+    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>)
+           {
+             __platform_wait((__platform_wait_t*)(void*) __addr, __old);

This has an implicit conversion from _To to __platform_wait_t, is that
safe? Can we make it explicit with a static_cast?

+           }
+         else
+           {
+             // TODO support timed backoff when this can be moved into the lib
+             __w._M_do_wait();
+           }
+       }
+    }
+
+  template<typename _Tp>
+    void
+    __atomic_notify(const _Tp* __addr, bool __all) noexcept
+    {
+      using namespace __detail;
+      auto& __w = __waiters::_S_for((void*)__addr);
+      if (!__w._M_waiting())
+       return;
+
+      if constexpr (__platform_wait_uses_type<_Tp>)
+       {
+         __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..f0c4235d91c
--- /dev/null
+++ b/libstdc++-v3/include/bits/semaphore_base.h

I'll continue reviewing from here ASAP ...


Reply via email to