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