From: Jonathan Wakely <jwakely@redhat.com>
To: Thomas Rodgers <trodgers@redhat.com>
Cc: gcc-patches@gcc.gnu.org, libstdc++@gcc.gnu.org
Subject: Re: [PATCH] Add C++2a synchronization support
Date: Mon, 11 May 2020 15:05:04 +0100 [thread overview]
Message-ID: <20200511140504.GL2678@redhat.com> (raw)
In-Reply-To: <xkqeo8qwhd4u.fsf@trodgers.remote>
On 09/05/20 17:01 -0700, Thomas Rodgers via Libstdc++ wrote:
>* Note, this patch supersedes my previous atomic wait and semaphore
>patches.
>
>Add support for -
> atomic wait/notify_one/notify_all
> counting_semaphore
> binary_semaphore
> latch
>
> * include/Makefile.am (bits_headers): Add new header.
> * include/Makefile.in: Regenerate.
> * include/bits/atomic_base.h (__atomic_base<_Itp>:wait): Define.
Should be two colons before wait.
>diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in
>index eb437ad8d8d..e73ff8b3e64 100644
>--- a/libstdc++-v3/include/Makefile.in
>+++ b/libstdc++-v3/include/Makefile.in
Generated files don't need to be in the patch.
>diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h
>index 87fe0bd6000..b2cec0f1722 100644
>--- a/libstdc++-v3/include/bits/atomic_base.h
>+++ b/libstdc++-v3/include/bits/atomic_base.h
>@@ -37,6 +37,11 @@
> #include <bits/atomic_lockfree_defines.h>
> #include <bits/move.h>
>
>+#if __cplusplus > 201703L
>+#include <bits/atomic_wait.h>
>+#include <iostream>
<iostream> shouldn't be here (it adds runtime cost, as well as
compile-time).
>@@ -542,6 +546,30 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> __cmpexch_failure_order(__m));
> }
>
>+#if __cplusplus > 201703L
>+ _GLIBCXX_ALWAYS_INLINE void
>+ wait(__int_type __old, memory_order __m = memory_order_seq_cst) const noexcept
Please format everything to <= 80 columns (ideally < 80).
>+ wait(__pointer_type __old, memory_order __m = memory_order_seq_cst) noexcept
>+ {
>+ __atomic_wait(&_M_p, __old,
This should be qualified to prevent ADL.
>+ [__m, this, __old]()
>+ { return this->load(__m) != __old; });
>+ }
>+
>+ // TODO add const volatile overload
>+
>+ _GLIBCXX_ALWAYS_INLINE void
>+ notify_one() const noexcept
>+ { __atomic_notify(&_M_p, false); }
Qualify to prevent ADL here too, and all similar calls.
>+#if __cplusplus > 201703L
>+ template<typename _Tp>
>+ _GLIBCXX_ALWAYS_INLINE void
>+ wait(const _Tp* __ptr, _Val<_Tp> __old, memory_order __m = memory_order_seq_cst) noexcept
>+ {
>+ __atomic_wait(__ptr, *std::__addressof(__old),
Can't this just be __old instead of *std::__addressof(__old) ?
>+ [=]()
>+ { return load(__ptr, __m) == *std::__addressof(__old); });
Same here?
>diff --git a/libstdc++-v3/include/bits/atomic_timed_wait.h b/libstdc++-v3/include/bits/atomic_timed_wait.h
>new file mode 100644
>index 00000000000..10f0fe50ed9
>--- /dev/null
>+++ b/libstdc++-v3/include/bits/atomic_timed_wait.h
>@@ -0,0 +1,270 @@
>+// -*- C++ -*- header.
>+
>+// Copyright (C) 2020 Free Software Foundation, Inc.
>+//
>+// This file is part of the GNU ISO C++ Library. This library is free
>+// software; you can redistribute it and/or modify it under the
>+// terms of the GNU General Public License as published by the
>+// Free Software Foundation; either version 3, or (at your option)
>+// any later version.
>+
>+// This library is distributed in the hope that it will be useful,
>+// but WITHOUT ANY WARRANTY; without even the implied warranty of
>+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>+// GNU General Public License for more details.
>+
>+// Under Section 7 of GPL version 3, you are granted additional
>+// permissions described in the GCC Runtime Library Exception, version
>+// 3.1, as published by the Free Software Foundation.
>+
>+// You should have received a copy of the GNU General Public License and
>+// a copy of the GCC Runtime Library Exception along with this program;
>+// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
>+// <http://www.gnu.org/licenses/>.
>+
>+/** @file bits/atomic_timed_wait.h
>+ * This is an internal header file, included by other library headers.
>+ * Do not attempt to use it directly. @headername{atomic}
>+ */
>+
>+#ifndef _GLIBCXX_ATOMIC_TIMED_WAIT_H
>+#define _GLIBCXX_ATOMIC_TIMED_WAIT_H 1
>+
>+#pragma GCC system_header
>+
>+#include <bits/c++config.h>
>+#include <bits/functional_hash.h>
>+#include <bits/atomic_wait.h>
>+
>+#include <chrono>
>+
>+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
>+#include <sys/time.h>
>+#endif
>+
>+namespace std _GLIBCXX_VISIBILITY(default)
>+{
>+ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>+ enum class __atomic_wait_status { __no_timeout, __timeout };
Blank line before and after this enum definition please.
>+ namespace __detail
>+ {
>+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
>+ enum
>+ {
>+ __futex_wait_bitset_private = __futex_wait_bitset | __futex_private_flag,
>+ __futex_wake_bitset_private = __futex_wake_bitset | __futex_private_flag,
>+ __futex_bitset_match_any = 0xffffffff
>+ };
>+
>+ using __platform_wait_clock_t = chrono::steady_clock;
Blank line after this using-decl please.
>+ template<typename _Duration>
>+ __atomic_wait_status
>+ __platform_wait_until_impl(__platform_wait_t* __addr, __platform_wait_t __val,
>+ const chrono::time_point<__platform_wait_clock_t, _Duration>& __atime) noexcept
>+ {
>+ auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
>+ auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
>+
Eventually we'll want to move the rest of this function (which doesn't
depend on the template argument) into the compiled library, but it's
better to be header-only for now.
>+ struct timespec __rt =
>+ {
>+ static_cast<std::time_t>(__s.time_since_epoch().count()),
>+ static_cast<long>(__ns.count())
>+ };
>+
>+ auto __e = syscall (SYS_futex, __addr, __futex_wait_bitset_private, __val, &__rt,
>+ nullptr, __futex_bitset_match_any);
>+ if (__e && !(errno == EINTR || errno == EAGAIN || errno == ETIMEDOUT))
>+ std::terminate();
>+ return (__platform_wait_clock_t::now() < __atime)
>+ ? __atomic_wait_status::__no_timeout : __atomic_wait_status::__timeout;
>+ }
>+
>+ template<typename _Clock, typename _Duration>
>+ __atomic_wait_status
>+ __platform_wait_until(__platform_wait_t* __addr, __platform_wait_t __val,
>+ const chrono::time_point<_Clock, _Duration>& __atime)
>+ {
>+ if constexpr (std::is_same<__platform_wait_clock_t, _Clock>::value)
This is C++20 so you can use is_same_v here, which uses the intrinsic
directly and avoids instantiating the is_same class template.
>+ {
>+ return __platform_wait_until_impl(__addr, __val, __atime);
>+ }
>+ else
>+ {
>+ const typename _Clock::time_point __c_entry = _Clock::now();
>+ const __platform_wait_clock_t::time_point __s_entry =
>+ __platform_wait_clock_t::now();
>+ const auto __delta = __atime - __c_entry;
>+ const auto __s_atime = __s_entry + __delta;
>+ if (__platform_wait_until_impl(__addr, __val, __s_atime) == __atomic_wait_status::__no_timeout)
>+ return __atomic_wait_status::__no_timeout;
>+
>+ // We got a timeout when measured against __clock_t but
>+ // we need to check against the caller-supplied clock
>+ // to tell whether we should return a timeout.
>+ if (_Clock::now() < __atime)
>+ return __atomic_wait_status::__no_timeout;
>+ return __atomic_wait_status::__timeout;
>+ }
>+ }
>+#endif
>+
>+#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT
>+ template<typename _Duration>
>+ __atomic_wait_status
>+ __cond_wait_until_impl(__gthread_cond_t* __cv, std::unique_lock<std::mutex>& __lock,
>+ const chrono::time_point<std::chrono::steady_clock, _Duration>& __atime)
The std:: qualification here isn't needed (and doesn't help with
keeping the line below 80 cols).
>+ {
>+ auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
>+ auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
>+
>+ __gthread_time_t __ts =
>+ {
>+ static_cast<std::time_t>(__s.time_since_epoch().count()),
>+ static_cast<long>(__ns.count())
>+ };
>+
>+ pthread_cond_clockwait(__cv, __lock.mutex()->native_handle(),
>+ CLOCK_MONOTONIC,
>+ &__ts);
>+ return (chrono::steady_clock::now() < __atime)
>+ ? __atomic_wait_status::__no_timeout : __atomic_wait_status::__timeout;
>+ }
>+#endif
>+
>+ template<typename _Duration>
>+ __atomic_wait_status
>+ __cond_wait_until_impl(__gthread_cond_t* __cv, std::unique_lock<std::mutex>& __lock,
>+ const chrono::time_point<chrono::system_clock, _Duration>& __atime)
>+ {
>+ auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
>+ auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
>+
>+ __gthread_time_t __ts =
>+ {
>+ static_cast<std::time_t>(__s.time_since_epoch().count()),
>+ static_cast<long>(__ns.count())
>+ };
>+
>+ __gthread_cond_timedwait(__cv, __lock.mutex()->native_handle(),
>+ &__ts);
>+ return (chrono::system_clock::now() < __atime)
>+ ? __atomic_wait_status::__no_timeout : __atomic_wait_status::__timeout;
>+ }
>+
>+ // return true if timeout
>+ template<typename _Clock, typename _Duration>
>+ __atomic_wait_status
>+ __cond_wait_until(__gthread_cond_t* __cv, std::unique_lock<std::mutex>& __lock,
>+ const chrono::time_point<_Clock, _Duration>& __atime)
>+ {
>+#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT
>+ using __clock_t = chrono::steady_clock;
>+#else
>+ using __clock_t = chrono::system_clock;
>+#endif
>+ const typename _Clock::time_point __c_entry = _Clock::now();
>+ const __clock_t::time_point __s_entry = __clock_t::now();
>+ const auto __delta = __atime - __c_entry;
>+ const auto __s_atime = __s_entry + __delta;
>+ if (__cond_wait_until_impl(__cv, __lock, __s_atime))
>+ return __atomic_wait_status::__no_timeout;
>+ // We got a timeout when measured against __clock_t but
>+ // we need to check against the caller-supplied clock
>+ // to tell whether we should return a timeout.
>+ if (_Clock::now() < __atime)
>+ return __atomic_wait_status::__no_timeout;
>+ return __atomic_wait_status::__timeout;
>+ }
>+
>+ struct __timed_waiters : __waiters
>+ {
>+ template<typename _Clock, typename _Duration>
>+ __atomic_wait_status
>+ _M_do_wait_until(int32_t __version,
>+ const chrono::time_point<_Clock, _Duration>& __atime)
>+ {
>+ int32_t __cur = 0;
>+ __waiters::__lock_t __l(_M_mtx);
>+ while (__cur <= __version)
>+ {
>+ if (__cond_wait_until(&_M_cv, __l, __atime) == __atomic_wait_status::__timeout)
>+ return __atomic_wait_status::__timeout;
>+
>+ int32_t __last = __cur;
>+ __atomic_load(&_M_ver, &__cur, __ATOMIC_ACQUIRE);
>+ if (__cur < __last)
>+ break; // break the loop if version overflows
>+ }
>+ return __atomic_wait_status::__no_timeout;
>+ }
>+
>+ static __timed_waiters&
>+ _S_timed_for(void* __t)
>+ {
>+ static_assert(sizeof(__timed_waiters) == sizeof(__waiters));
>+ return (__timed_waiters&) __waiters::_S_for(__t);
>+ }
>+ };
>+ } // namespace __detail
>+
>+ template<typename _Tp, typename _Pred,
>+ typename _Clock, typename _Duration>
>+ bool
>+ __atomic_wait_until(const _Tp* __addr, _Tp __old, _Pred __pred,
>+ const chrono::time_point<_Clock, _Duration>& __atime) noexcept
>+ {
>+ using namespace __detail;
>+
>+ if (__atomic_spin(__pred))
Qualify to prevent ADL.
>+ return true;
>+
>+ auto& __w = __timed_waiters::_S_timed_for((void*)__addr);
>+ auto __version = __w._M_enter_wait();
>+ do
>+ {
>+ __atomic_wait_status __res;
>+ if constexpr (__platform_wait_uses_type<_Tp>::__value)
>+ {
>+ __res = __platform_wait_until((__platform_wait_t*)(void*) __addr, __old,
>+ __atime);
>+ }
>+ else
>+ {
>+ __res = __w._M_do_wait_until(__version, __atime);
>+ }
>+ if (__res == __atomic_wait_status::__timeout)
>+ return false;
>+ }
>+ while (!__pred() && __atime < _Clock::now());
>+ __w._M_leave_wait();
>+
>+ // if timed out, return false
>+ return (_Clock::now() < __atime);
>+ }
>+
>+ template<typename _Tp, typename _Pred,
>+ typename _Rep, typename _Period>
>+ bool
>+ __atomic_wait_for(const _Tp* __addr, _Tp __old, _Pred __pred,
>+ const chrono::duration<_Rep, _Period>& __rtime) noexcept
>+ {
>+ using namespace __detail;
>+
>+ if (__atomic_spin(__pred))
>+ return true;
>+
>+ if (!__rtime.count())
>+ return false; // no rtime supplied, and spin did not acquire
>+
>+ using __dur = chrono::steady_clock::duration;
>+ auto __reltime = chrono::duration_cast<__dur>(__rtime);
>+ if (__reltime < __rtime)
>+ ++__reltime;
>+
>+
>+ return __atomic_wait_until(__addr, __old, std::move(__pred),
>+ chrono::steady_clock::now() + __reltime);
>+ }
>+_GLIBCXX_END_NAMESPACE_VERSION
>+} // namespace std
>+#endif
>diff --git a/libstdc++-v3/include/bits/atomic_wait.h b/libstdc++-v3/include/bits/atomic_wait.h
>new file mode 100644
>index 00000000000..32070a54f40
>--- /dev/null
>+++ b/libstdc++-v3/include/bits/atomic_wait.h
>@@ -0,0 +1,280 @@
>+// -*- C++ -*- header.
>+
>+// Copyright (C) 2020 Free Software Foundation, Inc.
>+//
>+// This file is part of the GNU ISO C++ Library. This library is free
>+// software; you can redistribute it and/or modify it under the
>+// terms of the GNU General Public License as published by the
>+// Free Software Foundation; either version 3, or (at your option)
>+// any later version.
>+
>+// This library is distributed in the hope that it will be useful,
>+// but WITHOUT ANY WARRANTY; without even the implied warranty of
>+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>+// GNU General Public License for more details.
>+
>+// Under Section 7 of GPL version 3, you are granted additional
>+// permissions described in the GCC Runtime Library Exception, version
>+// 3.1, as published by the Free Software Foundation.
>+
>+// You should have received a copy of the GNU General Public License and
>+// a copy of the GCC Runtime Library Exception along with this program;
>+// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
>+// <http://www.gnu.org/licenses/>.
>+
>+/** @file bits/atomic_wait.h
>+ * This is an internal header file, included by other library headers.
>+ * Do not attempt to use it directly. @headername{atomic}
>+ */
>+
>+#ifndef _GLIBCXX_ATOMIC_WAIT_H
>+#define _GLIBCXX_ATOMIC_WAIT_H 1
>+
>+#pragma GCC system_header
>+
>+#include <bits/c++config.h>
>+#include <bits/functional_hash.h>
>+#include <bits/gthr.h>
>+#include <bits/std_mutex.h>
>+#include <bits/unique_lock.h>
>+
>+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
>+#include <climits>
>+#include <unistd.h>
>+#include <syscall.h>
>+#endif
>+
>+#define _GLIBCXX_SPIN_COUNT_1 16
>+#define _GLIBCXX_SPIN_COUNT_2 12
>+
>+// TODO get this from Autoconf
>+#define _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE 1
>+
>+namespace std _GLIBCXX_VISIBILITY(default)
>+{
>+_GLIBCXX_BEGIN_NAMESPACE_VERSION
>+ namespace __detail
>+ {
>+ using __platform_wait_t = int;
>+
>+ template<class _Tp>
This should be typename not class.
>+ struct __platform_wait_uses_type
>+ {
>+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
>+ enum { __value = std::is_same<typename std::remove_cv<_Tp>::type,
This should be remove_cv_t.
>+ __platform_wait_t>::value };
>+#else
>+ enum { __value = std::false_type::value };
>+#endif
There's no need to use the C++03 enum hack here, it should just derive
from true_type or false_type.
template<typename _Tp>
struct __platform_wait_uses_type
#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
: is_same<std::remove_cv_t<_Tp>, __platform_wait_t>
#else
: false_type
#endif
{ };
Or better yet, just use a variable template:
template<typename _Tp>
inline constexpr bool __platform_wait_uses_type
#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
= is_same_v<std::remove_cv_t<_Tp>, __platform_wait_t>;
#else
= false;
#endif
>+ };
>+
>+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
>+ enum
>+ {
>+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE
>+ __futex_private_flag = 128,
>+#else
>+ __futex_private_flag = 0,
>+#endif
>+ __futex_wait = 0,
>+ __futex_wake = 1,
>+ __futex_wait_bitset = 9,
>+ __futex_wake_bitset = 10,
>+ __futex_wait_private = __futex_wait | __futex_private_flag,
>+ __futex_wake_private = __futex_wake | __futex_private_flag
>+ };
>+
>+ void
>+ __platform_wait(__platform_wait_t* __addr, __platform_wait_t __val) noexcept
>+ {
>+ auto __e = syscall (SYS_futex, __addr, __futex_wait_private, __val, nullptr);
>+ if (__e && !(errno == EINTR || errno == EAGAIN))
>+ std::terminate();
>+ }
>+
>+ void
>+ __platform_notify(__platform_wait_t* __addr, bool __all) noexcept
>+ {
>+ syscall (SYS_futex, __addr, __futex_wake_private, __all ? INT_MAX : 1);
>+ }
>+#endif
>+
>+ struct alignas(64) __waiters
Isn't alignas(64) already implied by the first data member?
>+ {
>+ int32_t alignas(64) _M_ver = 0;
>+ int32_t alignas(64) _M_wait = 0;
>+
>+ // TODO make this used only where we don't have futexes
Don't we always need these even with futexes, for the types that don't
use a futex?
>+ using __lock_t = std::unique_lock<std::mutex>;
>+ mutable __lock_t::mutex_type _M_mtx;
>+
>+#ifdef __GTHREAD_COND_INIT
>+ mutable __gthread_cond_t _M_cv = __GTHREAD_COND_INIT;
>+ __waiters() noexcept = default;
If we moved std::condition_variable into its own header (or
<bits/std_mutex.h>, could we reuse that here instead of using
__gthread_cond_t directly?
>+#else
>+ mutable __gthread_cond_t _M_cv;
>+ __waiters() noexcept
>+ {
>+ __GTHREAD_COND_INIT_FUNCTION(&_M_cond);
>+ }
>+#endif
>+
>+ int32_t
>+ _M_enter_wait() noexcept
>+ {
>+ int32_t __res;
>+ __atomic_load(&_M_ver, &__res, __ATOMIC_ACQUIRE);
>+ __atomic_fetch_add(&_M_wait, 1, __ATOMIC_ACQ_REL);
>+ return __res;
>+ }
>+
>+ void
>+ _M_leave_wait() noexcept
>+ {
>+ __atomic_fetch_sub(&_M_wait, 1, __ATOMIC_ACQ_REL);
>+ }
>+
>+ void
>+ _M_do_wait(int32_t __version) const noexcept
>+ {
>+ int32_t __cur = 0;
>+ while (__cur <= __version)
>+ {
>+ __waiters::__lock_t __l(_M_mtx);
>+ auto __e = __gthread_cond_wait(&_M_cv, __l.mutex()->native_handle());
>+ if (__e)
>+ std::terminate();
>+ int32_t __last = __cur;
>+ __atomic_load(&_M_ver, &__cur, __ATOMIC_ACQUIRE);
>+ if (__cur < __last)
>+ break; // break the loop if version overflows
>+ }
>+ }
>+
>+ int32_t
>+ _M_waiting() const noexcept
>+ {
>+ int32_t __res;
>+ __atomic_load(&_M_wait, &__res, __ATOMIC_ACQUIRE);
>+ return __res;
>+ }
>+
>+ void
>+ _M_notify(bool __all) noexcept
>+ {
>+ __atomic_fetch_add(&_M_ver, 1, __ATOMIC_ACQ_REL);
>+ auto __e = __gthread_cond_broadcast(&_M_cv);
>+ if (__e)
>+ __throw_system_error(__e);
>+ }
>+
>+ static __waiters&
>+ _S_for(void* __t)
>+ {
>+ const unsigned char __mask = 0xf;
>+ static __waiters __w[__mask + 1];
>+
>+ auto __key = _Hash_impl::hash(__t) & __mask;
>+ return __w[__key];
>+ }
>+ };
>+
>+ struct __waiter
>+ {
>+ __waiters& _M_w;
>+ int32_t _M_version;
>+
>+ template<typename _Tp>
>+ __waiter(const _Tp* __addr) noexcept
>+ : _M_w(__waiters::_S_for((void*) __addr))
>+ , _M_version(_M_w._M_enter_wait())
>+ { }
>+
>+ ~__waiter()
>+ { _M_w._M_leave_wait(); }
>+
>+ void _M_do_wait() noexcept
>+ { _M_w._M_do_wait(_M_version); }
>+ };
>+
>+ void
>+ __thread_relax() noexcept
>+ {
>+#if defined __i386__ || defined __x86_64__
>+ __builtin_ia32_pause();
>+#elif defined _GLIBCXX_USE_SCHED_YIELD
>+ __gthread_yield();
>+#endif
>+ }
>+
>+ void
>+ __thread_yield() noexcept
>+ {
>+#if defined _GLIBCXX_USE_SCHED_YIELD
>+ __gthread_yield();
>+#endif
>+ }
>+
>+ } // namespace __detail
>+
>+ template<class _Pred>
s/class/template/
>+ bool
>+ __atomic_spin(_Pred __pred) noexcept
>+ {
>+ for (auto __i = 0; __i < _GLIBCXX_SPIN_COUNT_1; ++__i)
>+ {
>+ if (__pred())
>+ return true;
>+
>+ if (__i < _GLIBCXX_SPIN_COUNT_2)
>+ __detail::__thread_relax();
>+ else
>+ __detail::__thread_yield();
>+ }
>+ return false;
>+ }
>+
>+ template<class _Tp, class _Pred>
s/class/template/
>+ void
>+ __atomic_wait(const _Tp* __addr, _Tp __old, _Pred __pred) noexcept
>+ {
>+ using namespace __detail;
>+ if (__atomic_spin(__pred))
>+ return;
>+
>+ __waiter __w(__addr);
>+ while (!__pred())
>+ {
>+ if constexpr (__platform_wait_uses_type<_Tp>::__value)
>+ {
>+ __platform_wait((__platform_wait_t*)(void*) __addr, __old);
>+ }
>+ else
>+ {
>+ // TODO support timed backoff when this can be moved into the lib
>+ __w._M_do_wait();
>+ }
>+ }
>+ }
>+
>+ template<class _Tp>
s/class/template/
>+ void
>+ __atomic_notify(const _Tp* __addr, bool __all) noexcept
>+ {
>+ using namespace __detail;
>+ auto& __w = __waiters::_S_for((void*)__addr);
>+ if (!__w._M_waiting())
When __platform_wait_uses_type<_Tp> is true, will __w._M_waiting()
ever be true? Won't this always return before notifying?
Is there meant to be a __waiter constructed here?
>+ return;
>+
>+ if constexpr (__platform_wait_uses_type<_Tp>::__value)
>+ {
>+ __platform_notify((__platform_wait_t*)(void*) __addr, __all);
>+ }
>+ else
>+ {
>+ __w._M_notify(__all);
>+ }
>+ }
>+_GLIBCXX_END_NAMESPACE_VERSION
>+} // namespace std
>+#endif
>diff --git a/libstdc++-v3/include/bits/semaphore_base.h b/libstdc++-v3/include/bits/semaphore_base.h
>new file mode 100644
>index 00000000000..b3c83bbc70b
>--- /dev/null
>+++ b/libstdc++-v3/include/bits/semaphore_base.h
>@@ -0,0 +1,270 @@
>+// -*- C++ -*- header.
>+
>+// Copyright (C) 2020 Free Software Foundation, Inc.
>+//
>+// This file is part of the GNU ISO C++ Library. This library is free
>+// software; you can redistribute it and/or modify it under the
>+// terms of the GNU General Public License as published by the
>+// Free Software Foundation; either version 3, or (at your option)
>+// any later version.
>+
>+// This library is distributed in the hope that it will be useful,
>+// but WITHOUT ANY WARRANTY; without even the implied warranty of
>+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>+// GNU General Public License for more details.
>+
>+// Under Section 7 of GPL version 3, you are granted additional
>+// permissions described in the GCC Runtime Library Exception, version
>+// 3.1, as published by the Free Software Foundation.
>+
>+// You should have received a copy of the GNU General Public License and
>+// a copy of the GCC Runtime Library Exception along with this program;
>+// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
>+// <http://www.gnu.org/licenses/>.
>+
>+/** @file bits/semaphore.h
Should be bits/semaphore_base.h
>+ * This is an internal header file, included by other library headers.
>+ * Do not attempt to use it directly. @headername{atomic}
Should be @headername{semaphore}
>+ */
>+
>+#ifndef _GLIBCXX_SEMAPHORE_BASE_H
>+#define _GLIBCXX_SEMAPHORE_BASE_H 1
>+
>+#pragma GCC system_header
>+
>+#include <bits/c++config.h>
>+#include <bits/atomic_base.h>
>+#include <bits/atomic_timed_wait.h>
>+
>+#if defined _POSIX_SEMAPHORES && __has_include(<semaphore.h>)
>+#define _GLIBCXX_HAVE_POSIX_SEMAPHORE 1
>+#include <semaphore.h>
>+#endif
>+
>+#include <chrono>
>+#include <type_traits>
>+#include <limits>
<ext/numeric_traits.h> is much smaller than <limits> and should be
used for limits of integer types. (I recently added
<bits/int_limits.h> too but that was a mistake that I need to fix).
>+namespace std _GLIBCXX_VISIBILITY(default)
>+{
>+_GLIBCXX_BEGIN_NAMESPACE_VERSION
>+
>+#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
>+ template<ptrdiff_t __least_max_t>
__least_max_t isn't a type so shouldn't have the _t suffix.
>+ struct __platform_semaphore
>+ {
>+ using __clock_t = chrono::system_clock;
>+
>+ __platform_semaphore(ptrdiff_t __count) noexcept
Should this constructor be explicit?
>+ {
>+ static_assert( __least_max_t <= SEM_VALUE_MAX, "__least_max_t > SEM_VALUE_MAX");
Our static_assert messages should state the positive condition, not
the negative one. So it should be "__least_max_t <= SEM_VALUE_MAX",
which is what the real condition is anyway, so you might as well omit
the string literal.
>+ auto __e = sem_init(&_M_semaphore, 0, __count);
>+ if (__e)
>+ std::terminate();
>+ }
>+
>+ ~__platform_semaphore()
>+ {
>+ auto __e = sem_destroy(&_M_semaphore);
>+ if (__e)
>+ std::terminate();
>+ }
>+
>+ _GLIBCXX_ALWAYS_INLINE void
>+ acquire() noexcept
>+ {
>+ auto __err = sem_wait(&_M_semaphore);
>+ if (__err)
>+ std::terminate();
>+ }
>+
>+ template<typename _Duration>
>+ _GLIBCXX_ALWAYS_INLINE bool
Do we really need this to be always_inline?
>+ __try_acquire_until_impl(const chrono::time_point<__clock_t>& __atime) noexcept
>+ {
>+ auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
>+ auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
>+
>+ struct timespec __ts =
>+ {
>+ static_cast<std::time_t>(__s.time_since_epoch().count()),
>+ static_cast<long>(__ns.count())
>+ };
>+
>+ auto __err = sem_timedwait(&_M_semaphore, &__ts);
>+ if (__err && (errno == ETIMEDOUT))
>+ return false;
>+ else if (__err)
>+ std::terminate();
>+ return true;
>+ }
>+
>+ template<typename _Clock, typename _Duration>
>+ _GLIBCXX_ALWAYS_INLINE bool
always_inline?
>+ try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept
>+ {
>+ if constexpr (std::is_same<__clock_t, _Clock>::value)
is_same_v
>+ {
>+ return __try_acquire_until_impl(__atime);
>+ }
>+ else
>+ {
>+ const typename _Clock::time_point __c_entry = _Clock::now();
>+ const __clock_t __s_entry = __clock_t::now();
>+ const auto __delta = __atime - __c_entry;
>+ const auto __s_atime = __s_entry + __delta;
>+ if (__try_acquire_until_impl(__s_atime))
>+ return true;
>+
>+ // We got a timeout when measured against __clock_t but
>+ // we need to check against the caller-supplied clock
>+ // to tell whether we should return a timeout.
>+ return (_Clock::now() < __atime);
>+ }
>+ }
>+
>+ template<typename _Rep, typename _Period>
>+ _GLIBCXX_ALWAYS_INLINE bool
>+ try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) noexcept
>+ { return try_acquire_until(__clock_t::now() + __rtime); }
>+
>+ template<typename _Clock, typename _Duration>
>+ _GLIBCXX_ALWAYS_INLINE void
>+ release(ptrdiff_t __update) noexcept
>+ {
>+ do
>+ {
>+ auto __err = sem_post(&_M_semaphore);
>+ if (__err)
>+ std::terminate();
>+ } while (--__update);
>+ }
>+
>+ private:
>+ sem_t _M_semaphore;
>+ };
>+#endif // _GLIBCXX_HAVE_POSIX_SEMAPHORE
>+
>+ template<typename _Tp>
>+ struct __atomic_semaphore
>+ {
>+ static constexpr size_t _S_alignment = __alignof__(_Tp);
>+
>+ __atomic_semaphore(_Tp __count)
Should this be explicit?
>+ : _M_a(__count)
>+ { }
>+
>+ _GLIBCXX_ALWAYS_INLINE void
>+ acquire() noexcept
>+ {
>+ auto const __pred = [this]
>+ {
>+ auto __old = __atomic_impl::load(&this->_M_a, memory_order::acquire);
>+ if (__old == 0)
>+ return false;
>+ return __atomic_impl::compare_exchange_strong(&this->_M_a,
>+ __old, __old - 1,
>+ memory_order::acquire,
>+ memory_order::release);
>+ };
>+ auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed);
>+ __atomic_wait(&_M_a, __old, __pred);
>+ }
>+
>+ bool
>+ try_acquire() noexcept
>+ {
>+ auto __old = __atomic_impl::load(&_M_a, memory_order::acquire);
>+ if (__old == 0)
>+ return false;
>+
>+ return __atomic_spin([this, &__old]
>+ {
>+ return __atomic_impl::compare_exchange_weak(&this->_M_a,
>+ __old, __old - 1,
>+ memory_order::acquire,
>+ memory_order::release);
>+ });
>+ }
>+
>+ template<typename _Clock, typename _Duration>
>+ _GLIBCXX_ALWAYS_INLINE bool
>+ try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept
>+ {
>+ auto const __pred = [this]
>+ {
>+ auto __old = __atomic_impl::load(&this->_M_a, memory_order::acquire);
>+ if (__old == 0)
>+ return false;
>+ return __atomic_impl::compare_exchange_strong(&this->_M_a,
>+ __old, __old - 1,
>+ memory_order::acquire,
>+ memory_order::release);
>+ };
>+
>+ auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed);
>+ return __atomic_wait_until(&_M_a, __old, __pred, __atime);
>+ }
>+
>+ template<typename _Rep, typename _Period>
>+ _GLIBCXX_ALWAYS_INLINE bool
>+ try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) noexcept
>+ {
>+ auto const __pred = [this]
>+ {
>+ auto __old = __atomic_impl::load(&this->_M_a, memory_order::acquire);
>+ if (__old == 0)
>+ return false;
>+ return __atomic_impl::compare_exchange_strong(&this->_M_a,
>+ __old, __old - 1,
>+ memory_order::acquire,
>+ memory_order::release);
>+ };
>+
>+ auto __old = __atomic_impl::load(&_M_a, memory_order_relaxed);
>+ return __atomic_wait_for(&_M_a, __old, __pred, __rtime);
>+ }
>+
>+ _GLIBCXX_ALWAYS_INLINE void
>+ release(ptrdiff_t __update) noexcept
>+ {
>+ if (0 < __atomic_impl::fetch_add(&_M_a, __update, memory_order_release))
>+ return;
>+ if (__update > 1)
>+ __atomic_impl::notify_all(&_M_a);
>+ else
>+ __atomic_impl::notify_one(&_M_a);
>+ }
>+
>+ private:
>+ alignas(_S_alignment) _Tp _M_a;
Could this just use alignas(__alignof__(_Tp)) _Tp here? There's no
need for the _S_alignment constant if it's only used in one place.
>+ };
>+
>+#ifdef _GLIBCXX_REQUIRE_POSIX_SEMAPHORE
>+ template<ptrdiff_t __least_max_t>
Rename __least_max_t here too.
>+ using __semaphore_base = __platform_semaphore<__least_max_t>;
>+#else
>+# ifdef _GLIBCXX_HAVE_LINUX_FUTEX
>+ template<ptrdiff_t __least_max_t>
>+ using __semaphore_base = std::conditional<(__least_max_t > 0
This should use conditional_t<> not conditional<>::type.
The least-max_value can't be negative. If it's zero, can't we use a
futex or semaphore? So the '__least_max_t > 0' condition is wrong?
>+ && __least_max_t < std::numeric_limits<__detail::__platform_wait_t>::max()),
Should that be <= rather than < ?
>+ __atomic_semaphore<__detail::__platform_wait_t>,
>+ __atomic_semaphore<ptrdiff_t>>::type;
>+ // __platform_semaphore
>+# else
>+# ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
Please use '#elif defined _GLIBCXX_HAVE_POSIX_SEMAPHORE' here to avoid
an extra level of #if nesting.
>+ template<ptrdiff_t __least_max_t>
>+ using __semaphore_base = std::conditional<(__least_max_t > 0 && __least_max_t <= SEM_VALUE_MAX),
>+ __platform_semaphore<__least_max_t>,
>+ __atomic_semaphore<ptrdiff_t>>::type;
>+# else
>+ template<ptrdiff_t __least_max_t>
>+ using __semaphore_base = __atomic_semaphore<ptrdiff_t>;
>+# endif
>+# endif
>+#endif
>+
>+_GLIBCXX_END_NAMESPACE_VERSION
>+} // namespace std
>+
>+#endif
>diff --git a/libstdc++-v3/include/std/atomic b/libstdc++-v3/include/std/atomic
>index a455286a784..3f18774031d 100644
>--- a/libstdc++-v3/include/std/atomic
>+++ b/libstdc++-v3/include/std/atomic
>@@ -163,6 +163,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> compare_exchange_strong(bool& __i1, bool __i2,
> memory_order __m = memory_order_seq_cst) volatile noexcept
> { return _M_base.compare_exchange_strong(__i1, __i2, __m); }
>+
>+#if __cplusplus > 201703L
>+ void wait(bool __old, memory_order __m = memory_order_seq_cst) const noexcept
>+ { _M_base.wait(__old, __m); }
>+
>+ // TODO add const volatile overload
>+
>+ void notify_one() const noexcept
>+ { _M_base.notify_one(); }
>+
>+ void notify_all() const noexcept
>+ { _M_base.notify_all(); }
>+#endif
> };
>
> #if __cplusplus <= 201703L
>@@ -352,6 +365,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> memory_order __m = memory_order_seq_cst) volatile noexcept
> { return compare_exchange_strong(__e, __i, __m,
> __cmpexch_failure_order(__m)); }
>+#if __cplusplus > 201703L
>+ void wait(_Tp __old, memory_order __m = memory_order_seq_cst) noexcept
>+ { _M_i.wait(__old, __m); }
>+
>+ // TODO add const volatile overload
>+
>+ void notify_one() const noexcept
>+ { _M_i.notify_one(); }
>+
>+ void notify_all() const noexcept
>+ { _M_i.notify_all(); }
>+#endif
>+
> };
> #undef _GLIBCXX20_INIT
>
>@@ -590,6 +616,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> __cmpexch_failure_order(__m));
> }
>
>+#if __cplusplus > 201703L
>+ void wait(__pointer_type __old, memory_order __m = memory_order_seq_cst) noexcept
>+ { _M_b.wait(__old, __m); }
>+
>+ // TODO add const volatile overload
>+
>+ void notify_one() const noexcept
>+ { _M_b.notify_one(); }
>+
>+ void notify_all() const noexcept
>+ { _M_b.notify_all(); }
>+#endif
> __pointer_type
> fetch_add(ptrdiff_t __d,
> memory_order __m = memory_order_seq_cst) noexcept
>@@ -1342,6 +1380,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> memory_order_seq_cst);
> }
>
>+
>+#if __cplusplus > 201703L
>+ template<typename _Tp>
>+ inline void atomic_wait(const atomic<_Tp>* __a,
>+ typename std::atomic<_Tp>::value_type __old) noexcept
>+ { __a->wait(__old); }
>+
>+ template<typename _Tp>
>+ inline void atomic_wait_explicit(const atomic<_Tp>* __a,
>+ typename std::atomic<_Tp>::value_type __old,
>+ std::memory_order __m) noexcept
>+ { __a->wait(__old, __m); }
>+
>+ template<typename _Tp>
>+ inline void atomic_notify_one(atomic<_Tp>* __a) noexcept
>+ { __a->notify_one(); }
>+
>+ template<typename _Tp>
>+ inline void atomic_notify_all(atomic<_Tp>* __a) noexcept
>+ { __a->notify_all(); }
>+
>+#endif // C++2a
>+
> // Function templates for atomic_integral and atomic_pointer operations only.
> // Some operations (and, or, xor) are only available for atomic integrals,
> // which is implemented by taking a parameter of type __atomic_base<_ITp>*.
>diff --git a/libstdc++-v3/include/std/latch b/libstdc++-v3/include/std/latch
>new file mode 100644
>index 00000000000..0099877416e
>--- /dev/null
>+++ b/libstdc++-v3/include/std/latch
>@@ -0,0 +1,91 @@
>+//<latch> -*- C++ -*-
A space before <latch>.
>+
>+// Copyright (C) 2020 Free Software Foundation, Inc.
>+//
>+// This file is part of the GNU ISO C++ Library. This library is free
>+// software; you can redistribute it and/or modify it under the
>+// terms of the GNU General Public License as published by the
>+// Free Software Foundation; either version 3, or (at your option)
>+// any later version.
>+
>+// This library is distributed in the hope that it will be useful,
>+// but WITHOUT ANY WARRANTY; without even the implied warranty of
>+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>+// GNU General Public License for more details.
>+
>+// Under Section 7 of GPL version 3, you are granted additional
>+// permissions described in the GCC Runtime Library Exception, version
>+// 3.1, as published by the Free Software Foundation.
>+
>+// You should have received a copy of the GNU General Public License and
>+// a copy of the GCC Runtime Library Exception along with this program;
>+// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
>+// <http://www.gnu.org/licenses/>.
>+
>+/** @file include/latch
>+ * This is a Standard C++ Library header.
Align "This" with "@file" here.
>+ */
>+
>+#ifndef _GLIBCXX_LATCH
>+#define _GLIBCXX_LATCH
>+
>+#pragma GCC system_header
>+
>+#if __cplusplus > 201703L
>+#define __cpp_lib_latch 201907L
>+
>+#include <bits/atomic_base.h>
>+#include <limits>
Use <ext/numeric_traits.h> here too.
>+namespace std _GLIBCXX_VISIBILITY(default)
>+{
>+_GLIBCXX_BEGIN_NAMESPACE_VERSION
>+
>+ class latch
>+ {
>+ static constexpr size_t _S_alignment = __alignof__(ptrdiff_t);
>+ public:
>+ static constexpr
>+ _GLIBCXX_ALWAYS_INLINE ptrdiff_t
>+ max() noexcept
>+ { return numeric_limits<ptrdiff_t>::max(); }
>+
>+ constexpr explicit latch(ptrdiff_t __expected) : _M_a(__expected) { }
>+
>+ ~latch() = default;
>+ latch(const latch&) = delete;
>+ latch& operator=(const latch&) = delete;
>+
>+ _GLIBCXX_ALWAYS_INLINE void
>+ count_down(ptrdiff_t __update = 1)
>+ {
>+ auto const __old = __atomic_impl::fetch_sub(&_M_a, __update, memory_order::release);
>+ if (__old == __update)
>+ __atomic_impl::notify_all(&_M_a);
>+ }
>+
>+ _GLIBCXX_ALWAYS_INLINE bool
>+ try_wait() const noexcept
>+ { return __atomic_impl::load(&_M_a, memory_order::acquire) == 0; }
>+
>+ _GLIBCXX_ALWAYS_INLINE void
>+ wait() const
>+ {
>+ auto const __old = __atomic_impl::load(&_M_a, memory_order::acquire);
>+ __atomic_wait(&_M_a, __old, [this] { return this->try_wait(); });
>+ }
>+
>+ _GLIBCXX_ALWAYS_INLINE void
>+ arrive_and_wait(ptrdiff_t __update = 1)
>+ {
>+ count_down();
>+ wait();
>+ }
>+
>+ private:
>+ alignas(_S_alignment) ptrdiff_t _M_a;
Just use __alignof__ directly here and get rid of _S_alignment?
>+ };
>+_GLIBCXX_END_NAMESPACE_VERSION
>+} // namespace
>+#endif // __cplusplus > 201703L
>+#endif // _GLIBCXX_LATCH
>diff --git a/libstdc++-v3/include/std/semaphore b/libstdc++-v3/include/std/semaphore
>new file mode 100644
>index 00000000000..b51940b46ac
>--- /dev/null
>+++ b/libstdc++-v3/include/std/semaphore
>@@ -0,0 +1,81 @@
>+//<semaphore> -*- C++ -*-
A space before <semaphore>.
>+
>+// Copyright (C) 2020 Free Software Foundation, Inc.
>+//
>+// This file is part of the GNU ISO C++ Library. This library is free
>+// software; you can redistribute it and/or modify it under the
>+// terms of the GNU General Public License as published by the
>+// Free Software Foundation; either version 3, or (at your option)
>+// any later version.
>+
>+// This library is distributed in the hope that it will be useful,
>+// but WITHOUT ANY WARRANTY; without even the implied warranty of
>+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>+// GNU General Public License for more details.
>+
>+// Under Section 7 of GPL version 3, you are granted additional
>+// permissions described in the GCC Runtime Library Exception, version
>+// 3.1, as published by the Free Software Foundation.
>+
>+// You should have received a copy of the GNU General Public License and
>+// a copy of the GCC Runtime Library Exception along with this program;
>+// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
>+// <http://www.gnu.org/licenses/>.
>+
>+/** @file include/semaphore
>+ * This is a Standard C++ Library header.
Align "This" with "@file" here.
>+ */
>+
>+#ifndef _GLIBCXX_SEMAPHORE
>+#define _GLIBCXX_SEMAPHORE
>+
>+#pragma GCC system_header
>+
>+#if __cplusplus > 201703L
>+#define __cpp_lib_semaphore 201907L
>+#include <bits/semaphore_base.h>
>+
>+namespace std _GLIBCXX_VISIBILITY(default)
>+{
>+_GLIBCXX_BEGIN_NAMESPACE_VERSION
>+
>+ template<ptrdiff_t __least_max_value = std::numeric_limits<ptrdiff_t>::max()>
>+ class counting_semaphore
>+ {
I don't see a static_assert making it ill-formed to use a negative
value for __least_max_value. Is that enforced somewhere else?
The standard says it's ill-formed, so we should also have a _neg.cc
test checking that we reject it.
>+ __semaphore_base<__least_max_value> _M_sem;
Blank line after this please.
>+ public:
>+ explicit counting_semaphore(ptrdiff_t __desired) noexcept
>+ : _M_sem(__desired)
>+ { }
>+
>+ ~counting_semaphore() = default;
>+
>+ counting_semaphore(const counting_semaphore&) = delete;
>+ counting_semaphore& operator=(const counting_semaphore&) = delete;
>+
>+ static constexpr ptrdiff_t max() noexcept
>+ { return __least_max_value; }
>+
>+ void release(ptrdiff_t __update = 1)
>+ { _M_sem.release(__update); }
>+
>+ void acquire()
>+ { _M_sem.acquire(); }
>+
>+ bool try_acquire() noexcept
>+ { return _M_sem.try_acquire(); }
>+
>+ template<class _Rep, class _Period>
s/class/template/
>+ bool try_acquire_for(const std::chrono::duration<_Rep, _Period>& __rel_time)
>+ { return _M_sem.try_acquire_for(__rel_time); }
>+
>+ template<class _Clock, class _Duration>
s/class/template/
>+ bool try_acquire_until(const std::chrono::time_point<_Clock, _Duration>& __abs_time)
>+ { return _M_sem.try_acquire_until(__abs_time); }
>+ };
>+
>+ using binary_semaphore = std::counting_semaphore<1>;
>+_GLIBCXX_END_NAMESPACE_VERSION
>+} // namespace
>+#endif // __cplusplus > 201703L
>+#endif // _GLIBCXX_SEMAPHORE
>diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version
>index c3a5bd26e63..390990282b0 100644
>--- a/libstdc++-v3/include/std/version
>+++ b/libstdc++-v3/include/std/version
>@@ -188,6 +188,8 @@
> #endif
> #define __cpp_lib_type_identity 201806L
> #define __cpp_lib_unwrap_ref 201811L
>+#define __cpp_lib_semaphore 201907L
>+#define __cpp_lib_latch 201907L
These features aren't supported in a freestanding implementation, so
should be in the #if _GLIBCXX_HOSTED block (and the macros should be
in alphabetical order).
>
> #if _GLIBCXX_HOSTED
> #undef __cpp_lib_array_constexpr
>diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc
>new file mode 100644
>index 00000000000..1ced9d44b20
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc
>@@ -0,0 +1,103 @@
>+// { dg-options "-std=gnu++2a -pthread -latomic -L../../libatomic/.libs" }
Use { dg-add-options libatomic } instead of adding -latomic -L...
next prev parent reply other threads:[~2020-05-11 14:05 UTC|newest]
Thread overview: 43+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-05-10 0:01 Thomas Rodgers
2020-05-11 14:05 ` Jonathan Wakely [this message]
2020-05-11 15:43 ` Thomas Rodgers
2020-05-11 17:05 ` Jonathan Wakely
2020-05-11 20:59 ` Thomas Rodgers
2020-05-23 22:52 ` Thomas Rodgers
2020-05-24 17:41 ` Thomas Rodgers
2020-06-06 0:29 ` Thomas Rodgers
2020-07-08 16:43 ` Jonathan Wakely
2020-08-03 14:09 ` Jonathan Wakely
2020-08-03 20:19 ` Jonathan Wakely
2020-09-03 0:47 ` Thomas Rodgers
2020-09-03 0:54 ` Thomas Rodgers
2020-09-11 23:58 ` [PATCH] libstdc++: " Thomas Rodgers
2020-09-28 13:25 ` Jonathan Wakely
2020-10-01 23:37 ` Thomas Rodgers
2020-10-02 15:40 ` Thomas Rodgers
2020-10-05 22:54 ` [PATCH] t/trodgers/c2a_synchronization Thomas Rodgers
2020-10-23 10:28 ` Jonathan Wakely
2020-10-26 21:48 ` [PATCH] libstdc++: Add C++2a synchronization support Thomas Rodgers
2020-10-27 10:23 ` Jonathan Wakely
2020-11-20 22:44 ` Thomas Rodgers
2020-11-22 21:13 ` Stephan Bergmann
2020-11-23 18:33 ` Jonathan Wakely
2020-11-22 21:41 ` Stephan Bergmann
2020-11-23 18:32 ` Jonathan Wakely
2020-11-21 15:16 ` Andreas Schwab
2020-11-21 17:04 ` Jonathan Wakely
2020-11-21 17:39 ` Jonathan Wakely
2020-11-22 0:36 ` H.J. Lu
2020-11-23 14:50 ` Jonathan Wakely
2020-11-24 23:45 ` Jonathan Wakely
2020-11-25 1:07 ` Jonathan Wakely
2020-11-25 10:35 ` Jonathan Wakely
2020-11-25 12:32 ` Jonathan Wakely
2020-11-25 18:39 ` Jonathan Wakely
2020-11-26 16:26 ` Jonathan Wakely
2020-11-23 16:08 ` Jonathan Wakely
2020-09-28 13:30 ` Jonathan Wakely
2020-09-28 13:36 ` Jonathan Wakely
2020-09-28 21:29 ` Thomas Rodgers
2020-09-29 9:44 ` Jonathan Wakely
2020-06-06 21:24 [PATCH] " Thomas Rodgers
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20200511140504.GL2678@redhat.com \
--to=jwakely@redhat.com \
--cc=gcc-patches@gcc.gnu.org \
--cc=libstdc++@gcc.gnu.org \
--cc=trodgers@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).