* [PATCH] Add C++2a synchronization support
@ 2020-05-10 0:01 Thomas Rodgers
2020-05-11 14:05 ` Jonathan Wakely
0 siblings, 1 reply; 50+ messages in thread
From: Thomas Rodgers @ 2020-05-10 0:01 UTC (permalink / raw)
To: gcc-patches, libstdc++
[-- Attachment #1: Type: text/plain, Size: 3028 bytes --]
* 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.
(__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/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
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: le patch --]
[-- Type: text/x-patch, Size: 82165 bytes --]
From 436ab6fd5286a6467792263fbfbd603ba0f0c04d Mon Sep 17 00:00:00 2001
From: Thomas Rodgers <rodgert@appliantology.com>
Date: Mon, 6 Apr 2020 17:58:47 -0700
Subject: [PATCH] Add C++2a synchronization support
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/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.
---
libstdc++-v3/include/Makefile.am | 5 +
libstdc++-v3/include/Makefile.in | 5 +
libstdc++-v3/include/bits/atomic_base.h | 162 +++++++++-
libstdc++-v3/include/bits/atomic_timed_wait.h | 270 +++++++++++++++++
libstdc++-v3/include/bits/atomic_wait.h | 280 ++++++++++++++++++
libstdc++-v3/include/bits/semaphore_base.h | 270 +++++++++++++++++
libstdc++-v3/include/std/atomic | 61 ++++
libstdc++-v3/include/std/latch | 91 ++++++
libstdc++-v3/include/std/semaphore | 81 +++++
libstdc++-v3/include/std/version | 2 +
.../atomic/wait_notify/atomic_refs.cc | 103 +++++++
.../29_atomics/atomic/wait_notify/bool.cc | 59 ++++
.../29_atomics/atomic/wait_notify/floats.cc | 32 ++
.../29_atomics/atomic/wait_notify/generic.h | 88 ++++++
.../atomic/wait_notify/integrals.cc | 56 ++++
.../29_atomics/atomic/wait_notify/pointers.cc | 59 ++++
libstdc++-v3/testsuite/30_threads/latch/1.cc | 27 ++
libstdc++-v3/testsuite/30_threads/latch/2.cc | 27 ++
libstdc++-v3/testsuite/30_threads/latch/3.cc | 50 ++++
.../testsuite/30_threads/semaphore/1.cc | 27 ++
.../testsuite/30_threads/semaphore/2.cc | 27 ++
.../30_threads/semaphore/try_acquire.cc | 55 ++++
.../30_threads/semaphore/try_acquire_for.cc | 85 ++++++
.../30_threads/semaphore/try_acquire_futex.cc | 51 ++++
.../30_threads/semaphore/try_acquire_posix.cc | 169 +++++++++++
.../30_threads/semaphore/try_acquire_until.cc | 94 ++++++
26 files changed, 2235 insertions(+), 1 deletion(-)
create mode 100644 libstdc++-v3/include/bits/atomic_timed_wait.h
create mode 100644 libstdc++-v3/include/bits/atomic_wait.h
create mode 100644 libstdc++-v3/include/bits/semaphore_base.h
create mode 100644 libstdc++-v3/include/std/latch
create mode 100644 libstdc++-v3/include/std/semaphore
create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc
create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc
create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc
create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h
create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc
create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/latch/1.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/latch/2.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/latch/3.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/1.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/2.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_futex.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc
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\
${std_srcdir}/limits \
${std_srcdir}/list \
${std_srcdir}/locale \
@@ -69,6 +70,7 @@ std_headers = \
${std_srcdir}/ratio \
${std_srcdir}/regex \
${std_srcdir}/scoped_allocator \
+ ${std_srcdir}/semaphore \
${std_srcdir}/set \
${std_srcdir}/shared_mutex \
${std_srcdir}/span \
@@ -100,6 +102,8 @@ bits_headers = \
${bits_srcdir}/allocated_ptr.h \
${bits_srcdir}/allocator.h \
${bits_srcdir}/atomic_base.h \
+ ${bits_srcdir}/atomic_wait.h \
+ ${bits_srcdir}/atomic_timed_wait.h \
${bits_srcdir}/atomic_futex.h \
${bits_srcdir}/basic_ios.h \
${bits_srcdir}/basic_ios.tcc \
@@ -174,6 +178,7 @@ bits_headers = \
${bits_srcdir}/regex_compiler.tcc \
${bits_srcdir}/regex_executor.h \
${bits_srcdir}/regex_executor.tcc \
+ ${bits_srcdir}/semaphore_base.h \
${bits_srcdir}/shared_ptr.h \
${bits_srcdir}/shared_ptr_atomic.h \
${bits_srcdir}/shared_ptr_base.h \
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
@@ -397,6 +397,7 @@ std_headers = \
${std_srcdir}/iostream \
${std_srcdir}/istream \
${std_srcdir}/iterator \
+ ${std_srcdir}/latch\
${std_srcdir}/limits \
${std_srcdir}/list \
${std_srcdir}/locale \
@@ -414,6 +415,7 @@ std_headers = \
${std_srcdir}/ratio \
${std_srcdir}/regex \
${std_srcdir}/scoped_allocator \
+ ${std_srcdir}/semaphore \
${std_srcdir}/set \
${std_srcdir}/shared_mutex \
${std_srcdir}/span \
@@ -445,6 +447,8 @@ bits_headers = \
${bits_srcdir}/allocated_ptr.h \
${bits_srcdir}/allocator.h \
${bits_srcdir}/atomic_base.h \
+ ${bits_srcdir}/atomic_wait.h \
+ ${bits_srcdir}/atomic_timed_wait.h \
${bits_srcdir}/atomic_futex.h \
${bits_srcdir}/basic_ios.h \
${bits_srcdir}/basic_ios.tcc \
@@ -519,6 +523,7 @@ bits_headers = \
${bits_srcdir}/regex_compiler.tcc \
${bits_srcdir}/regex_executor.h \
${bits_srcdir}/regex_executor.tcc \
+ ${bits_srcdir}/semaphore_base.h \
${bits_srcdir}/shared_ptr.h \
${bits_srcdir}/shared_ptr_atomic.h \
${bits_srcdir}/shared_ptr_base.h \
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>
+#endif
+
#ifndef _GLIBCXX_ALWAYS_INLINE
#define _GLIBCXX_ALWAYS_INLINE inline __attribute__((__always_inline__))
#endif
@@ -134,7 +139,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
return __ret;
}
-
// Base types for atomics.
template<typename _IntTp>
struct __atomic_base;
@@ -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
+ {
+ __atomic_wait(&_M_i, __old,
+ [__m, this, __old]()
+ { return this->load(__m) != __old; });
+ }
+
+ // TODO add const volatile overload
+
+ _GLIBCXX_ALWAYS_INLINE void
+ notify_one() const noexcept
+ { __atomic_notify(&_M_i, false); }
+
+ // TODO add const volatile overload
+
+ _GLIBCXX_ALWAYS_INLINE void
+ notify_all() const noexcept
+ { __atomic_notify(&_M_i, true); }
+
+ // TODO add const volatile overload
+#endif // C++2a
+
_GLIBCXX_ALWAYS_INLINE __int_type
fetch_add(__int_type __i,
memory_order __m = memory_order_seq_cst) noexcept
@@ -803,6 +831,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
+ {
+ __atomic_wait(&_M_p, __old,
+ [__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); }
+
+ // TODO add const volatile overload
+
+ _GLIBCXX_ALWAYS_INLINE void
+ notify_all() const noexcept
+ { __atomic_notify(&_M_p, true); }
+
+ // TODO add const volatile overload
+#endif // C++2a
+
_GLIBCXX_ALWAYS_INLINE __pointer_type
fetch_add(ptrdiff_t __d,
memory_order __m = memory_order_seq_cst) noexcept
@@ -891,6 +943,33 @@ _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
+ {
+ __atomic_wait(__ptr, *std::__addressof(__old),
+ [=]()
+ { return load(__ptr, __m) == *std::__addressof(__old); });
+ }
+
+ // TODO add const volatile overload
+
+ template<typename _Tp>
+ _GLIBCXX_ALWAYS_INLINE void
+ notify_one(const _Tp* __ptr) noexcept
+ { __atomic_notify(__ptr, false); }
+
+ // TODO add const volatile overload
+
+ template<typename _Tp>
+ _GLIBCXX_ALWAYS_INLINE void
+ notify_all(const _Tp* __ptr) noexcept
+ { __atomic_notify(__ptr, true); }
+
+ // TODO add const volatile overload
+#endif // C++2a
+
template<typename _Tp>
_GLIBCXX_ALWAYS_INLINE _Tp
fetch_add(_Tp* __ptr, _Diff<_Tp> __i, memory_order __m) noexcept
@@ -1144,6 +1223,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
value_type
fetch_add(value_type __i,
memory_order __m = memory_order_seq_cst) noexcept
@@ -1281,6 +1377,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); }
+
private:
_Tp* _M_ptr;
};
@@ -1376,6 +1488,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); }
+
value_type
fetch_add(value_type __i,
memory_order __m = memory_order_seq_cst) const noexcept
@@ -1531,6 +1659,22 @@ _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_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); }
+
value_type
fetch_add(value_type __i,
memory_order __m = memory_order_seq_cst) const noexcept
@@ -1640,6 +1784,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); }
+
_GLIBCXX_ALWAYS_INLINE value_type
fetch_add(difference_type __d,
memory_order __m = memory_order_seq_cst) const noexcept
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 };
+ 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;
+ 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<__platform_wait_clock_t, _Clock>::value)
+ {
+ 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)
+ {
+ 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))
+ 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>
+ struct __platform_wait_uses_type
+ {
+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
+ enum { __value = std::is_same<typename std::remove_cv<_Tp>::type,
+ __platform_wait_t>::value };
+#else
+ enum { __value = std::false_type::value };
+#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
+ {
+ 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
+ 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;
+#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>
+ 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>
+ 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>
+ 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>::__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
+ * This is an internal header file, included by other library headers.
+ * Do not attempt to use it directly. @headername{atomic}
+ */
+
+#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>
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
+ template<ptrdiff_t __least_max_t>
+ struct __platform_semaphore
+ {
+ using __clock_t = chrono::system_clock;
+
+ __platform_semaphore(ptrdiff_t __count) noexcept
+ {
+ static_assert( __least_max_t <= SEM_VALUE_MAX, "__least_max_t > SEM_VALUE_MAX");
+ 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
+ __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
+ try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept
+ {
+ if constexpr (std::is_same<__clock_t, _Clock>::value)
+ {
+ 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)
+ : _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;
+ };
+
+#ifdef _GLIBCXX_REQUIRE_POSIX_SEMAPHORE
+ template<ptrdiff_t __least_max_t>
+ 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
+ && __least_max_t < std::numeric_limits<__detail::__platform_wait_t>::max()),
+ __atomic_semaphore<__detail::__platform_wait_t>,
+ __atomic_semaphore<ptrdiff_t>>::type;
+ // __platform_semaphore
+# else
+# ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
+ 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++ -*-
+
+// 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.
+ */
+
+#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>
+
+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;
+ };
+_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++ -*-
+
+// 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.
+ */
+
+#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
+ {
+ __semaphore_base<__least_max_value> _M_sem;
+ 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>
+ 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>
+ 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
#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" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <atomic>
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+#include <chrono>
+#include <type_traits>
+
+#include <testsuite_hooks.h>
+
+template<typename Tp>
+Tp check_wait_notify(Tp val1, Tp val2)
+{
+ using namespace std::literals::chrono_literals;
+
+ std::mutex m;
+ std::condition_variable cv;
+
+ Tp aa = val1;
+ std::atomic_ref<Tp> a(aa);
+ std::thread t([&]
+ {
+ cv.notify_one();
+ a.wait(val1);
+ if (a.load() != val2)
+ a = val1;
+ });
+ std::unique_lock<std::mutex> l(m);
+ cv.wait(l);
+ std::this_thread::sleep_for(100ms);
+ a.store(val2);
+ a.notify_one();
+ t.join();
+ return a.load();
+}
+
+template<typename Tp,
+ bool = std::is_integral_v<Tp>
+ || std::is_floating_point_v<Tp>>
+struct check;
+
+template<typename Tp>
+struct check<Tp, true>
+{
+ check()
+ {
+ Tp a = 0;
+ Tp b = 42;
+ VERIFY(check_wait_notify(a, b) == b);
+ }
+};
+
+template<typename Tp>
+struct check<Tp, false>
+{
+ check(Tp b)
+ {
+ Tp a;
+ VERIFY(check_wait_notify(a, b) == b);
+ }
+};
+
+struct foo
+{
+ long a = 0;
+ long b = 0;
+
+ foo& operator=(foo const&) = default;
+
+ friend bool
+ operator==(foo const& rhs, foo const& lhs)
+ { return rhs.a == lhs.a && rhs.b == lhs.b; }
+};
+
+int
+main ()
+{
+ check<long>();
+ check<double>();
+ check<foo>({42, 48});
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc
new file mode 100644
index 00000000000..b9fc063c66f
--- /dev/null
+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc
@@ -0,0 +1,59 @@
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <atomic>
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+#include <type_traits>
+#include <chrono>
+
+#include <testsuite_hooks.h>
+
+int
+main ()
+{
+ using namespace std::literals::chrono_literals;
+
+ std::mutex m;
+ std::condition_variable cv;
+
+ std::atomic<bool> a(false);
+ std::atomic<bool> b(false);
+ std::thread t([&]
+ {
+ cv.notify_one();
+ a.wait(false);
+ if (a.load())
+ {
+ b.store(true);
+ }
+ });
+ std::unique_lock<std::mutex> l(m);
+ cv.wait(l);
+ std::this_thread::sleep_for(100ms);
+ a.store(true);
+ a.notify_one();
+ t.join();
+ VERIFY( b.load() );
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc
new file mode 100644
index 00000000000..1d032085752
--- /dev/null
+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc
@@ -0,0 +1,32 @@
+// { dg-options "-std=gnu++2a -pthread -latomic -L../../libatomic/.libs" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include "generic.h"
+
+int
+main ()
+{
+ check<float> f;
+ check<double> d;
+ check<long double> l;
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h
new file mode 100644
index 00000000000..0da374ece87
--- /dev/null
+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h
@@ -0,0 +1,88 @@
+// -*- 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <atomic>
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+#include <chrono>
+
+#include <testsuite_hooks.h>
+
+template<typename Tp>
+Tp check_wait_notify(Tp val1, Tp val2)
+{
+ using namespace std::literals::chrono_literals;
+
+ std::mutex m;
+ std::condition_variable cv;
+
+ std::atomic<Tp> a(val1);
+ std::thread t([&]
+ {
+ cv.notify_one();
+ a.wait(val1);
+ if (a.load() != val2)
+ a = val1;
+ });
+ std::unique_lock<std::mutex> l(m);
+ cv.wait(l);
+ std::this_thread::sleep_for(100ms);
+ a.store(val2);
+ a.notify_one();
+ t.join();
+ return a.load();
+}
+
+template<typename Tp>
+Tp check_atomic_wait_notify(Tp val1, Tp val2)
+{
+ using namespace std::literals::chrono_literals;
+
+ std::mutex m;
+ std::condition_variable cv;
+
+ std::atomic<Tp> a(val1);
+ std::thread t([&]
+ {
+ cv.notify_one();
+ std::atomic_wait(&a, val1);
+ if (a.load() != val2)
+ a = val1;
+ });
+ std::unique_lock<std::mutex> l(m);
+ cv.wait(l);
+ std::this_thread::sleep_for(100ms);
+ a.store(val2);
+ std::atomic_notify_one(&a);
+ t.join();
+ return a.load();
+}
+
+template<typename Tp>
+struct check
+{
+ check()
+ {
+ Tp a = 0;
+ Tp b = 42;
+ VERIFY(check_wait_notify(a, b) == b);
+ VERIFY(check_atomic_wait_notify(a, b) == b);
+ }
+};
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc
new file mode 100644
index 00000000000..2afd19a7d14
--- /dev/null
+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc
@@ -0,0 +1,56 @@
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include "generic.h"
+
+int
+main ()
+{
+ // check<bool> bb;
+ check<char> ch;
+ check<signed char> sch;
+ check<unsigned char> uch;
+ check<short> s;
+ check<unsigned short> us;
+ check<int> i;
+ check<unsigned int> ui;
+ check<long> l;
+ check<unsigned long> ul;
+ check<long long> ll;
+ check<unsigned long long> ull;
+
+ check<wchar_t> wch;
+ check<char8_t> ch8;
+ check<char16_t> ch16;
+ check<char32_t> ch32;
+
+ check<int8_t> i8;
+ check<int16_t> i16;
+ check<int32_t> i32;
+ check<int64_t> i64;
+
+ check<uint8_t> u8;
+ check<uint16_t> u16;
+ check<uint32_t> u32;
+ check<uint64_t> u64;
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc
new file mode 100644
index 00000000000..8531bb2e788
--- /dev/null
+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc
@@ -0,0 +1,59 @@
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <atomic>
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+#include <type_traits>
+#include <chrono>
+
+#include <testsuite_hooks.h>
+
+int
+main ()
+{
+ using namespace std::literals::chrono_literals;
+
+ std::mutex m;
+ std::condition_variable cv;
+
+ long aa;
+ long bb;
+
+ std::atomic<long*> a(nullptr);
+ std::thread t([&]
+ {
+ cv.notify_one();
+ a.wait(nullptr);
+ if (a.load() == &aa)
+ a.store(&bb);
+ });
+ std::unique_lock<std::mutex> l(m);
+ cv.wait(l);
+ std::this_thread::sleep_for(100ms);
+ a.store(&aa);
+ a.notify_one();
+ t.join();
+ VERIFY( a.load() == &bb);
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/30_threads/latch/1.cc b/libstdc++-v3/testsuite/30_threads/latch/1.cc
new file mode 100644
index 00000000000..aa203cdf525
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/latch/1.cc
@@ -0,0 +1,27 @@
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <latch>
+
+#ifndef __cpp_lib_latch
+# error "Feature-test macro for latch missing in <latch>"
+#elif __cpp_lib_latch!= 201907L
+# error "Feature-test macro for latch has wrong value in <latch>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/latch/2.cc b/libstdc++-v3/testsuite/30_threads/latch/2.cc
new file mode 100644
index 00000000000..756727f33b3
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/latch/2.cc
@@ -0,0 +1,27 @@
+// Copyright (C) 2019-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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <version>
+
+#ifndef __cpp_lib_latch
+# error "Feature-test macro for latch missing in <version>"
+#elif __cpp_lib_latch != 201907L
+# error "Feature-test macro for latch has wrong value in <version>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/latch/3.cc b/libstdc++-v3/testsuite/30_threads/latch/3.cc
new file mode 100644
index 00000000000..10bb500d261
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/latch/3.cc
@@ -0,0 +1,50 @@
+// Copyright (C) 2019-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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+//
+#include <latch>
+#include <atomic>
+#include <thread>
+#include <testsuite_hooks.h>
+
+int main()
+{
+ std::atomic<int> a(0);
+
+ std::latch l(3);
+
+ VERIFY( !l.try_wait() );
+
+ auto fn = [&]
+ {
+ ++a;
+ l.count_down();
+ };
+
+ std::thread t0(fn);
+ std::thread t1(fn);
+
+ l.arrive_and_wait();
+ t0.join();
+ t1.join();
+
+ VERIFY( l.try_wait() );
+}
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/1.cc b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc
new file mode 100644
index 00000000000..1bbca687fc3
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc
@@ -0,0 +1,27 @@
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <semaphore>
+
+#ifndef __cpp_lib_semaphore
+# error "Feature-test macro for semaphore missing in <semaphore>"
+#elif __cpp_lib_semaphore != 201907L
+# error "Feature-test macro for semaphore has wrong value in <semaphore>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/2.cc b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc
new file mode 100644
index 00000000000..b96b8a59c64
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc
@@ -0,0 +1,27 @@
+// Copyright (C) 2019-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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <version>
+
+#ifndef __cpp_lib_semaphore
+# error "Feature-test macro for semaphore missing in <version>"
+#elif __cpp_lib_semaphore != 201907L
+# error "Feature-test macro for semaphore has wrong value in <version>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc
new file mode 100644
index 00000000000..d38cef86cfc
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc
@@ -0,0 +1,55 @@
+// Copyright (C) 2019-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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <semaphore>
+#include <limits>
+#include <cstddef>
+#include <testsuite_hooks.h>
+
+void test01()
+{
+ std::counting_semaphore<10> s(3);
+
+ s.acquire();
+ VERIFY( s.try_acquire() );
+ VERIFY( s.try_acquire() );
+ VERIFY( !s.try_acquire() );
+ s.release();
+ VERIFY( s.try_acquire() );
+}
+
+void test02()
+{
+ std::binary_semaphore s(1);
+
+ s.acquire();
+ VERIFY( !s.try_acquire() );
+ s.release();
+ VERIFY( s.try_acquire() );
+}
+
+
+int main()
+{
+ test01();
+ test02();
+}
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc
new file mode 100644
index 00000000000..965554a3c28
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc
@@ -0,0 +1,85 @@
+// Copyright (C) 2019-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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <semaphore>
+#include <chrono>
+#include <thread>
+#include <atomic>
+#include <testsuite_hooks.h>
+
+void test01()
+{
+ using namespace std::chrono_literals;
+ std::counting_semaphore<10> s(2);
+ s.acquire();
+
+ auto const dur = 250ms;
+ {
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( s.try_acquire_for(dur) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff < dur );
+ }
+
+ {
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( !s.try_acquire_for(dur) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff >= dur );
+ }
+}
+
+void test02()
+{
+ using namespace std::chrono_literals;
+ std::binary_semaphore s(1);
+ std::atomic<int> a(0), b(0);
+ std::thread t([&] {
+ a.wait(0);
+ auto const dur = 250ms;
+ VERIFY( !s.try_acquire_for(dur) );
+ b++;
+ b.notify_one();
+
+ a.wait(1);
+ VERIFY( s.try_acquire_for(dur) );
+ b++;
+ b.notify_one();
+ });
+ t.detach();
+
+ s.acquire();
+ a++;
+ a.notify_one();
+ b.wait(0);
+ s.release();
+ a++;
+ a.notify_one();
+
+ b.wait(1);
+}
+
+int main()
+{
+ test01();
+ test02();
+}
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_futex.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_futex.cc
new file mode 100644
index 00000000000..5e05606e97f
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_futex.cc
@@ -0,0 +1,51 @@
+// Copyright (C) 2019-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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <semaphore>
+#include <limits>
+#include <cstddef>
+#include <testsuite_hooks.h>
+
+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
+void test01()
+{
+ // the implementation optimizes for values of least_max_t that can fit
+ // in a futex, make sure we cover the case where least_max_t doesn't
+ auto constexpr least_max_t = std::numeric_limits<std::ptrdiff_t>::max();
+ std::counting_semaphore<least_max_t> s(3);
+
+ s.acquire();
+ VERIFY( s.try_acquire() );
+ VERIFY( s.try_acquire() );
+ VERIFY( !s.try_acquire() );
+ s.release();
+ VERIFY( s.try_acquire() );
+}
+
+#endif
+
+int main()
+{
+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
+ test01();
+#endif
+}
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc
new file mode 100644
index 00000000000..bf99fd3cf8f
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc
@@ -0,0 +1,169 @@
+// Copyright (C) 2019-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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <semaphore>
+#include <chrono>
+#include <thread>
+#include <atomic>
+#include <testsuite_hooks.h>
+
+#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
+ // The implementation supports posix as an implementation strategy
+ // make sure we cover that case
+#define _GLIBCXX_REQUIRE_POSIX_SEMAPHORE
+
+void test01()
+{
+ std::counting_semaphore<10> s(3);
+
+ s.acquire();
+ VERIFY( s.try_acquire() );
+ VERIFY( s.try_acquire() );
+ VERIFY( !s.try_acquire() );
+ s.release();
+ VERIFY( s.try_acquire() );
+}
+
+void test02()
+{
+ using namespace std::chrono_literals;
+ std::counting_semaphore<10> s(2);
+ s.acquire();
+
+ auto const dur = 250ms;
+ {
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( s.try_acquire_for(dur) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff < dur );
+ }
+
+ {
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( !s.try_acquire_for(dur) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff >= dur );
+ }
+}
+
+void test03()
+{
+ using namespace std::chrono_literals;
+ std::binary_semaphore s(1);
+ std::atomic<int> a(0), b(0);
+ std::thread t([&] {
+ a.wait(0);
+ auto const dur = 250ms;
+ VERIFY( !s.try_acquire_for(dur) );
+ b++;
+ b.notify_one();
+
+ a.wait(1);
+ VERIFY( s.try_acquire_for(dur) );
+ b++;
+ b.notify_one();
+ });
+ t.detach();
+
+ s.acquire();
+ a++;
+ a.notify_one();
+ b.wait(0);
+ s.release();
+ a++;
+ a.notify_one();
+
+ b.wait(1);
+}
+
+void test04()
+{
+ using namespace std::chrono_literals;
+ std::counting_semaphore<10> s(2);
+ s.acquire();
+
+ auto const dur = 250ms;
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( s.try_acquire_until(at) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff < dur );
+ }
+
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( !s.try_acquire_until(at) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff >= dur );
+ }
+}
+
+void test05()
+{
+ using namespace std::chrono_literals;
+ std::binary_semaphore s(1);
+ std::atomic<int> a(0), b(0);
+ std::thread t([&] {
+ a.wait(0);
+ auto const dur = 250ms;
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ VERIFY( !s.try_acquire_until(at) );
+
+ b++;
+ b.notify_one();
+ }
+
+ a.wait(1);
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ VERIFY( s.try_acquire_until(at) );
+ }
+ b++;
+ b.notify_one();
+ });
+ t.detach();
+
+ s.acquire();
+ a++;
+ a.notify_one();
+ b.wait(0);
+ s.release();
+ a++;
+ a.notify_one();
+
+ b.wait(1);
+}
+#endif
+
+int main()
+{
+#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
+ test01();
+ test02();
+ test03();
+ test04();
+ test05();
+#endif
+}
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc
new file mode 100644
index 00000000000..cc67c5c0bf0
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc
@@ -0,0 +1,94 @@
+// Copyright (C) 2019-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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <semaphore>
+#include <chrono>
+#include <thread>
+#include <atomic>
+#include <testsuite_hooks.h>
+
+void test01()
+{
+ using namespace std::chrono_literals;
+ std::counting_semaphore<10> s(2);
+ s.acquire();
+
+ auto const dur = 250ms;
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( s.try_acquire_until(at) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff < dur );
+ }
+
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( !s.try_acquire_until(at) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff >= dur );
+ }
+}
+
+void test02()
+{
+ using namespace std::chrono_literals;
+ std::binary_semaphore s(1);
+ std::atomic<int> a(0), b(0);
+ std::thread t([&] {
+ a.wait(0);
+ auto const dur = 250ms;
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ VERIFY( !s.try_acquire_until(at) );
+
+ b++;
+ b.notify_one();
+ }
+
+ a.wait(1);
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ VERIFY( s.try_acquire_until(at) );
+ }
+ b++;
+ b.notify_one();
+ });
+ t.detach();
+
+ s.acquire();
+ a++;
+ a.notify_one();
+ b.wait(0);
+ s.release();
+ a++;
+ a.notify_one();
+
+ b.wait(1);
+}
+
+int main()
+{
+ test01();
+ test02();
+}
--
2.26.2
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] Add C++2a synchronization support
2020-05-10 0:01 [PATCH] Add C++2a synchronization support Thomas Rodgers
@ 2020-05-11 14:05 ` Jonathan Wakely
2020-05-11 15:43 ` Thomas Rodgers
2020-05-11 20:59 ` Thomas Rodgers
0 siblings, 2 replies; 50+ messages in thread
From: Jonathan Wakely @ 2020-05-11 14:05 UTC (permalink / raw)
To: Thomas Rodgers; +Cc: gcc-patches, libstdc++
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...
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] Add C++2a synchronization support
2020-05-11 14:05 ` Jonathan Wakely
@ 2020-05-11 15:43 ` Thomas Rodgers
2020-05-11 17:05 ` Jonathan Wakely
2020-05-11 20:59 ` Thomas Rodgers
1 sibling, 1 reply; 50+ messages in thread
From: Thomas Rodgers @ 2020-05-11 15:43 UTC (permalink / raw)
To: Jonathan Wakely; +Cc: gcc-patches, libstdc++
Jonathan Wakely writes:
> On 09/05/20 17:01 -0700, Thomas Rodgers via Libstdc++ wrote:
<snip>
>>+#include <iostream>
>
> <iostream> shouldn't be here (it adds runtime cost, as well as
> compile-time).
>
Oversight, not removed after debugging it.
<snip>
>
> Can't this just be __old instead of *std::__addressof(__old) ?
>
Copypasta from elsewhere in the same class, I believe. I'll change it.
<snip>
>
> Isn't alignas(64) already implied by the first data member?
>
Yes
>>+ {
>>+ 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?
>
If we have futexes, we can use the address of _M_ver to wake
_M_do_wait() instead of using a condvar for types that don't use a
futex directly.
>>+ 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?
>
Yes, I started down that route initially, I could revisit it in a future
patch as part of also making it's use only necessary when the platform
doesn't support futex.
>>+ __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?
>
__waiter (an RAII type) is constructed in the __atomic_wait(), that
increments the _M_wait count on the way into the wait, and decrements it
on the way out, __atomic_notify checks to see if that count is non-zero
before invoking the platform/semaphore notify because it is cheaper
to do the atomic load than it is to make the syscall() when there are no
waiters.
>>+ return;
>>+
>>+ if constexpr (__platform_wait_uses_type<_Tp>::__value)
>>+ {
>>+ __platform_notify((__platform_wait_t*)(void*) __addr, __all);
>>+ }
<snip>
>>+ struct __platform_semaphore
>>+ {
>>+ using __clock_t = chrono::system_clock;
>>+
>>+ __platform_semaphore(ptrdiff_t __count) noexcept
>
> Should this constructor be explicit?
>
Yes.
>>+ template<typename _Duration>
>>+ _GLIBCXX_ALWAYS_INLINE bool
>
> Do we really need this to be always_inline?
>
Probably not, copypasta from elsewhere in the same file.
>>+ __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);
<snip>
>>+ template<typename _Tp>
>>+ struct __atomic_semaphore
>>+ {
>>+ static constexpr size_t _S_alignment = __alignof__(_Tp);
>>+
>>+ __atomic_semaphore(_Tp __count)
>
> Should this be explicit?
>
Yes.
>>+ 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.
>
Yes.
>>+ };
>>+
>>+#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?
>
Yes.
>>+ && __least_max_t < std::numeric_limits<__detail::__platform_wait_t>::max()),
>
> Should that be <= rather than < ?
>
Likely.
>>+ __atomic_semaphore<__detail::__platform_wait_t>,
>>+ __atomic_semaphore<ptrdiff_t>>::type;
>>+ // __platform_semaphore
>>+# else
<snip...>
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] Add C++2a synchronization support
2020-05-11 15:43 ` Thomas Rodgers
@ 2020-05-11 17:05 ` Jonathan Wakely
0 siblings, 0 replies; 50+ messages in thread
From: Jonathan Wakely @ 2020-05-11 17:05 UTC (permalink / raw)
To: Thomas Rodgers; +Cc: gcc-patches, libstdc++
On 11/05/20 08:43 -0700, Thomas Rodgers wrote:
>
>Jonathan Wakely writes:
>
>> On 09/05/20 17:01 -0700, Thomas Rodgers via Libstdc++ wrote:
>
><snip>
>
>>>+#include <iostream>
>>
>> <iostream> shouldn't be here (it adds runtime cost, as well as
>> compile-time).
>>
>Oversight, not removed after debugging it.
>
><snip>
>
>>
>> Can't this just be __old instead of *std::__addressof(__old) ?
>>
>Copypasta from elsewhere in the same class, I believe. I'll change it.
>
><snip>
>
>>
>> Isn't alignas(64) already implied by the first data member?
>>
>
>Yes
>
>>>+ {
>>>+ 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?
>>
>
>If we have futexes, we can use the address of _M_ver to wake
>_M_do_wait() instead of using a condvar for types that don't use a
>futex directly.
>
>>>+ 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?
>>
>Yes, I started down that route initially, I could revisit it in a future
>patch as part of also making it's use only necessary when the platform
>doesn't support futex.
>
>>>+ __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?
>>
>
>__waiter (an RAII type) is constructed in the __atomic_wait(), that
>increments the _M_wait count on the way into the wait, and decrements it
>on the way out, __atomic_notify checks to see if that count is non-zero
>before invoking the platform/semaphore notify because it is cheaper
>to do the atomic load than it is to make the syscall() when there are no
>waiters.
Doh, yes of course.
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] Add C++2a synchronization support
2020-05-11 14:05 ` Jonathan Wakely
2020-05-11 15:43 ` Thomas Rodgers
@ 2020-05-11 20:59 ` Thomas Rodgers
2020-05-23 22:52 ` Thomas Rodgers
1 sibling, 1 reply; 50+ messages in thread
From: Thomas Rodgers @ 2020-05-11 20:59 UTC (permalink / raw)
To: Jonathan Wakely; +Cc: gcc-patches, libstdc++
[-- Attachment #1: Type: text/plain, Size: 61 bytes --]
I *think* I have addressed everything in the attached patch.
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: updated patch --]
[-- Type: text/x-patch, Size: 79499 bytes --]
commit 24a989d2bf2158bdbe2511310d0583d0c6226f71
Author: Thomas Rodgers <rodgert@appliantology.com>
Date: Mon Apr 6 17:58:47 2020 -0700
Add C++2a synchronization support
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\
${std_srcdir}/limits \
${std_srcdir}/list \
${std_srcdir}/locale \
@@ -69,6 +70,7 @@ std_headers = \
${std_srcdir}/ratio \
${std_srcdir}/regex \
${std_srcdir}/scoped_allocator \
+ ${std_srcdir}/semaphore \
${std_srcdir}/set \
${std_srcdir}/shared_mutex \
${std_srcdir}/span \
@@ -100,6 +102,8 @@ bits_headers = \
${bits_srcdir}/allocated_ptr.h \
${bits_srcdir}/allocator.h \
${bits_srcdir}/atomic_base.h \
+ ${bits_srcdir}/atomic_wait.h \
+ ${bits_srcdir}/atomic_timed_wait.h \
${bits_srcdir}/atomic_futex.h \
${bits_srcdir}/basic_ios.h \
${bits_srcdir}/basic_ios.tcc \
@@ -174,6 +178,7 @@ bits_headers = \
${bits_srcdir}/regex_compiler.tcc \
${bits_srcdir}/regex_executor.h \
${bits_srcdir}/regex_executor.tcc \
+ ${bits_srcdir}/semaphore_base.h \
${bits_srcdir}/shared_ptr.h \
${bits_srcdir}/shared_ptr_atomic.h \
${bits_srcdir}/shared_ptr_base.h \
diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h
index 87fe0bd6000..73a8a77271e 100644
--- a/libstdc++-v3/include/bits/atomic_base.h
+++ b/libstdc++-v3/include/bits/atomic_base.h
@@ -37,6 +37,10 @@
#include <bits/atomic_lockfree_defines.h>
#include <bits/move.h>
+#if __cplusplus > 201703L
+#include <bits/atomic_wait.h>
+#endif
+
#ifndef _GLIBCXX_ALWAYS_INLINE
#define _GLIBCXX_ALWAYS_INLINE inline __attribute__((__always_inline__))
#endif
@@ -134,7 +138,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
return __ret;
}
-
// Base types for atomics.
template<typename _IntTp>
struct __atomic_base;
@@ -542,6 +545,31 @@ _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
+ {
+ std::__atomic_wait(&_M_i, __old,
+ [__m, this, __old]()
+ { return this->load(__m) != __old; });
+ }
+
+ // TODO add const volatile overload
+
+ _GLIBCXX_ALWAYS_INLINE void
+ notify_one() const noexcept
+ { std::__atomic_notify(&_M_i, false); }
+
+ // TODO add const volatile overload
+
+ _GLIBCXX_ALWAYS_INLINE void
+ notify_all() const noexcept
+ { std::__atomic_notify(&_M_i, true); }
+
+ // TODO add const volatile overload
+#endif // C++2a
+
_GLIBCXX_ALWAYS_INLINE __int_type
fetch_add(__int_type __i,
memory_order __m = memory_order_seq_cst) noexcept
@@ -803,6 +831,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
+ {
+ std::__atomic_wait(&_M_p, __old,
+ [__m, this, __old]()
+ { return this->load(__m) != __old; });
+ }
+
+ // TODO add const volatile overload
+
+ _GLIBCXX_ALWAYS_INLINE void
+ notify_one() const noexcept
+ { std::__atomic_notify(&_M_p, false); }
+
+ // TODO add const volatile overload
+
+ _GLIBCXX_ALWAYS_INLINE void
+ notify_all() const noexcept
+ { std::__atomic_notify(&_M_p, true); }
+
+ // TODO add const volatile overload
+#endif // C++2a
+
_GLIBCXX_ALWAYS_INLINE __pointer_type
fetch_add(ptrdiff_t __d,
memory_order __m = memory_order_seq_cst) noexcept
@@ -891,6 +943,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
+ {
+ std::__atomic_wait(__ptr, __old,
+ [=]() { return load(__ptr, __m) == __old; });
+ }
+
+ // TODO add const volatile overload
+
+ template<typename _Tp>
+ _GLIBCXX_ALWAYS_INLINE void
+ notify_one(const _Tp* __ptr) noexcept
+ { std::__atomic_notify(__ptr, false); }
+
+ // TODO add const volatile overload
+
+ template<typename _Tp>
+ _GLIBCXX_ALWAYS_INLINE void
+ notify_all(const _Tp* __ptr) noexcept
+ { std::__atomic_notify(__ptr, true); }
+
+ // TODO add const volatile overload
+#endif // C++2a
+
template<typename _Tp>
_GLIBCXX_ALWAYS_INLINE _Tp
fetch_add(_Tp* __ptr, _Diff<_Tp> __i, memory_order __m) noexcept
@@ -1144,6 +1222,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
value_type
fetch_add(value_type __i,
memory_order __m = memory_order_seq_cst) noexcept
@@ -1281,6 +1376,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); }
+
private:
_Tp* _M_ptr;
};
@@ -1376,6 +1487,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); }
+
value_type
fetch_add(value_type __i,
memory_order __m = memory_order_seq_cst) const noexcept
@@ -1531,6 +1658,22 @@ _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_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); }
+
value_type
fetch_add(value_type __i,
memory_order __m = memory_order_seq_cst) const noexcept
@@ -1640,6 +1783,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); }
+
_GLIBCXX_ALWAYS_INLINE value_type
fetch_add(difference_type __d,
memory_order __m = memory_order_seq_cst) const noexcept
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..691eed128a6
--- /dev/null
+++ b/libstdc++-v3/include/bits/atomic_timed_wait.h
@@ -0,0 +1,278 @@
+// -*- 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 };
+
+ 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;
+
+ 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);
+ }
+ 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))
+ 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 (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..1deacf02cce
--- /dev/null
+++ b/libstdc++-v3/include/bits/atomic_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_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
+
+// 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;
+
+ 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
+ {
+#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
+ {
+ 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
+ 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;
+#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<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);
+ }
+ 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..db5949e5b44
--- /dev/null
+++ b/libstdc++-v3/include/bits/semaphore_base.h
@@ -0,0 +1,272 @@
+// -*- 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_base.h
+ * This is an internal header file, included by other library headers.
+ * Do not attempt to use it directly. @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>
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
+ template<ptrdiff_t __least_max_value>
+ struct __platform_semaphore
+ {
+ using __clock_t = chrono::system_clock;
+
+ __platform_semaphore(ptrdiff_t __count) noexcept
+ {
+ static_assert( __least_max_value <= SEM_VALUE_MAX, "");
+ 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>
+ bool
+ __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>
+ bool
+ try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept
+ {
+ if constexpr (std::is_same<__clock_t, _Clock>::value)
+ {
+ 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
+ {
+ __atomic_semaphore(_Tp __count)
+ : _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(__alignof__(_Tp)) _Tp _M_a;
+ };
+
+#ifdef _GLIBCXX_REQUIRE_POSIX_SEMAPHORE
+ template<ptrdiff_t __least_max_value>
+ using __semaphore_base = __platform_semaphore<__least_max_value>;
+#else
+# ifdef _GLIBCXX_HAVE_LINUX_FUTEX
+ template<ptrdiff_t __least_max_value>
+ using __semaphore_base = conditional_t<(
+ __least_max_value >= 0
+ && __least_max_value <= __detail::__platform_wait_max_value),
+ __atomic_semaphore<__detail::__platform_wait_t>,
+ __atomic_semaphore<ptrdiff_t>>;
+
+// __platform_semaphore
+# elif defined _GLIBCXX_HAVE_POSIX_SEMAPHORE
+ template<ptrdiff_t __least_max_value>
+ using __semaphore_base = conditional_t<(
+ __least_max_value >= 0
+ && __least_max_value <= SEM_VALUE_MAX),
+ __platform_semaphore<__least_max_value>,
+ __atomic_semaphore<ptrdiff_t>>;
+# else
+ template<ptrdiff_t __least_max_value>
+ using __semaphore_base = __atomic_semaphore<ptrdiff_t>;
+# 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..aa5299d9fdd
--- /dev/null
+++ b/libstdc++-v3/include/std/latch
@@ -0,0 +1,90 @@
+// <latch> -*- C++ -*-
+
+// 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.
+ */
+
+#ifndef _GLIBCXX_LATCH
+#define _GLIBCXX_LATCH
+
+#pragma GCC system_header
+
+#if __cplusplus > 201703L
+#define __cpp_lib_latch 201907L
+
+#include <bits/atomic_base.h>
+#include <ext/numeric_traits.h>
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+ class latch
+ {
+ public:
+ static constexpr
+ _GLIBCXX_ALWAYS_INLINE ptrdiff_t
+ max() noexcept
+ { return __gnu_cxx::__numeric_traits<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(__alignof__(ptrdiff_t)) ptrdiff_t _M_a;
+ };
+_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..90cf3244647
--- /dev/null
+++ b/libstdc++-v3/include/std/semaphore
@@ -0,0 +1,86 @@
+// <semaphore> -*- C++ -*-
+
+// 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.
+ */
+
+#ifndef _GLIBCXX_SEMAPHORE
+#define _GLIBCXX_SEMAPHORE
+
+#pragma GCC system_header
+
+#if __cplusplus > 201703L
+#define __cpp_lib_semaphore 201907L
+#include <bits/semaphore_base.h>
+#include <ext/numeric_traits.h>
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+ template<ptrdiff_t __least_max_value =
+ __gnu_cxx::__numeric_traits<ptrdiff_t>::__max>
+ class counting_semaphore
+ {
+ static_assert(__least_max_value >=0, "");
+
+ __semaphore_base<__least_max_value> _M_sem;
+
+ 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>
+ 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>
+ 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
#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" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <atomic>
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+#include <chrono>
+#include <type_traits>
+
+#include <testsuite_hooks.h>
+
+template<typename Tp>
+Tp check_wait_notify(Tp val1, Tp val2)
+{
+ using namespace std::literals::chrono_literals;
+
+ std::mutex m;
+ std::condition_variable cv;
+
+ Tp aa = val1;
+ std::atomic_ref<Tp> a(aa);
+ std::thread t([&]
+ {
+ cv.notify_one();
+ a.wait(val1);
+ if (a.load() != val2)
+ a = val1;
+ });
+ std::unique_lock<std::mutex> l(m);
+ cv.wait(l);
+ std::this_thread::sleep_for(100ms);
+ a.store(val2);
+ a.notify_one();
+ t.join();
+ return a.load();
+}
+
+template<typename Tp,
+ bool = std::is_integral_v<Tp>
+ || std::is_floating_point_v<Tp>>
+struct check;
+
+template<typename Tp>
+struct check<Tp, true>
+{
+ check()
+ {
+ Tp a = 0;
+ Tp b = 42;
+ VERIFY(check_wait_notify(a, b) == b);
+ }
+};
+
+template<typename Tp>
+struct check<Tp, false>
+{
+ check(Tp b)
+ {
+ Tp a;
+ VERIFY(check_wait_notify(a, b) == b);
+ }
+};
+
+struct foo
+{
+ long a = 0;
+ long b = 0;
+
+ foo& operator=(foo const&) = default;
+
+ friend bool
+ operator==(foo const& rhs, foo const& lhs)
+ { return rhs.a == lhs.a && rhs.b == lhs.b; }
+};
+
+int
+main ()
+{
+ check<long>();
+ check<double>();
+ check<foo>({42, 48});
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc
new file mode 100644
index 00000000000..b9fc063c66f
--- /dev/null
+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc
@@ -0,0 +1,59 @@
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <atomic>
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+#include <type_traits>
+#include <chrono>
+
+#include <testsuite_hooks.h>
+
+int
+main ()
+{
+ using namespace std::literals::chrono_literals;
+
+ std::mutex m;
+ std::condition_variable cv;
+
+ std::atomic<bool> a(false);
+ std::atomic<bool> b(false);
+ std::thread t([&]
+ {
+ cv.notify_one();
+ a.wait(false);
+ if (a.load())
+ {
+ b.store(true);
+ }
+ });
+ std::unique_lock<std::mutex> l(m);
+ cv.wait(l);
+ std::this_thread::sleep_for(100ms);
+ a.store(true);
+ a.notify_one();
+ t.join();
+ VERIFY( b.load() );
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc
new file mode 100644
index 00000000000..1d032085752
--- /dev/null
+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc
@@ -0,0 +1,32 @@
+// { dg-options "-std=gnu++2a -pthread -latomic -L../../libatomic/.libs" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include "generic.h"
+
+int
+main ()
+{
+ check<float> f;
+ check<double> d;
+ check<long double> l;
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h
new file mode 100644
index 00000000000..0da374ece87
--- /dev/null
+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h
@@ -0,0 +1,88 @@
+// -*- 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <atomic>
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+#include <chrono>
+
+#include <testsuite_hooks.h>
+
+template<typename Tp>
+Tp check_wait_notify(Tp val1, Tp val2)
+{
+ using namespace std::literals::chrono_literals;
+
+ std::mutex m;
+ std::condition_variable cv;
+
+ std::atomic<Tp> a(val1);
+ std::thread t([&]
+ {
+ cv.notify_one();
+ a.wait(val1);
+ if (a.load() != val2)
+ a = val1;
+ });
+ std::unique_lock<std::mutex> l(m);
+ cv.wait(l);
+ std::this_thread::sleep_for(100ms);
+ a.store(val2);
+ a.notify_one();
+ t.join();
+ return a.load();
+}
+
+template<typename Tp>
+Tp check_atomic_wait_notify(Tp val1, Tp val2)
+{
+ using namespace std::literals::chrono_literals;
+
+ std::mutex m;
+ std::condition_variable cv;
+
+ std::atomic<Tp> a(val1);
+ std::thread t([&]
+ {
+ cv.notify_one();
+ std::atomic_wait(&a, val1);
+ if (a.load() != val2)
+ a = val1;
+ });
+ std::unique_lock<std::mutex> l(m);
+ cv.wait(l);
+ std::this_thread::sleep_for(100ms);
+ a.store(val2);
+ std::atomic_notify_one(&a);
+ t.join();
+ return a.load();
+}
+
+template<typename Tp>
+struct check
+{
+ check()
+ {
+ Tp a = 0;
+ Tp b = 42;
+ VERIFY(check_wait_notify(a, b) == b);
+ VERIFY(check_atomic_wait_notify(a, b) == b);
+ }
+};
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc
new file mode 100644
index 00000000000..2afd19a7d14
--- /dev/null
+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc
@@ -0,0 +1,56 @@
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include "generic.h"
+
+int
+main ()
+{
+ // check<bool> bb;
+ check<char> ch;
+ check<signed char> sch;
+ check<unsigned char> uch;
+ check<short> s;
+ check<unsigned short> us;
+ check<int> i;
+ check<unsigned int> ui;
+ check<long> l;
+ check<unsigned long> ul;
+ check<long long> ll;
+ check<unsigned long long> ull;
+
+ check<wchar_t> wch;
+ check<char8_t> ch8;
+ check<char16_t> ch16;
+ check<char32_t> ch32;
+
+ check<int8_t> i8;
+ check<int16_t> i16;
+ check<int32_t> i32;
+ check<int64_t> i64;
+
+ check<uint8_t> u8;
+ check<uint16_t> u16;
+ check<uint32_t> u32;
+ check<uint64_t> u64;
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc
new file mode 100644
index 00000000000..8531bb2e788
--- /dev/null
+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc
@@ -0,0 +1,59 @@
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <atomic>
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+#include <type_traits>
+#include <chrono>
+
+#include <testsuite_hooks.h>
+
+int
+main ()
+{
+ using namespace std::literals::chrono_literals;
+
+ std::mutex m;
+ std::condition_variable cv;
+
+ long aa;
+ long bb;
+
+ std::atomic<long*> a(nullptr);
+ std::thread t([&]
+ {
+ cv.notify_one();
+ a.wait(nullptr);
+ if (a.load() == &aa)
+ a.store(&bb);
+ });
+ std::unique_lock<std::mutex> l(m);
+ cv.wait(l);
+ std::this_thread::sleep_for(100ms);
+ a.store(&aa);
+ a.notify_one();
+ t.join();
+ VERIFY( a.load() == &bb);
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/30_threads/latch/1.cc b/libstdc++-v3/testsuite/30_threads/latch/1.cc
new file mode 100644
index 00000000000..aa203cdf525
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/latch/1.cc
@@ -0,0 +1,27 @@
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <latch>
+
+#ifndef __cpp_lib_latch
+# error "Feature-test macro for latch missing in <latch>"
+#elif __cpp_lib_latch!= 201907L
+# error "Feature-test macro for latch has wrong value in <latch>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/latch/2.cc b/libstdc++-v3/testsuite/30_threads/latch/2.cc
new file mode 100644
index 00000000000..756727f33b3
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/latch/2.cc
@@ -0,0 +1,27 @@
+// Copyright (C) 2019-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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <version>
+
+#ifndef __cpp_lib_latch
+# error "Feature-test macro for latch missing in <version>"
+#elif __cpp_lib_latch != 201907L
+# error "Feature-test macro for latch has wrong value in <version>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/latch/3.cc b/libstdc++-v3/testsuite/30_threads/latch/3.cc
new file mode 100644
index 00000000000..10bb500d261
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/latch/3.cc
@@ -0,0 +1,50 @@
+// Copyright (C) 2019-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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+//
+#include <latch>
+#include <atomic>
+#include <thread>
+#include <testsuite_hooks.h>
+
+int main()
+{
+ std::atomic<int> a(0);
+
+ std::latch l(3);
+
+ VERIFY( !l.try_wait() );
+
+ auto fn = [&]
+ {
+ ++a;
+ l.count_down();
+ };
+
+ std::thread t0(fn);
+ std::thread t1(fn);
+
+ l.arrive_and_wait();
+ t0.join();
+ t1.join();
+
+ VERIFY( l.try_wait() );
+}
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/1.cc b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc
new file mode 100644
index 00000000000..1bbca687fc3
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc
@@ -0,0 +1,27 @@
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <semaphore>
+
+#ifndef __cpp_lib_semaphore
+# error "Feature-test macro for semaphore missing in <semaphore>"
+#elif __cpp_lib_semaphore != 201907L
+# error "Feature-test macro for semaphore has wrong value in <semaphore>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/2.cc b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc
new file mode 100644
index 00000000000..b96b8a59c64
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc
@@ -0,0 +1,27 @@
+// Copyright (C) 2019-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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <version>
+
+#ifndef __cpp_lib_semaphore
+# error "Feature-test macro for semaphore missing in <version>"
+#elif __cpp_lib_semaphore != 201907L
+# error "Feature-test macro for semaphore has wrong value in <version>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc b/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc
new file mode 100644
index 00000000000..1ac9d261ca5
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc
@@ -0,0 +1,28 @@
+// Copyright (C) 2019-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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <semaphore>
+
+int main()
+{
+ std::counting_semaphore<-1> sem(2);
+ return 0;
+}
+// { dg-error "static assertion failed" "" { target *-*-* } 0 }
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc
new file mode 100644
index 00000000000..d38cef86cfc
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc
@@ -0,0 +1,55 @@
+// Copyright (C) 2019-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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <semaphore>
+#include <limits>
+#include <cstddef>
+#include <testsuite_hooks.h>
+
+void test01()
+{
+ std::counting_semaphore<10> s(3);
+
+ s.acquire();
+ VERIFY( s.try_acquire() );
+ VERIFY( s.try_acquire() );
+ VERIFY( !s.try_acquire() );
+ s.release();
+ VERIFY( s.try_acquire() );
+}
+
+void test02()
+{
+ std::binary_semaphore s(1);
+
+ s.acquire();
+ VERIFY( !s.try_acquire() );
+ s.release();
+ VERIFY( s.try_acquire() );
+}
+
+
+int main()
+{
+ test01();
+ test02();
+}
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc
new file mode 100644
index 00000000000..965554a3c28
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc
@@ -0,0 +1,85 @@
+// Copyright (C) 2019-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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <semaphore>
+#include <chrono>
+#include <thread>
+#include <atomic>
+#include <testsuite_hooks.h>
+
+void test01()
+{
+ using namespace std::chrono_literals;
+ std::counting_semaphore<10> s(2);
+ s.acquire();
+
+ auto const dur = 250ms;
+ {
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( s.try_acquire_for(dur) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff < dur );
+ }
+
+ {
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( !s.try_acquire_for(dur) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff >= dur );
+ }
+}
+
+void test02()
+{
+ using namespace std::chrono_literals;
+ std::binary_semaphore s(1);
+ std::atomic<int> a(0), b(0);
+ std::thread t([&] {
+ a.wait(0);
+ auto const dur = 250ms;
+ VERIFY( !s.try_acquire_for(dur) );
+ b++;
+ b.notify_one();
+
+ a.wait(1);
+ VERIFY( s.try_acquire_for(dur) );
+ b++;
+ b.notify_one();
+ });
+ t.detach();
+
+ s.acquire();
+ a++;
+ a.notify_one();
+ b.wait(0);
+ s.release();
+ a++;
+ a.notify_one();
+
+ b.wait(1);
+}
+
+int main()
+{
+ test01();
+ test02();
+}
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_futex.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_futex.cc
new file mode 100644
index 00000000000..5e05606e97f
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_futex.cc
@@ -0,0 +1,51 @@
+// Copyright (C) 2019-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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <semaphore>
+#include <limits>
+#include <cstddef>
+#include <testsuite_hooks.h>
+
+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
+void test01()
+{
+ // the implementation optimizes for values of least_max_t that can fit
+ // in a futex, make sure we cover the case where least_max_t doesn't
+ auto constexpr least_max_t = std::numeric_limits<std::ptrdiff_t>::max();
+ std::counting_semaphore<least_max_t> s(3);
+
+ s.acquire();
+ VERIFY( s.try_acquire() );
+ VERIFY( s.try_acquire() );
+ VERIFY( !s.try_acquire() );
+ s.release();
+ VERIFY( s.try_acquire() );
+}
+
+#endif
+
+int main()
+{
+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
+ test01();
+#endif
+}
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc
new file mode 100644
index 00000000000..bf99fd3cf8f
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc
@@ -0,0 +1,169 @@
+// Copyright (C) 2019-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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <semaphore>
+#include <chrono>
+#include <thread>
+#include <atomic>
+#include <testsuite_hooks.h>
+
+#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
+ // The implementation supports posix as an implementation strategy
+ // make sure we cover that case
+#define _GLIBCXX_REQUIRE_POSIX_SEMAPHORE
+
+void test01()
+{
+ std::counting_semaphore<10> s(3);
+
+ s.acquire();
+ VERIFY( s.try_acquire() );
+ VERIFY( s.try_acquire() );
+ VERIFY( !s.try_acquire() );
+ s.release();
+ VERIFY( s.try_acquire() );
+}
+
+void test02()
+{
+ using namespace std::chrono_literals;
+ std::counting_semaphore<10> s(2);
+ s.acquire();
+
+ auto const dur = 250ms;
+ {
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( s.try_acquire_for(dur) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff < dur );
+ }
+
+ {
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( !s.try_acquire_for(dur) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff >= dur );
+ }
+}
+
+void test03()
+{
+ using namespace std::chrono_literals;
+ std::binary_semaphore s(1);
+ std::atomic<int> a(0), b(0);
+ std::thread t([&] {
+ a.wait(0);
+ auto const dur = 250ms;
+ VERIFY( !s.try_acquire_for(dur) );
+ b++;
+ b.notify_one();
+
+ a.wait(1);
+ VERIFY( s.try_acquire_for(dur) );
+ b++;
+ b.notify_one();
+ });
+ t.detach();
+
+ s.acquire();
+ a++;
+ a.notify_one();
+ b.wait(0);
+ s.release();
+ a++;
+ a.notify_one();
+
+ b.wait(1);
+}
+
+void test04()
+{
+ using namespace std::chrono_literals;
+ std::counting_semaphore<10> s(2);
+ s.acquire();
+
+ auto const dur = 250ms;
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( s.try_acquire_until(at) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff < dur );
+ }
+
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( !s.try_acquire_until(at) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff >= dur );
+ }
+}
+
+void test05()
+{
+ using namespace std::chrono_literals;
+ std::binary_semaphore s(1);
+ std::atomic<int> a(0), b(0);
+ std::thread t([&] {
+ a.wait(0);
+ auto const dur = 250ms;
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ VERIFY( !s.try_acquire_until(at) );
+
+ b++;
+ b.notify_one();
+ }
+
+ a.wait(1);
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ VERIFY( s.try_acquire_until(at) );
+ }
+ b++;
+ b.notify_one();
+ });
+ t.detach();
+
+ s.acquire();
+ a++;
+ a.notify_one();
+ b.wait(0);
+ s.release();
+ a++;
+ a.notify_one();
+
+ b.wait(1);
+}
+#endif
+
+int main()
+{
+#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
+ test01();
+ test02();
+ test03();
+ test04();
+ test05();
+#endif
+}
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc
new file mode 100644
index 00000000000..cc67c5c0bf0
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc
@@ -0,0 +1,94 @@
+// Copyright (C) 2019-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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <semaphore>
+#include <chrono>
+#include <thread>
+#include <atomic>
+#include <testsuite_hooks.h>
+
+void test01()
+{
+ using namespace std::chrono_literals;
+ std::counting_semaphore<10> s(2);
+ s.acquire();
+
+ auto const dur = 250ms;
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( s.try_acquire_until(at) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff < dur );
+ }
+
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( !s.try_acquire_until(at) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff >= dur );
+ }
+}
+
+void test02()
+{
+ using namespace std::chrono_literals;
+ std::binary_semaphore s(1);
+ std::atomic<int> a(0), b(0);
+ std::thread t([&] {
+ a.wait(0);
+ auto const dur = 250ms;
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ VERIFY( !s.try_acquire_until(at) );
+
+ b++;
+ b.notify_one();
+ }
+
+ a.wait(1);
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ VERIFY( s.try_acquire_until(at) );
+ }
+ b++;
+ b.notify_one();
+ });
+ t.detach();
+
+ s.acquire();
+ a++;
+ a.notify_one();
+ b.wait(0);
+ s.release();
+ a++;
+ a.notify_one();
+
+ b.wait(1);
+}
+
+int main()
+{
+ test01();
+ test02();
+}
[-- Attachment #3: Type: text/plain, Size: 43750 bytes --]
Jonathan Wakely writes:
> 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...
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] Add C++2a synchronization support
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
0 siblings, 2 replies; 50+ messages in thread
From: Thomas Rodgers @ 2020-05-23 22:52 UTC (permalink / raw)
To: libstdc++, gcc-patches; +Cc: Jonathan Wakely, Thomas Rodgers
[-- Attachment #1: Type: text/plain, Size: 16 bytes --]
Updated patch.
[-- Attachment #2: 0001-Add-C-2a-synchronization-support.patch --]
[-- Type: application/octet-stream, Size: 86718 bytes --]
From a3decdc503fbaa0805358946ac5646bfa17840e4 Mon Sep 17 00:00:00 2001
From: Thomas Rodgers <rodgert@appliantology.com>
Date: Mon, 6 Apr 2020 17:58:47 -0700
Subject: [PATCH] Add C++2a synchronization support
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.
---
libstdc++-v3/include/Makefile.am | 5 +
libstdc++-v3/include/Makefile.in | 5 +
libstdc++-v3/include/bits/atomic_base.h | 161 +++++++++-
libstdc++-v3/include/bits/atomic_timed_wait.h | 282 +++++++++++++++++
libstdc++-v3/include/bits/atomic_wait.h | 291 ++++++++++++++++++
libstdc++-v3/include/bits/semaphore_base.h | 272 ++++++++++++++++
libstdc++-v3/include/std/atomic | 61 ++++
libstdc++-v3/include/std/latch | 90 ++++++
libstdc++-v3/include/std/semaphore | 86 ++++++
libstdc++-v3/include/std/version | 2 +
.../atomic/wait_notify/atomic_refs.cc | 103 +++++++
.../29_atomics/atomic/wait_notify/bool.cc | 59 ++++
.../29_atomics/atomic/wait_notify/floats.cc | 32 ++
.../29_atomics/atomic/wait_notify/generic.h | 88 ++++++
.../atomic/wait_notify/integrals.cc | 56 ++++
.../29_atomics/atomic/wait_notify/pointers.cc | 59 ++++
libstdc++-v3/testsuite/30_threads/latch/1.cc | 27 ++
libstdc++-v3/testsuite/30_threads/latch/2.cc | 27 ++
libstdc++-v3/testsuite/30_threads/latch/3.cc | 50 +++
.../testsuite/30_threads/semaphore/1.cc | 27 ++
.../testsuite/30_threads/semaphore/2.cc | 27 ++
.../semaphore/least_max_value_neg.cc | 28 ++
.../30_threads/semaphore/try_acquire.cc | 55 ++++
.../30_threads/semaphore/try_acquire_for.cc | 85 +++++
.../30_threads/semaphore/try_acquire_futex.cc | 51 +++
.../30_threads/semaphore/try_acquire_posix.cc | 169 ++++++++++
.../30_threads/semaphore/try_acquire_until.cc | 94 ++++++
27 files changed, 2291 insertions(+), 1 deletion(-)
create mode 100644 libstdc++-v3/include/bits/atomic_timed_wait.h
create mode 100644 libstdc++-v3/include/bits/atomic_wait.h
create mode 100644 libstdc++-v3/include/bits/semaphore_base.h
create mode 100644 libstdc++-v3/include/std/latch
create mode 100644 libstdc++-v3/include/std/semaphore
create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc
create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc
create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc
create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h
create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc
create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/latch/1.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/latch/2.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/latch/3.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/1.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/2.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_futex.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc
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\
${std_srcdir}/limits \
${std_srcdir}/list \
${std_srcdir}/locale \
@@ -69,6 +70,7 @@ std_headers = \
${std_srcdir}/ratio \
${std_srcdir}/regex \
${std_srcdir}/scoped_allocator \
+ ${std_srcdir}/semaphore \
${std_srcdir}/set \
${std_srcdir}/shared_mutex \
${std_srcdir}/span \
@@ -100,6 +102,8 @@ bits_headers = \
${bits_srcdir}/allocated_ptr.h \
${bits_srcdir}/allocator.h \
${bits_srcdir}/atomic_base.h \
+ ${bits_srcdir}/atomic_wait.h \
+ ${bits_srcdir}/atomic_timed_wait.h \
${bits_srcdir}/atomic_futex.h \
${bits_srcdir}/basic_ios.h \
${bits_srcdir}/basic_ios.tcc \
@@ -174,6 +178,7 @@ bits_headers = \
${bits_srcdir}/regex_compiler.tcc \
${bits_srcdir}/regex_executor.h \
${bits_srcdir}/regex_executor.tcc \
+ ${bits_srcdir}/semaphore_base.h \
${bits_srcdir}/shared_ptr.h \
${bits_srcdir}/shared_ptr_atomic.h \
${bits_srcdir}/shared_ptr_base.h \
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
@@ -397,6 +397,7 @@ std_headers = \
${std_srcdir}/iostream \
${std_srcdir}/istream \
${std_srcdir}/iterator \
+ ${std_srcdir}/latch\
${std_srcdir}/limits \
${std_srcdir}/list \
${std_srcdir}/locale \
@@ -414,6 +415,7 @@ std_headers = \
${std_srcdir}/ratio \
${std_srcdir}/regex \
${std_srcdir}/scoped_allocator \
+ ${std_srcdir}/semaphore \
${std_srcdir}/set \
${std_srcdir}/shared_mutex \
${std_srcdir}/span \
@@ -445,6 +447,8 @@ bits_headers = \
${bits_srcdir}/allocated_ptr.h \
${bits_srcdir}/allocator.h \
${bits_srcdir}/atomic_base.h \
+ ${bits_srcdir}/atomic_wait.h \
+ ${bits_srcdir}/atomic_timed_wait.h \
${bits_srcdir}/atomic_futex.h \
${bits_srcdir}/basic_ios.h \
${bits_srcdir}/basic_ios.tcc \
@@ -519,6 +523,7 @@ bits_headers = \
${bits_srcdir}/regex_compiler.tcc \
${bits_srcdir}/regex_executor.h \
${bits_srcdir}/regex_executor.tcc \
+ ${bits_srcdir}/semaphore_base.h \
${bits_srcdir}/shared_ptr.h \
${bits_srcdir}/shared_ptr_atomic.h \
${bits_srcdir}/shared_ptr_base.h \
diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h
index 3b66b040976..68d9e7e3756 100644
--- a/libstdc++-v3/include/bits/atomic_base.h
+++ b/libstdc++-v3/include/bits/atomic_base.h
@@ -37,6 +37,10 @@
#include <bits/atomic_lockfree_defines.h>
#include <bits/move.h>
+#if __cplusplus > 201703L
+#include <bits/atomic_wait.h>
+#endif
+
#ifndef _GLIBCXX_ALWAYS_INLINE
#define _GLIBCXX_ALWAYS_INLINE inline __attribute__((__always_inline__))
#endif
@@ -134,7 +138,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
return __ret;
}
-
// Base types for atomics.
template<typename _IntTp>
struct __atomic_base;
@@ -562,6 +565,31 @@ _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
+ {
+ std::__atomic_wait(&_M_i, __old,
+ [__m, this, __old]()
+ { return this->load(__m) != __old; });
+ }
+
+ // TODO add const volatile overload
+
+ _GLIBCXX_ALWAYS_INLINE void
+ notify_one() const noexcept
+ { std::__atomic_notify(&_M_i, false); }
+
+ // TODO add const volatile overload
+
+ _GLIBCXX_ALWAYS_INLINE void
+ notify_all() const noexcept
+ { std::__atomic_notify(&_M_i, true); }
+
+ // TODO add const volatile overload
+#endif // C++2a
+
_GLIBCXX_ALWAYS_INLINE __int_type
fetch_add(__int_type __i,
memory_order __m = memory_order_seq_cst) noexcept
@@ -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
+ {
+ std::__atomic_wait(&_M_p, __old,
+ [__m, this, __old]()
+ { return this->load(__m) != __old; });
+ }
+
+ // TODO add const volatile overload
+
+ _GLIBCXX_ALWAYS_INLINE void
+ notify_one() const noexcept
+ { std::__atomic_notify(&_M_p, false); }
+
+ // TODO add const volatile overload
+
+ _GLIBCXX_ALWAYS_INLINE void
+ notify_all() const noexcept
+ { std::__atomic_notify(&_M_p, true); }
+
+ // TODO add const volatile overload
+#endif // C++2a
+
_GLIBCXX_ALWAYS_INLINE __pointer_type
fetch_add(ptrdiff_t __d,
memory_order __m = memory_order_seq_cst) noexcept
@@ -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
+ {
+ std::__atomic_wait(__ptr, __old,
+ [=]() { return load(__ptr, __m) == __old; });
+ }
+
+ // TODO add const volatile overload
+
+ template<typename _Tp>
+ _GLIBCXX_ALWAYS_INLINE void
+ notify_one(const _Tp* __ptr) noexcept
+ { std::__atomic_notify(__ptr, false); }
+
+ // TODO add const volatile overload
+
+ template<typename _Tp>
+ _GLIBCXX_ALWAYS_INLINE void
+ notify_all(const _Tp* __ptr) noexcept
+ { std::__atomic_notify(__ptr, true); }
+
+ // TODO add const volatile overload
+#endif // C++2a
+
template<typename _Tp>
_GLIBCXX_ALWAYS_INLINE _Tp
fetch_add(_Tp* __ptr, _Diff<_Tp> __i, memory_order __m) noexcept
@@ -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
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); }
+
private:
_Tp* _M_ptr;
};
@@ -1396,6 +1507,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); }
+
value_type
fetch_add(value_type __i,
memory_order __m = memory_order_seq_cst) const noexcept
@@ -1551,6 +1678,22 @@ _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_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); }
+
value_type
fetch_add(value_type __i,
memory_order __m = memory_order_seq_cst) const noexcept
@@ -1660,6 +1803,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); }
+
_GLIBCXX_ALWAYS_INLINE value_type
fetch_add(difference_type __d,
memory_order __m = memory_order_seq_cst) const noexcept
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
+
+ enum class __atomic_wait_status { __no_timeout, __timeout };
+
+ 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;
+
+ 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);
+ }
+ 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))
+ 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)
+ 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);
+ }
+ };
+ } // 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
+
+// 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;
+
+ 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
+ {
+#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;
+ __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
+ }
+
+ 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);
+ }
+ 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
@@ -0,0 +1,272 @@
+// -*- 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_base.h
+ * This is an internal header file, included by other library headers.
+ * Do not attempt to use it directly. @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>
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
+ template<ptrdiff_t __least_max_value>
+ struct __platform_semaphore
+ {
+ using __clock_t = chrono::system_clock;
+
+ explicit __platform_semaphore(ptrdiff_t __count) noexcept
+ {
+ static_assert( __least_max_value <= SEM_VALUE_MAX, "");
+ 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>
+ bool
+ __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>
+ bool
+ try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept
+ {
+ if constexpr (std::is_same<__clock_t, _Clock>::value)
+ {
+ 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
+ {
+ explicit __atomic_semaphore(_Tp __count)
+ : _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(__alignof__(_Tp)) _Tp _M_a;
+ };
+
+#ifdef _GLIBCXX_REQUIRE_POSIX_SEMAPHORE
+ template<ptrdiff_t __least_max_value>
+ using __semaphore_base = __platform_semaphore<__least_max_value>;
+#else
+# ifdef _GLIBCXX_HAVE_LINUX_FUTEX
+ template<ptrdiff_t __least_max_value>
+ using __semaphore_base = conditional_t<(
+ __least_max_value >= 0
+ && __least_max_value <= __detail::__platform_wait_max_value),
+ __atomic_semaphore<__detail::__platform_wait_t>,
+ __atomic_semaphore<ptrdiff_t>>;
+
+// __platform_semaphore
+# elif defined _GLIBCXX_HAVE_POSIX_SEMAPHORE
+ template<ptrdiff_t __least_max_value>
+ using __semaphore_base = conditional_t<(
+ __least_max_value >= 0
+ && __least_max_value <= SEM_VALUE_MAX),
+ __platform_semaphore<__least_max_value>,
+ __atomic_semaphore<ptrdiff_t>>;
+# else
+ template<ptrdiff_t __least_max_value>
+ using __semaphore_base = __atomic_semaphore<ptrdiff_t>;
+# 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..aa5299d9fdd
--- /dev/null
+++ b/libstdc++-v3/include/std/latch
@@ -0,0 +1,90 @@
+// <latch> -*- C++ -*-
+
+// 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.
+ */
+
+#ifndef _GLIBCXX_LATCH
+#define _GLIBCXX_LATCH
+
+#pragma GCC system_header
+
+#if __cplusplus > 201703L
+#define __cpp_lib_latch 201907L
+
+#include <bits/atomic_base.h>
+#include <ext/numeric_traits.h>
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+ class latch
+ {
+ public:
+ static constexpr
+ _GLIBCXX_ALWAYS_INLINE ptrdiff_t
+ max() noexcept
+ { return __gnu_cxx::__numeric_traits<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(__alignof__(ptrdiff_t)) ptrdiff_t _M_a;
+ };
+_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..90cf3244647
--- /dev/null
+++ b/libstdc++-v3/include/std/semaphore
@@ -0,0 +1,86 @@
+// <semaphore> -*- C++ -*-
+
+// 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.
+ */
+
+#ifndef _GLIBCXX_SEMAPHORE
+#define _GLIBCXX_SEMAPHORE
+
+#pragma GCC system_header
+
+#if __cplusplus > 201703L
+#define __cpp_lib_semaphore 201907L
+#include <bits/semaphore_base.h>
+#include <ext/numeric_traits.h>
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+ template<ptrdiff_t __least_max_value =
+ __gnu_cxx::__numeric_traits<ptrdiff_t>::__max>
+ class counting_semaphore
+ {
+ static_assert(__least_max_value >=0, "");
+
+ __semaphore_base<__least_max_value> _M_sem;
+
+ 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>
+ 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>
+ 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 c6bde2cfbda..f09da3344f7 100644
--- a/libstdc++-v3/include/std/version
+++ b/libstdc++-v3/include/std/version
@@ -189,6 +189,8 @@
#endif
#define __cpp_lib_type_identity 201806L
#define __cpp_lib_unwrap_ref 201811L
+#define __cpp_lib_semaphore 201907L
+#define __cpp_lib_latch 201907L
#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" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <atomic>
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+#include <chrono>
+#include <type_traits>
+
+#include <testsuite_hooks.h>
+
+template<typename Tp>
+Tp check_wait_notify(Tp val1, Tp val2)
+{
+ using namespace std::literals::chrono_literals;
+
+ std::mutex m;
+ std::condition_variable cv;
+
+ Tp aa = val1;
+ std::atomic_ref<Tp> a(aa);
+ std::thread t([&]
+ {
+ cv.notify_one();
+ a.wait(val1);
+ if (a.load() != val2)
+ a = val1;
+ });
+ std::unique_lock<std::mutex> l(m);
+ cv.wait(l);
+ std::this_thread::sleep_for(100ms);
+ a.store(val2);
+ a.notify_one();
+ t.join();
+ return a.load();
+}
+
+template<typename Tp,
+ bool = std::is_integral_v<Tp>
+ || std::is_floating_point_v<Tp>>
+struct check;
+
+template<typename Tp>
+struct check<Tp, true>
+{
+ check()
+ {
+ Tp a = 0;
+ Tp b = 42;
+ VERIFY(check_wait_notify(a, b) == b);
+ }
+};
+
+template<typename Tp>
+struct check<Tp, false>
+{
+ check(Tp b)
+ {
+ Tp a;
+ VERIFY(check_wait_notify(a, b) == b);
+ }
+};
+
+struct foo
+{
+ long a = 0;
+ long b = 0;
+
+ foo& operator=(foo const&) = default;
+
+ friend bool
+ operator==(foo const& rhs, foo const& lhs)
+ { return rhs.a == lhs.a && rhs.b == lhs.b; }
+};
+
+int
+main ()
+{
+ check<long>();
+ check<double>();
+ check<foo>({42, 48});
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc
new file mode 100644
index 00000000000..b9fc063c66f
--- /dev/null
+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc
@@ -0,0 +1,59 @@
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <atomic>
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+#include <type_traits>
+#include <chrono>
+
+#include <testsuite_hooks.h>
+
+int
+main ()
+{
+ using namespace std::literals::chrono_literals;
+
+ std::mutex m;
+ std::condition_variable cv;
+
+ std::atomic<bool> a(false);
+ std::atomic<bool> b(false);
+ std::thread t([&]
+ {
+ cv.notify_one();
+ a.wait(false);
+ if (a.load())
+ {
+ b.store(true);
+ }
+ });
+ std::unique_lock<std::mutex> l(m);
+ cv.wait(l);
+ std::this_thread::sleep_for(100ms);
+ a.store(true);
+ a.notify_one();
+ t.join();
+ VERIFY( b.load() );
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc
new file mode 100644
index 00000000000..1d032085752
--- /dev/null
+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc
@@ -0,0 +1,32 @@
+// { dg-options "-std=gnu++2a -pthread -latomic -L../../libatomic/.libs" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include "generic.h"
+
+int
+main ()
+{
+ check<float> f;
+ check<double> d;
+ check<long double> l;
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h
new file mode 100644
index 00000000000..0da374ece87
--- /dev/null
+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h
@@ -0,0 +1,88 @@
+// -*- 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <atomic>
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+#include <chrono>
+
+#include <testsuite_hooks.h>
+
+template<typename Tp>
+Tp check_wait_notify(Tp val1, Tp val2)
+{
+ using namespace std::literals::chrono_literals;
+
+ std::mutex m;
+ std::condition_variable cv;
+
+ std::atomic<Tp> a(val1);
+ std::thread t([&]
+ {
+ cv.notify_one();
+ a.wait(val1);
+ if (a.load() != val2)
+ a = val1;
+ });
+ std::unique_lock<std::mutex> l(m);
+ cv.wait(l);
+ std::this_thread::sleep_for(100ms);
+ a.store(val2);
+ a.notify_one();
+ t.join();
+ return a.load();
+}
+
+template<typename Tp>
+Tp check_atomic_wait_notify(Tp val1, Tp val2)
+{
+ using namespace std::literals::chrono_literals;
+
+ std::mutex m;
+ std::condition_variable cv;
+
+ std::atomic<Tp> a(val1);
+ std::thread t([&]
+ {
+ cv.notify_one();
+ std::atomic_wait(&a, val1);
+ if (a.load() != val2)
+ a = val1;
+ });
+ std::unique_lock<std::mutex> l(m);
+ cv.wait(l);
+ std::this_thread::sleep_for(100ms);
+ a.store(val2);
+ std::atomic_notify_one(&a);
+ t.join();
+ return a.load();
+}
+
+template<typename Tp>
+struct check
+{
+ check()
+ {
+ Tp a = 0;
+ Tp b = 42;
+ VERIFY(check_wait_notify(a, b) == b);
+ VERIFY(check_atomic_wait_notify(a, b) == b);
+ }
+};
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc
new file mode 100644
index 00000000000..2afd19a7d14
--- /dev/null
+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc
@@ -0,0 +1,56 @@
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include "generic.h"
+
+int
+main ()
+{
+ // check<bool> bb;
+ check<char> ch;
+ check<signed char> sch;
+ check<unsigned char> uch;
+ check<short> s;
+ check<unsigned short> us;
+ check<int> i;
+ check<unsigned int> ui;
+ check<long> l;
+ check<unsigned long> ul;
+ check<long long> ll;
+ check<unsigned long long> ull;
+
+ check<wchar_t> wch;
+ check<char8_t> ch8;
+ check<char16_t> ch16;
+ check<char32_t> ch32;
+
+ check<int8_t> i8;
+ check<int16_t> i16;
+ check<int32_t> i32;
+ check<int64_t> i64;
+
+ check<uint8_t> u8;
+ check<uint16_t> u16;
+ check<uint32_t> u32;
+ check<uint64_t> u64;
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc
new file mode 100644
index 00000000000..8531bb2e788
--- /dev/null
+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc
@@ -0,0 +1,59 @@
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <atomic>
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+#include <type_traits>
+#include <chrono>
+
+#include <testsuite_hooks.h>
+
+int
+main ()
+{
+ using namespace std::literals::chrono_literals;
+
+ std::mutex m;
+ std::condition_variable cv;
+
+ long aa;
+ long bb;
+
+ std::atomic<long*> a(nullptr);
+ std::thread t([&]
+ {
+ cv.notify_one();
+ a.wait(nullptr);
+ if (a.load() == &aa)
+ a.store(&bb);
+ });
+ std::unique_lock<std::mutex> l(m);
+ cv.wait(l);
+ std::this_thread::sleep_for(100ms);
+ a.store(&aa);
+ a.notify_one();
+ t.join();
+ VERIFY( a.load() == &bb);
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/30_threads/latch/1.cc b/libstdc++-v3/testsuite/30_threads/latch/1.cc
new file mode 100644
index 00000000000..aa203cdf525
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/latch/1.cc
@@ -0,0 +1,27 @@
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <latch>
+
+#ifndef __cpp_lib_latch
+# error "Feature-test macro for latch missing in <latch>"
+#elif __cpp_lib_latch!= 201907L
+# error "Feature-test macro for latch has wrong value in <latch>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/latch/2.cc b/libstdc++-v3/testsuite/30_threads/latch/2.cc
new file mode 100644
index 00000000000..756727f33b3
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/latch/2.cc
@@ -0,0 +1,27 @@
+// Copyright (C) 2019-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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <version>
+
+#ifndef __cpp_lib_latch
+# error "Feature-test macro for latch missing in <version>"
+#elif __cpp_lib_latch != 201907L
+# error "Feature-test macro for latch has wrong value in <version>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/latch/3.cc b/libstdc++-v3/testsuite/30_threads/latch/3.cc
new file mode 100644
index 00000000000..10bb500d261
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/latch/3.cc
@@ -0,0 +1,50 @@
+// Copyright (C) 2019-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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+//
+#include <latch>
+#include <atomic>
+#include <thread>
+#include <testsuite_hooks.h>
+
+int main()
+{
+ std::atomic<int> a(0);
+
+ std::latch l(3);
+
+ VERIFY( !l.try_wait() );
+
+ auto fn = [&]
+ {
+ ++a;
+ l.count_down();
+ };
+
+ std::thread t0(fn);
+ std::thread t1(fn);
+
+ l.arrive_and_wait();
+ t0.join();
+ t1.join();
+
+ VERIFY( l.try_wait() );
+}
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/1.cc b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc
new file mode 100644
index 00000000000..1bbca687fc3
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc
@@ -0,0 +1,27 @@
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <semaphore>
+
+#ifndef __cpp_lib_semaphore
+# error "Feature-test macro for semaphore missing in <semaphore>"
+#elif __cpp_lib_semaphore != 201907L
+# error "Feature-test macro for semaphore has wrong value in <semaphore>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/2.cc b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc
new file mode 100644
index 00000000000..b96b8a59c64
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc
@@ -0,0 +1,27 @@
+// Copyright (C) 2019-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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <version>
+
+#ifndef __cpp_lib_semaphore
+# error "Feature-test macro for semaphore missing in <version>"
+#elif __cpp_lib_semaphore != 201907L
+# error "Feature-test macro for semaphore has wrong value in <version>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc b/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc
new file mode 100644
index 00000000000..1ac9d261ca5
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc
@@ -0,0 +1,28 @@
+// Copyright (C) 2019-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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <semaphore>
+
+int main()
+{
+ std::counting_semaphore<-1> sem(2);
+ return 0;
+}
+// { dg-error "static assertion failed" "" { target *-*-* } 0 }
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc
new file mode 100644
index 00000000000..d38cef86cfc
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc
@@ -0,0 +1,55 @@
+// Copyright (C) 2019-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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <semaphore>
+#include <limits>
+#include <cstddef>
+#include <testsuite_hooks.h>
+
+void test01()
+{
+ std::counting_semaphore<10> s(3);
+
+ s.acquire();
+ VERIFY( s.try_acquire() );
+ VERIFY( s.try_acquire() );
+ VERIFY( !s.try_acquire() );
+ s.release();
+ VERIFY( s.try_acquire() );
+}
+
+void test02()
+{
+ std::binary_semaphore s(1);
+
+ s.acquire();
+ VERIFY( !s.try_acquire() );
+ s.release();
+ VERIFY( s.try_acquire() );
+}
+
+
+int main()
+{
+ test01();
+ test02();
+}
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc
new file mode 100644
index 00000000000..965554a3c28
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc
@@ -0,0 +1,85 @@
+// Copyright (C) 2019-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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <semaphore>
+#include <chrono>
+#include <thread>
+#include <atomic>
+#include <testsuite_hooks.h>
+
+void test01()
+{
+ using namespace std::chrono_literals;
+ std::counting_semaphore<10> s(2);
+ s.acquire();
+
+ auto const dur = 250ms;
+ {
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( s.try_acquire_for(dur) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff < dur );
+ }
+
+ {
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( !s.try_acquire_for(dur) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff >= dur );
+ }
+}
+
+void test02()
+{
+ using namespace std::chrono_literals;
+ std::binary_semaphore s(1);
+ std::atomic<int> a(0), b(0);
+ std::thread t([&] {
+ a.wait(0);
+ auto const dur = 250ms;
+ VERIFY( !s.try_acquire_for(dur) );
+ b++;
+ b.notify_one();
+
+ a.wait(1);
+ VERIFY( s.try_acquire_for(dur) );
+ b++;
+ b.notify_one();
+ });
+ t.detach();
+
+ s.acquire();
+ a++;
+ a.notify_one();
+ b.wait(0);
+ s.release();
+ a++;
+ a.notify_one();
+
+ b.wait(1);
+}
+
+int main()
+{
+ test01();
+ test02();
+}
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_futex.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_futex.cc
new file mode 100644
index 00000000000..5e05606e97f
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_futex.cc
@@ -0,0 +1,51 @@
+// Copyright (C) 2019-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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <semaphore>
+#include <limits>
+#include <cstddef>
+#include <testsuite_hooks.h>
+
+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
+void test01()
+{
+ // the implementation optimizes for values of least_max_t that can fit
+ // in a futex, make sure we cover the case where least_max_t doesn't
+ auto constexpr least_max_t = std::numeric_limits<std::ptrdiff_t>::max();
+ std::counting_semaphore<least_max_t> s(3);
+
+ s.acquire();
+ VERIFY( s.try_acquire() );
+ VERIFY( s.try_acquire() );
+ VERIFY( !s.try_acquire() );
+ s.release();
+ VERIFY( s.try_acquire() );
+}
+
+#endif
+
+int main()
+{
+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
+ test01();
+#endif
+}
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc
new file mode 100644
index 00000000000..bf99fd3cf8f
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc
@@ -0,0 +1,169 @@
+// Copyright (C) 2019-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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <semaphore>
+#include <chrono>
+#include <thread>
+#include <atomic>
+#include <testsuite_hooks.h>
+
+#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
+ // The implementation supports posix as an implementation strategy
+ // make sure we cover that case
+#define _GLIBCXX_REQUIRE_POSIX_SEMAPHORE
+
+void test01()
+{
+ std::counting_semaphore<10> s(3);
+
+ s.acquire();
+ VERIFY( s.try_acquire() );
+ VERIFY( s.try_acquire() );
+ VERIFY( !s.try_acquire() );
+ s.release();
+ VERIFY( s.try_acquire() );
+}
+
+void test02()
+{
+ using namespace std::chrono_literals;
+ std::counting_semaphore<10> s(2);
+ s.acquire();
+
+ auto const dur = 250ms;
+ {
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( s.try_acquire_for(dur) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff < dur );
+ }
+
+ {
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( !s.try_acquire_for(dur) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff >= dur );
+ }
+}
+
+void test03()
+{
+ using namespace std::chrono_literals;
+ std::binary_semaphore s(1);
+ std::atomic<int> a(0), b(0);
+ std::thread t([&] {
+ a.wait(0);
+ auto const dur = 250ms;
+ VERIFY( !s.try_acquire_for(dur) );
+ b++;
+ b.notify_one();
+
+ a.wait(1);
+ VERIFY( s.try_acquire_for(dur) );
+ b++;
+ b.notify_one();
+ });
+ t.detach();
+
+ s.acquire();
+ a++;
+ a.notify_one();
+ b.wait(0);
+ s.release();
+ a++;
+ a.notify_one();
+
+ b.wait(1);
+}
+
+void test04()
+{
+ using namespace std::chrono_literals;
+ std::counting_semaphore<10> s(2);
+ s.acquire();
+
+ auto const dur = 250ms;
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( s.try_acquire_until(at) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff < dur );
+ }
+
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( !s.try_acquire_until(at) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff >= dur );
+ }
+}
+
+void test05()
+{
+ using namespace std::chrono_literals;
+ std::binary_semaphore s(1);
+ std::atomic<int> a(0), b(0);
+ std::thread t([&] {
+ a.wait(0);
+ auto const dur = 250ms;
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ VERIFY( !s.try_acquire_until(at) );
+
+ b++;
+ b.notify_one();
+ }
+
+ a.wait(1);
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ VERIFY( s.try_acquire_until(at) );
+ }
+ b++;
+ b.notify_one();
+ });
+ t.detach();
+
+ s.acquire();
+ a++;
+ a.notify_one();
+ b.wait(0);
+ s.release();
+ a++;
+ a.notify_one();
+
+ b.wait(1);
+}
+#endif
+
+int main()
+{
+#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
+ test01();
+ test02();
+ test03();
+ test04();
+ test05();
+#endif
+}
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc
new file mode 100644
index 00000000000..cc67c5c0bf0
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc
@@ -0,0 +1,94 @@
+// Copyright (C) 2019-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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <semaphore>
+#include <chrono>
+#include <thread>
+#include <atomic>
+#include <testsuite_hooks.h>
+
+void test01()
+{
+ using namespace std::chrono_literals;
+ std::counting_semaphore<10> s(2);
+ s.acquire();
+
+ auto const dur = 250ms;
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( s.try_acquire_until(at) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff < dur );
+ }
+
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( !s.try_acquire_until(at) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff >= dur );
+ }
+}
+
+void test02()
+{
+ using namespace std::chrono_literals;
+ std::binary_semaphore s(1);
+ std::atomic<int> a(0), b(0);
+ std::thread t([&] {
+ a.wait(0);
+ auto const dur = 250ms;
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ VERIFY( !s.try_acquire_until(at) );
+
+ b++;
+ b.notify_one();
+ }
+
+ a.wait(1);
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ VERIFY( s.try_acquire_until(at) );
+ }
+ b++;
+ b.notify_one();
+ });
+ t.detach();
+
+ s.acquire();
+ a++;
+ a.notify_one();
+ b.wait(0);
+ s.release();
+ a++;
+ a.notify_one();
+
+ b.wait(1);
+}
+
+int main()
+{
+ test01();
+ test02();
+}
--
2.26.2
[-- Attachment #3: Type: text/plain, Size: 47984 bytes --]
> On May 11, 2020, at 1:59 PM, Thomas Rodgers via Libstdc++ <libstdc++@gcc.gnu.org> wrote:
>
> I *think* I have addressed everything in the attached patch.
> <0001-Add-C-2a-synchronization-support.patch>
> Jonathan Wakely writes:
>
>> 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...
>
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] Add C++2a synchronization support
2020-05-23 22:52 ` Thomas Rodgers
@ 2020-05-24 17:41 ` Thomas Rodgers
2020-06-06 0:29 ` Thomas Rodgers
1 sibling, 0 replies; 50+ messages in thread
From: Thomas Rodgers @ 2020-05-24 17:41 UTC (permalink / raw)
To: libstdc++, gcc-patches; +Cc: Thomas Rodgers, Jonathan Wakely
[-- Attachment #1: Type: text/plain, Size: 100 bytes --]
The previous patch had a Makefile.in included, should be a property filtered patch attached now.
[-- Attachment #2: 0001-Add-C-2a-synchronization-support_f.patch --]
[-- Type: application/octet-stream, Size: 85450 bytes --]
From a3decdc503fbaa0805358946ac5646bfa17840e4 Mon Sep 17 00:00:00 2001
From: Thomas Rodgers <rodgert@appliantology.com>
Date: Mon, 6 Apr 2020 17:58:47 -0700
Subject: [PATCH] Add C++2a synchronization support
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.
---
libstdc++-v3/include/Makefile.am | 5 +
libstdc++-v3/include/Makefile.in | 5 +
libstdc++-v3/include/bits/atomic_base.h | 161 +++++++++-
libstdc++-v3/include/bits/atomic_timed_wait.h | 282 +++++++++++++++++
libstdc++-v3/include/bits/atomic_wait.h | 291 ++++++++++++++++++
libstdc++-v3/include/bits/semaphore_base.h | 272 ++++++++++++++++
libstdc++-v3/include/std/atomic | 61 ++++
libstdc++-v3/include/std/latch | 90 ++++++
libstdc++-v3/include/std/semaphore | 86 ++++++
libstdc++-v3/include/std/version | 2 +
.../atomic/wait_notify/atomic_refs.cc | 103 +++++++
.../29_atomics/atomic/wait_notify/bool.cc | 59 ++++
.../29_atomics/atomic/wait_notify/floats.cc | 32 ++
.../29_atomics/atomic/wait_notify/generic.h | 88 ++++++
.../atomic/wait_notify/integrals.cc | 56 ++++
.../29_atomics/atomic/wait_notify/pointers.cc | 59 ++++
libstdc++-v3/testsuite/30_threads/latch/1.cc | 27 ++
libstdc++-v3/testsuite/30_threads/latch/2.cc | 27 ++
libstdc++-v3/testsuite/30_threads/latch/3.cc | 50 +++
.../testsuite/30_threads/semaphore/1.cc | 27 ++
.../testsuite/30_threads/semaphore/2.cc | 27 ++
.../semaphore/least_max_value_neg.cc | 28 ++
.../30_threads/semaphore/try_acquire.cc | 55 ++++
.../30_threads/semaphore/try_acquire_for.cc | 85 +++++
.../30_threads/semaphore/try_acquire_futex.cc | 51 +++
.../30_threads/semaphore/try_acquire_posix.cc | 169 ++++++++++
.../30_threads/semaphore/try_acquire_until.cc | 94 ++++++
27 files changed, 2291 insertions(+), 1 deletion(-)
create mode 100644 libstdc++-v3/include/bits/atomic_timed_wait.h
create mode 100644 libstdc++-v3/include/bits/atomic_wait.h
create mode 100644 libstdc++-v3/include/bits/semaphore_base.h
create mode 100644 libstdc++-v3/include/std/latch
create mode 100644 libstdc++-v3/include/std/semaphore
create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc
create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc
create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc
create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h
create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc
create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/latch/1.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/latch/2.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/latch/3.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/1.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/2.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_futex.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc
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\
${std_srcdir}/limits \
${std_srcdir}/list \
${std_srcdir}/locale \
@@ -69,6 +70,7 @@ std_headers = \
${std_srcdir}/ratio \
${std_srcdir}/regex \
${std_srcdir}/scoped_allocator \
+ ${std_srcdir}/semaphore \
${std_srcdir}/set \
${std_srcdir}/shared_mutex \
${std_srcdir}/span \
@@ -100,6 +102,8 @@ bits_headers = \
${bits_srcdir}/allocated_ptr.h \
${bits_srcdir}/allocator.h \
${bits_srcdir}/atomic_base.h \
+ ${bits_srcdir}/atomic_wait.h \
+ ${bits_srcdir}/atomic_timed_wait.h \
${bits_srcdir}/atomic_futex.h \
${bits_srcdir}/basic_ios.h \
${bits_srcdir}/basic_ios.tcc \
@@ -174,6 +178,7 @@ bits_headers = \
${bits_srcdir}/regex_compiler.tcc \
${bits_srcdir}/regex_executor.h \
${bits_srcdir}/regex_executor.tcc \
+ ${bits_srcdir}/semaphore_base.h \
${bits_srcdir}/shared_ptr.h \
${bits_srcdir}/shared_ptr_atomic.h \
${bits_srcdir}/shared_ptr_base.h \
diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h
index 3b66b040976..68d9e7e3756 100644
--- a/libstdc++-v3/include/bits/atomic_base.h
+++ b/libstdc++-v3/include/bits/atomic_base.h
@@ -37,6 +37,10 @@
#include <bits/atomic_lockfree_defines.h>
#include <bits/move.h>
+#if __cplusplus > 201703L
+#include <bits/atomic_wait.h>
+#endif
+
#ifndef _GLIBCXX_ALWAYS_INLINE
#define _GLIBCXX_ALWAYS_INLINE inline __attribute__((__always_inline__))
#endif
@@ -134,7 +138,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
return __ret;
}
-
// Base types for atomics.
template<typename _IntTp>
struct __atomic_base;
@@ -562,6 +565,31 @@ _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
+ {
+ std::__atomic_wait(&_M_i, __old,
+ [__m, this, __old]()
+ { return this->load(__m) != __old; });
+ }
+
+ // TODO add const volatile overload
+
+ _GLIBCXX_ALWAYS_INLINE void
+ notify_one() const noexcept
+ { std::__atomic_notify(&_M_i, false); }
+
+ // TODO add const volatile overload
+
+ _GLIBCXX_ALWAYS_INLINE void
+ notify_all() const noexcept
+ { std::__atomic_notify(&_M_i, true); }
+
+ // TODO add const volatile overload
+#endif // C++2a
+
_GLIBCXX_ALWAYS_INLINE __int_type
fetch_add(__int_type __i,
memory_order __m = memory_order_seq_cst) noexcept
@@ -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
+ {
+ std::__atomic_wait(&_M_p, __old,
+ [__m, this, __old]()
+ { return this->load(__m) != __old; });
+ }
+
+ // TODO add const volatile overload
+
+ _GLIBCXX_ALWAYS_INLINE void
+ notify_one() const noexcept
+ { std::__atomic_notify(&_M_p, false); }
+
+ // TODO add const volatile overload
+
+ _GLIBCXX_ALWAYS_INLINE void
+ notify_all() const noexcept
+ { std::__atomic_notify(&_M_p, true); }
+
+ // TODO add const volatile overload
+#endif // C++2a
+
_GLIBCXX_ALWAYS_INLINE __pointer_type
fetch_add(ptrdiff_t __d,
memory_order __m = memory_order_seq_cst) noexcept
@@ -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
+ {
+ std::__atomic_wait(__ptr, __old,
+ [=]() { return load(__ptr, __m) == __old; });
+ }
+
+ // TODO add const volatile overload
+
+ template<typename _Tp>
+ _GLIBCXX_ALWAYS_INLINE void
+ notify_one(const _Tp* __ptr) noexcept
+ { std::__atomic_notify(__ptr, false); }
+
+ // TODO add const volatile overload
+
+ template<typename _Tp>
+ _GLIBCXX_ALWAYS_INLINE void
+ notify_all(const _Tp* __ptr) noexcept
+ { std::__atomic_notify(__ptr, true); }
+
+ // TODO add const volatile overload
+#endif // C++2a
+
template<typename _Tp>
_GLIBCXX_ALWAYS_INLINE _Tp
fetch_add(_Tp* __ptr, _Diff<_Tp> __i, memory_order __m) noexcept
@@ -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
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); }
+
private:
_Tp* _M_ptr;
};
@@ -1396,6 +1507,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); }
+
value_type
fetch_add(value_type __i,
memory_order __m = memory_order_seq_cst) const noexcept
@@ -1551,6 +1678,22 @@ _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_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); }
+
value_type
fetch_add(value_type __i,
memory_order __m = memory_order_seq_cst) const noexcept
@@ -1660,6 +1803,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); }
+
_GLIBCXX_ALWAYS_INLINE value_type
fetch_add(difference_type __d,
memory_order __m = memory_order_seq_cst) const noexcept
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
+
+ enum class __atomic_wait_status { __no_timeout, __timeout };
+
+ 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;
+
+ 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);
+ }
+ 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))
+ 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)
+ 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);
+ }
+ };
+ } // 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
+
+// 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;
+
+ 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
+ {
+#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;
+ __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
+ }
+
+ 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);
+ }
+ 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
@@ -0,0 +1,272 @@
+// -*- 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_base.h
+ * This is an internal header file, included by other library headers.
+ * Do not attempt to use it directly. @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>
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
+ template<ptrdiff_t __least_max_value>
+ struct __platform_semaphore
+ {
+ using __clock_t = chrono::system_clock;
+
+ explicit __platform_semaphore(ptrdiff_t __count) noexcept
+ {
+ static_assert( __least_max_value <= SEM_VALUE_MAX, "");
+ 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>
+ bool
+ __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>
+ bool
+ try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept
+ {
+ if constexpr (std::is_same<__clock_t, _Clock>::value)
+ {
+ 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
+ {
+ explicit __atomic_semaphore(_Tp __count)
+ : _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(__alignof__(_Tp)) _Tp _M_a;
+ };
+
+#ifdef _GLIBCXX_REQUIRE_POSIX_SEMAPHORE
+ template<ptrdiff_t __least_max_value>
+ using __semaphore_base = __platform_semaphore<__least_max_value>;
+#else
+# ifdef _GLIBCXX_HAVE_LINUX_FUTEX
+ template<ptrdiff_t __least_max_value>
+ using __semaphore_base = conditional_t<(
+ __least_max_value >= 0
+ && __least_max_value <= __detail::__platform_wait_max_value),
+ __atomic_semaphore<__detail::__platform_wait_t>,
+ __atomic_semaphore<ptrdiff_t>>;
+
+// __platform_semaphore
+# elif defined _GLIBCXX_HAVE_POSIX_SEMAPHORE
+ template<ptrdiff_t __least_max_value>
+ using __semaphore_base = conditional_t<(
+ __least_max_value >= 0
+ && __least_max_value <= SEM_VALUE_MAX),
+ __platform_semaphore<__least_max_value>,
+ __atomic_semaphore<ptrdiff_t>>;
+# else
+ template<ptrdiff_t __least_max_value>
+ using __semaphore_base = __atomic_semaphore<ptrdiff_t>;
+# 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..aa5299d9fdd
--- /dev/null
+++ b/libstdc++-v3/include/std/latch
@@ -0,0 +1,90 @@
+// <latch> -*- C++ -*-
+
+// 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.
+ */
+
+#ifndef _GLIBCXX_LATCH
+#define _GLIBCXX_LATCH
+
+#pragma GCC system_header
+
+#if __cplusplus > 201703L
+#define __cpp_lib_latch 201907L
+
+#include <bits/atomic_base.h>
+#include <ext/numeric_traits.h>
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+ class latch
+ {
+ public:
+ static constexpr
+ _GLIBCXX_ALWAYS_INLINE ptrdiff_t
+ max() noexcept
+ { return __gnu_cxx::__numeric_traits<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(__alignof__(ptrdiff_t)) ptrdiff_t _M_a;
+ };
+_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..90cf3244647
--- /dev/null
+++ b/libstdc++-v3/include/std/semaphore
@@ -0,0 +1,86 @@
+// <semaphore> -*- C++ -*-
+
+// 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.
+ */
+
+#ifndef _GLIBCXX_SEMAPHORE
+#define _GLIBCXX_SEMAPHORE
+
+#pragma GCC system_header
+
+#if __cplusplus > 201703L
+#define __cpp_lib_semaphore 201907L
+#include <bits/semaphore_base.h>
+#include <ext/numeric_traits.h>
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+ template<ptrdiff_t __least_max_value =
+ __gnu_cxx::__numeric_traits<ptrdiff_t>::__max>
+ class counting_semaphore
+ {
+ static_assert(__least_max_value >=0, "");
+
+ __semaphore_base<__least_max_value> _M_sem;
+
+ 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>
+ 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>
+ 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 c6bde2cfbda..f09da3344f7 100644
--- a/libstdc++-v3/include/std/version
+++ b/libstdc++-v3/include/std/version
@@ -189,6 +189,8 @@
#endif
#define __cpp_lib_type_identity 201806L
#define __cpp_lib_unwrap_ref 201811L
+#define __cpp_lib_semaphore 201907L
+#define __cpp_lib_latch 201907L
#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" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <atomic>
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+#include <chrono>
+#include <type_traits>
+
+#include <testsuite_hooks.h>
+
+template<typename Tp>
+Tp check_wait_notify(Tp val1, Tp val2)
+{
+ using namespace std::literals::chrono_literals;
+
+ std::mutex m;
+ std::condition_variable cv;
+
+ Tp aa = val1;
+ std::atomic_ref<Tp> a(aa);
+ std::thread t([&]
+ {
+ cv.notify_one();
+ a.wait(val1);
+ if (a.load() != val2)
+ a = val1;
+ });
+ std::unique_lock<std::mutex> l(m);
+ cv.wait(l);
+ std::this_thread::sleep_for(100ms);
+ a.store(val2);
+ a.notify_one();
+ t.join();
+ return a.load();
+}
+
+template<typename Tp,
+ bool = std::is_integral_v<Tp>
+ || std::is_floating_point_v<Tp>>
+struct check;
+
+template<typename Tp>
+struct check<Tp, true>
+{
+ check()
+ {
+ Tp a = 0;
+ Tp b = 42;
+ VERIFY(check_wait_notify(a, b) == b);
+ }
+};
+
+template<typename Tp>
+struct check<Tp, false>
+{
+ check(Tp b)
+ {
+ Tp a;
+ VERIFY(check_wait_notify(a, b) == b);
+ }
+};
+
+struct foo
+{
+ long a = 0;
+ long b = 0;
+
+ foo& operator=(foo const&) = default;
+
+ friend bool
+ operator==(foo const& rhs, foo const& lhs)
+ { return rhs.a == lhs.a && rhs.b == lhs.b; }
+};
+
+int
+main ()
+{
+ check<long>();
+ check<double>();
+ check<foo>({42, 48});
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc
new file mode 100644
index 00000000000..b9fc063c66f
--- /dev/null
+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc
@@ -0,0 +1,59 @@
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <atomic>
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+#include <type_traits>
+#include <chrono>
+
+#include <testsuite_hooks.h>
+
+int
+main ()
+{
+ using namespace std::literals::chrono_literals;
+
+ std::mutex m;
+ std::condition_variable cv;
+
+ std::atomic<bool> a(false);
+ std::atomic<bool> b(false);
+ std::thread t([&]
+ {
+ cv.notify_one();
+ a.wait(false);
+ if (a.load())
+ {
+ b.store(true);
+ }
+ });
+ std::unique_lock<std::mutex> l(m);
+ cv.wait(l);
+ std::this_thread::sleep_for(100ms);
+ a.store(true);
+ a.notify_one();
+ t.join();
+ VERIFY( b.load() );
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc
new file mode 100644
index 00000000000..1d032085752
--- /dev/null
+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc
@@ -0,0 +1,32 @@
+// { dg-options "-std=gnu++2a -pthread -latomic -L../../libatomic/.libs" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include "generic.h"
+
+int
+main ()
+{
+ check<float> f;
+ check<double> d;
+ check<long double> l;
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h
new file mode 100644
index 00000000000..0da374ece87
--- /dev/null
+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h
@@ -0,0 +1,88 @@
+// -*- 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <atomic>
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+#include <chrono>
+
+#include <testsuite_hooks.h>
+
+template<typename Tp>
+Tp check_wait_notify(Tp val1, Tp val2)
+{
+ using namespace std::literals::chrono_literals;
+
+ std::mutex m;
+ std::condition_variable cv;
+
+ std::atomic<Tp> a(val1);
+ std::thread t([&]
+ {
+ cv.notify_one();
+ a.wait(val1);
+ if (a.load() != val2)
+ a = val1;
+ });
+ std::unique_lock<std::mutex> l(m);
+ cv.wait(l);
+ std::this_thread::sleep_for(100ms);
+ a.store(val2);
+ a.notify_one();
+ t.join();
+ return a.load();
+}
+
+template<typename Tp>
+Tp check_atomic_wait_notify(Tp val1, Tp val2)
+{
+ using namespace std::literals::chrono_literals;
+
+ std::mutex m;
+ std::condition_variable cv;
+
+ std::atomic<Tp> a(val1);
+ std::thread t([&]
+ {
+ cv.notify_one();
+ std::atomic_wait(&a, val1);
+ if (a.load() != val2)
+ a = val1;
+ });
+ std::unique_lock<std::mutex> l(m);
+ cv.wait(l);
+ std::this_thread::sleep_for(100ms);
+ a.store(val2);
+ std::atomic_notify_one(&a);
+ t.join();
+ return a.load();
+}
+
+template<typename Tp>
+struct check
+{
+ check()
+ {
+ Tp a = 0;
+ Tp b = 42;
+ VERIFY(check_wait_notify(a, b) == b);
+ VERIFY(check_atomic_wait_notify(a, b) == b);
+ }
+};
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc
new file mode 100644
index 00000000000..2afd19a7d14
--- /dev/null
+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc
@@ -0,0 +1,56 @@
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include "generic.h"
+
+int
+main ()
+{
+ // check<bool> bb;
+ check<char> ch;
+ check<signed char> sch;
+ check<unsigned char> uch;
+ check<short> s;
+ check<unsigned short> us;
+ check<int> i;
+ check<unsigned int> ui;
+ check<long> l;
+ check<unsigned long> ul;
+ check<long long> ll;
+ check<unsigned long long> ull;
+
+ check<wchar_t> wch;
+ check<char8_t> ch8;
+ check<char16_t> ch16;
+ check<char32_t> ch32;
+
+ check<int8_t> i8;
+ check<int16_t> i16;
+ check<int32_t> i32;
+ check<int64_t> i64;
+
+ check<uint8_t> u8;
+ check<uint16_t> u16;
+ check<uint32_t> u32;
+ check<uint64_t> u64;
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc
new file mode 100644
index 00000000000..8531bb2e788
--- /dev/null
+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc
@@ -0,0 +1,59 @@
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <atomic>
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+#include <type_traits>
+#include <chrono>
+
+#include <testsuite_hooks.h>
+
+int
+main ()
+{
+ using namespace std::literals::chrono_literals;
+
+ std::mutex m;
+ std::condition_variable cv;
+
+ long aa;
+ long bb;
+
+ std::atomic<long*> a(nullptr);
+ std::thread t([&]
+ {
+ cv.notify_one();
+ a.wait(nullptr);
+ if (a.load() == &aa)
+ a.store(&bb);
+ });
+ std::unique_lock<std::mutex> l(m);
+ cv.wait(l);
+ std::this_thread::sleep_for(100ms);
+ a.store(&aa);
+ a.notify_one();
+ t.join();
+ VERIFY( a.load() == &bb);
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/30_threads/latch/1.cc b/libstdc++-v3/testsuite/30_threads/latch/1.cc
new file mode 100644
index 00000000000..aa203cdf525
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/latch/1.cc
@@ -0,0 +1,27 @@
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <latch>
+
+#ifndef __cpp_lib_latch
+# error "Feature-test macro for latch missing in <latch>"
+#elif __cpp_lib_latch!= 201907L
+# error "Feature-test macro for latch has wrong value in <latch>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/latch/2.cc b/libstdc++-v3/testsuite/30_threads/latch/2.cc
new file mode 100644
index 00000000000..756727f33b3
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/latch/2.cc
@@ -0,0 +1,27 @@
+// Copyright (C) 2019-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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <version>
+
+#ifndef __cpp_lib_latch
+# error "Feature-test macro for latch missing in <version>"
+#elif __cpp_lib_latch != 201907L
+# error "Feature-test macro for latch has wrong value in <version>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/latch/3.cc b/libstdc++-v3/testsuite/30_threads/latch/3.cc
new file mode 100644
index 00000000000..10bb500d261
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/latch/3.cc
@@ -0,0 +1,50 @@
+// Copyright (C) 2019-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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+//
+#include <latch>
+#include <atomic>
+#include <thread>
+#include <testsuite_hooks.h>
+
+int main()
+{
+ std::atomic<int> a(0);
+
+ std::latch l(3);
+
+ VERIFY( !l.try_wait() );
+
+ auto fn = [&]
+ {
+ ++a;
+ l.count_down();
+ };
+
+ std::thread t0(fn);
+ std::thread t1(fn);
+
+ l.arrive_and_wait();
+ t0.join();
+ t1.join();
+
+ VERIFY( l.try_wait() );
+}
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/1.cc b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc
new file mode 100644
index 00000000000..1bbca687fc3
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc
@@ -0,0 +1,27 @@
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <semaphore>
+
+#ifndef __cpp_lib_semaphore
+# error "Feature-test macro for semaphore missing in <semaphore>"
+#elif __cpp_lib_semaphore != 201907L
+# error "Feature-test macro for semaphore has wrong value in <semaphore>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/2.cc b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc
new file mode 100644
index 00000000000..b96b8a59c64
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc
@@ -0,0 +1,27 @@
+// Copyright (C) 2019-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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <version>
+
+#ifndef __cpp_lib_semaphore
+# error "Feature-test macro for semaphore missing in <version>"
+#elif __cpp_lib_semaphore != 201907L
+# error "Feature-test macro for semaphore has wrong value in <version>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc b/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc
new file mode 100644
index 00000000000..1ac9d261ca5
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc
@@ -0,0 +1,28 @@
+// Copyright (C) 2019-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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <semaphore>
+
+int main()
+{
+ std::counting_semaphore<-1> sem(2);
+ return 0;
+}
+// { dg-error "static assertion failed" "" { target *-*-* } 0 }
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc
new file mode 100644
index 00000000000..d38cef86cfc
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc
@@ -0,0 +1,55 @@
+// Copyright (C) 2019-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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <semaphore>
+#include <limits>
+#include <cstddef>
+#include <testsuite_hooks.h>
+
+void test01()
+{
+ std::counting_semaphore<10> s(3);
+
+ s.acquire();
+ VERIFY( s.try_acquire() );
+ VERIFY( s.try_acquire() );
+ VERIFY( !s.try_acquire() );
+ s.release();
+ VERIFY( s.try_acquire() );
+}
+
+void test02()
+{
+ std::binary_semaphore s(1);
+
+ s.acquire();
+ VERIFY( !s.try_acquire() );
+ s.release();
+ VERIFY( s.try_acquire() );
+}
+
+
+int main()
+{
+ test01();
+ test02();
+}
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc
new file mode 100644
index 00000000000..965554a3c28
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc
@@ -0,0 +1,85 @@
+// Copyright (C) 2019-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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <semaphore>
+#include <chrono>
+#include <thread>
+#include <atomic>
+#include <testsuite_hooks.h>
+
+void test01()
+{
+ using namespace std::chrono_literals;
+ std::counting_semaphore<10> s(2);
+ s.acquire();
+
+ auto const dur = 250ms;
+ {
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( s.try_acquire_for(dur) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff < dur );
+ }
+
+ {
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( !s.try_acquire_for(dur) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff >= dur );
+ }
+}
+
+void test02()
+{
+ using namespace std::chrono_literals;
+ std::binary_semaphore s(1);
+ std::atomic<int> a(0), b(0);
+ std::thread t([&] {
+ a.wait(0);
+ auto const dur = 250ms;
+ VERIFY( !s.try_acquire_for(dur) );
+ b++;
+ b.notify_one();
+
+ a.wait(1);
+ VERIFY( s.try_acquire_for(dur) );
+ b++;
+ b.notify_one();
+ });
+ t.detach();
+
+ s.acquire();
+ a++;
+ a.notify_one();
+ b.wait(0);
+ s.release();
+ a++;
+ a.notify_one();
+
+ b.wait(1);
+}
+
+int main()
+{
+ test01();
+ test02();
+}
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_futex.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_futex.cc
new file mode 100644
index 00000000000..5e05606e97f
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_futex.cc
@@ -0,0 +1,51 @@
+// Copyright (C) 2019-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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <semaphore>
+#include <limits>
+#include <cstddef>
+#include <testsuite_hooks.h>
+
+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
+void test01()
+{
+ // the implementation optimizes for values of least_max_t that can fit
+ // in a futex, make sure we cover the case where least_max_t doesn't
+ auto constexpr least_max_t = std::numeric_limits<std::ptrdiff_t>::max();
+ std::counting_semaphore<least_max_t> s(3);
+
+ s.acquire();
+ VERIFY( s.try_acquire() );
+ VERIFY( s.try_acquire() );
+ VERIFY( !s.try_acquire() );
+ s.release();
+ VERIFY( s.try_acquire() );
+}
+
+#endif
+
+int main()
+{
+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
+ test01();
+#endif
+}
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc
new file mode 100644
index 00000000000..bf99fd3cf8f
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc
@@ -0,0 +1,169 @@
+// Copyright (C) 2019-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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <semaphore>
+#include <chrono>
+#include <thread>
+#include <atomic>
+#include <testsuite_hooks.h>
+
+#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
+ // The implementation supports posix as an implementation strategy
+ // make sure we cover that case
+#define _GLIBCXX_REQUIRE_POSIX_SEMAPHORE
+
+void test01()
+{
+ std::counting_semaphore<10> s(3);
+
+ s.acquire();
+ VERIFY( s.try_acquire() );
+ VERIFY( s.try_acquire() );
+ VERIFY( !s.try_acquire() );
+ s.release();
+ VERIFY( s.try_acquire() );
+}
+
+void test02()
+{
+ using namespace std::chrono_literals;
+ std::counting_semaphore<10> s(2);
+ s.acquire();
+
+ auto const dur = 250ms;
+ {
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( s.try_acquire_for(dur) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff < dur );
+ }
+
+ {
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( !s.try_acquire_for(dur) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff >= dur );
+ }
+}
+
+void test03()
+{
+ using namespace std::chrono_literals;
+ std::binary_semaphore s(1);
+ std::atomic<int> a(0), b(0);
+ std::thread t([&] {
+ a.wait(0);
+ auto const dur = 250ms;
+ VERIFY( !s.try_acquire_for(dur) );
+ b++;
+ b.notify_one();
+
+ a.wait(1);
+ VERIFY( s.try_acquire_for(dur) );
+ b++;
+ b.notify_one();
+ });
+ t.detach();
+
+ s.acquire();
+ a++;
+ a.notify_one();
+ b.wait(0);
+ s.release();
+ a++;
+ a.notify_one();
+
+ b.wait(1);
+}
+
+void test04()
+{
+ using namespace std::chrono_literals;
+ std::counting_semaphore<10> s(2);
+ s.acquire();
+
+ auto const dur = 250ms;
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( s.try_acquire_until(at) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff < dur );
+ }
+
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( !s.try_acquire_until(at) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff >= dur );
+ }
+}
+
+void test05()
+{
+ using namespace std::chrono_literals;
+ std::binary_semaphore s(1);
+ std::atomic<int> a(0), b(0);
+ std::thread t([&] {
+ a.wait(0);
+ auto const dur = 250ms;
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ VERIFY( !s.try_acquire_until(at) );
+
+ b++;
+ b.notify_one();
+ }
+
+ a.wait(1);
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ VERIFY( s.try_acquire_until(at) );
+ }
+ b++;
+ b.notify_one();
+ });
+ t.detach();
+
+ s.acquire();
+ a++;
+ a.notify_one();
+ b.wait(0);
+ s.release();
+ a++;
+ a.notify_one();
+
+ b.wait(1);
+}
+#endif
+
+int main()
+{
+#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
+ test01();
+ test02();
+ test03();
+ test04();
+ test05();
+#endif
+}
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc
new file mode 100644
index 00000000000..cc67c5c0bf0
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc
@@ -0,0 +1,94 @@
+// Copyright (C) 2019-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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <semaphore>
+#include <chrono>
+#include <thread>
+#include <atomic>
+#include <testsuite_hooks.h>
+
+void test01()
+{
+ using namespace std::chrono_literals;
+ std::counting_semaphore<10> s(2);
+ s.acquire();
+
+ auto const dur = 250ms;
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( s.try_acquire_until(at) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff < dur );
+ }
+
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( !s.try_acquire_until(at) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff >= dur );
+ }
+}
+
+void test02()
+{
+ using namespace std::chrono_literals;
+ std::binary_semaphore s(1);
+ std::atomic<int> a(0), b(0);
+ std::thread t([&] {
+ a.wait(0);
+ auto const dur = 250ms;
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ VERIFY( !s.try_acquire_until(at) );
+
+ b++;
+ b.notify_one();
+ }
+
+ a.wait(1);
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ VERIFY( s.try_acquire_until(at) );
+ }
+ b++;
+ b.notify_one();
+ });
+ t.detach();
+
+ s.acquire();
+ a++;
+ a.notify_one();
+ b.wait(0);
+ s.release();
+ a++;
+ a.notify_one();
+
+ b.wait(1);
+}
+
+int main()
+{
+ test01();
+ test02();
+}
--
2.26.2
[-- Attachment #3: Type: text/plain, Size: 49514 bytes --]
> On May 23, 2020, at 3:52 PM, Thomas Rodgers <rodgert@appliantology.com> wrote:
>
> Updated patch.
>
> <0001-Add-C-2a-synchronization-support.patch>
>
>> On May 11, 2020, at 1:59 PM, Thomas Rodgers via Libstdc++ <libstdc++@gcc.gnu.org> wrote:
>>
>> I *think* I have addressed everything in the attached patch.
>> <0001-Add-C-2a-synchronization-support.patch>
>> Jonathan Wakely writes:
>>
>>> 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...
>>
>
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH] Add C++2a synchronization support
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
1 sibling, 2 replies; 50+ messages in thread
From: Thomas Rodgers @ 2020-06-06 0:29 UTC (permalink / raw)
To: gcc-patches, libstdc++; +Cc: trodgers, Thomas Rodgers
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.
---
libstdc++-v3/include/Makefile.am | 5 +
libstdc++-v3/include/Makefile.in | 5 +
libstdc++-v3/include/bits/atomic_base.h | 161 +++++++++-
libstdc++-v3/include/bits/atomic_timed_wait.h | 282 +++++++++++++++++
libstdc++-v3/include/bits/atomic_wait.h | 291 ++++++++++++++++++
libstdc++-v3/include/bits/semaphore_base.h | 272 ++++++++++++++++
libstdc++-v3/include/std/atomic | 61 ++++
libstdc++-v3/include/std/latch | 90 ++++++
libstdc++-v3/include/std/semaphore | 86 ++++++
libstdc++-v3/include/std/version | 2 +
.../atomic/wait_notify/atomic_refs.cc | 103 +++++++
.../29_atomics/atomic/wait_notify/bool.cc | 59 ++++
.../29_atomics/atomic/wait_notify/floats.cc | 32 ++
.../29_atomics/atomic/wait_notify/generic.h | 88 ++++++
.../atomic/wait_notify/integrals.cc | 56 ++++
.../29_atomics/atomic/wait_notify/pointers.cc | 59 ++++
libstdc++-v3/testsuite/30_threads/latch/1.cc | 27 ++
libstdc++-v3/testsuite/30_threads/latch/2.cc | 27 ++
libstdc++-v3/testsuite/30_threads/latch/3.cc | 50 +++
.../testsuite/30_threads/semaphore/1.cc | 27 ++
.../testsuite/30_threads/semaphore/2.cc | 27 ++
.../semaphore/least_max_value_neg.cc | 28 ++
.../30_threads/semaphore/try_acquire.cc | 55 ++++
.../30_threads/semaphore/try_acquire_for.cc | 85 +++++
.../30_threads/semaphore/try_acquire_futex.cc | 51 +++
.../30_threads/semaphore/try_acquire_posix.cc | 169 ++++++++++
.../30_threads/semaphore/try_acquire_until.cc | 94 ++++++
27 files changed, 2291 insertions(+), 1 deletion(-)
create mode 100644 libstdc++-v3/include/bits/atomic_timed_wait.h
create mode 100644 libstdc++-v3/include/bits/atomic_wait.h
create mode 100644 libstdc++-v3/include/bits/semaphore_base.h
create mode 100644 libstdc++-v3/include/std/latch
create mode 100644 libstdc++-v3/include/std/semaphore
create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc
create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc
create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc
create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h
create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc
create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/latch/1.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/latch/2.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/latch/3.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/1.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/2.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_futex.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc
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\
${std_srcdir}/limits \
${std_srcdir}/list \
${std_srcdir}/locale \
@@ -69,6 +70,7 @@ std_headers = \
${std_srcdir}/ratio \
${std_srcdir}/regex \
${std_srcdir}/scoped_allocator \
+ ${std_srcdir}/semaphore \
${std_srcdir}/set \
${std_srcdir}/shared_mutex \
${std_srcdir}/span \
@@ -100,6 +102,8 @@ bits_headers = \
${bits_srcdir}/allocated_ptr.h \
${bits_srcdir}/allocator.h \
${bits_srcdir}/atomic_base.h \
+ ${bits_srcdir}/atomic_wait.h \
+ ${bits_srcdir}/atomic_timed_wait.h \
${bits_srcdir}/atomic_futex.h \
${bits_srcdir}/basic_ios.h \
${bits_srcdir}/basic_ios.tcc \
@@ -174,6 +178,7 @@ bits_headers = \
${bits_srcdir}/regex_compiler.tcc \
${bits_srcdir}/regex_executor.h \
${bits_srcdir}/regex_executor.tcc \
+ ${bits_srcdir}/semaphore_base.h \
${bits_srcdir}/shared_ptr.h \
${bits_srcdir}/shared_ptr_atomic.h \
${bits_srcdir}/shared_ptr_base.h \
diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h
index 3b66b040976..68d9e7e3756 100644
--- a/libstdc++-v3/include/bits/atomic_base.h
+++ b/libstdc++-v3/include/bits/atomic_base.h
@@ -37,6 +37,10 @@
#include <bits/atomic_lockfree_defines.h>
#include <bits/move.h>
+#if __cplusplus > 201703L
+#include <bits/atomic_wait.h>
+#endif
+
#ifndef _GLIBCXX_ALWAYS_INLINE
#define _GLIBCXX_ALWAYS_INLINE inline __attribute__((__always_inline__))
#endif
@@ -134,7 +138,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
return __ret;
}
-
// Base types for atomics.
template<typename _IntTp>
struct __atomic_base;
@@ -562,6 +565,31 @@ _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
+ {
+ std::__atomic_wait(&_M_i, __old,
+ [__m, this, __old]()
+ { return this->load(__m) != __old; });
+ }
+
+ // TODO add const volatile overload
+
+ _GLIBCXX_ALWAYS_INLINE void
+ notify_one() const noexcept
+ { std::__atomic_notify(&_M_i, false); }
+
+ // TODO add const volatile overload
+
+ _GLIBCXX_ALWAYS_INLINE void
+ notify_all() const noexcept
+ { std::__atomic_notify(&_M_i, true); }
+
+ // TODO add const volatile overload
+#endif // C++2a
+
_GLIBCXX_ALWAYS_INLINE __int_type
fetch_add(__int_type __i,
memory_order __m = memory_order_seq_cst) noexcept
@@ -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
+ {
+ std::__atomic_wait(&_M_p, __old,
+ [__m, this, __old]()
+ { return this->load(__m) != __old; });
+ }
+
+ // TODO add const volatile overload
+
+ _GLIBCXX_ALWAYS_INLINE void
+ notify_one() const noexcept
+ { std::__atomic_notify(&_M_p, false); }
+
+ // TODO add const volatile overload
+
+ _GLIBCXX_ALWAYS_INLINE void
+ notify_all() const noexcept
+ { std::__atomic_notify(&_M_p, true); }
+
+ // TODO add const volatile overload
+#endif // C++2a
+
_GLIBCXX_ALWAYS_INLINE __pointer_type
fetch_add(ptrdiff_t __d,
memory_order __m = memory_order_seq_cst) noexcept
@@ -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
+ {
+ std::__atomic_wait(__ptr, __old,
+ [=]() { return load(__ptr, __m) == __old; });
+ }
+
+ // TODO add const volatile overload
+
+ template<typename _Tp>
+ _GLIBCXX_ALWAYS_INLINE void
+ notify_one(const _Tp* __ptr) noexcept
+ { std::__atomic_notify(__ptr, false); }
+
+ // TODO add const volatile overload
+
+ template<typename _Tp>
+ _GLIBCXX_ALWAYS_INLINE void
+ notify_all(const _Tp* __ptr) noexcept
+ { std::__atomic_notify(__ptr, true); }
+
+ // TODO add const volatile overload
+#endif // C++2a
+
template<typename _Tp>
_GLIBCXX_ALWAYS_INLINE _Tp
fetch_add(_Tp* __ptr, _Diff<_Tp> __i, memory_order __m) noexcept
@@ -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
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); }
+
private:
_Tp* _M_ptr;
};
@@ -1396,6 +1507,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); }
+
value_type
fetch_add(value_type __i,
memory_order __m = memory_order_seq_cst) const noexcept
@@ -1551,6 +1678,22 @@ _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_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); }
+
value_type
fetch_add(value_type __i,
memory_order __m = memory_order_seq_cst) const noexcept
@@ -1660,6 +1803,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); }
+
_GLIBCXX_ALWAYS_INLINE value_type
fetch_add(difference_type __d,
memory_order __m = memory_order_seq_cst) const noexcept
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
+
+ enum class __atomic_wait_status { __no_timeout, __timeout };
+
+ 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;
+
+ 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);
+ }
+ 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))
+ 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)
+ 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);
+ }
+ };
+ } // 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
+
+// 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;
+
+ 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
+ {
+#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;
+ __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
+ }
+
+ 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);
+ }
+ 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
@@ -0,0 +1,272 @@
+// -*- 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_base.h
+ * This is an internal header file, included by other library headers.
+ * Do not attempt to use it directly. @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>
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
+ template<ptrdiff_t __least_max_value>
+ struct __platform_semaphore
+ {
+ using __clock_t = chrono::system_clock;
+
+ explicit __platform_semaphore(ptrdiff_t __count) noexcept
+ {
+ static_assert( __least_max_value <= SEM_VALUE_MAX, "");
+ 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>
+ bool
+ __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>
+ bool
+ try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept
+ {
+ if constexpr (std::is_same<__clock_t, _Clock>::value)
+ {
+ 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
+ {
+ explicit __atomic_semaphore(_Tp __count)
+ : _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(__alignof__(_Tp)) _Tp _M_a;
+ };
+
+#ifdef _GLIBCXX_REQUIRE_POSIX_SEMAPHORE
+ template<ptrdiff_t __least_max_value>
+ using __semaphore_base = __platform_semaphore<__least_max_value>;
+#else
+# ifdef _GLIBCXX_HAVE_LINUX_FUTEX
+ template<ptrdiff_t __least_max_value>
+ using __semaphore_base = conditional_t<(
+ __least_max_value >= 0
+ && __least_max_value <= __detail::__platform_wait_max_value),
+ __atomic_semaphore<__detail::__platform_wait_t>,
+ __atomic_semaphore<ptrdiff_t>>;
+
+// __platform_semaphore
+# elif defined _GLIBCXX_HAVE_POSIX_SEMAPHORE
+ template<ptrdiff_t __least_max_value>
+ using __semaphore_base = conditional_t<(
+ __least_max_value >= 0
+ && __least_max_value <= SEM_VALUE_MAX),
+ __platform_semaphore<__least_max_value>,
+ __atomic_semaphore<ptrdiff_t>>;
+# else
+ template<ptrdiff_t __least_max_value>
+ using __semaphore_base = __atomic_semaphore<ptrdiff_t>;
+# 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..aa5299d9fdd
--- /dev/null
+++ b/libstdc++-v3/include/std/latch
@@ -0,0 +1,90 @@
+// <latch> -*- C++ -*-
+
+// 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.
+ */
+
+#ifndef _GLIBCXX_LATCH
+#define _GLIBCXX_LATCH
+
+#pragma GCC system_header
+
+#if __cplusplus > 201703L
+#define __cpp_lib_latch 201907L
+
+#include <bits/atomic_base.h>
+#include <ext/numeric_traits.h>
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+ class latch
+ {
+ public:
+ static constexpr
+ _GLIBCXX_ALWAYS_INLINE ptrdiff_t
+ max() noexcept
+ { return __gnu_cxx::__numeric_traits<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(__alignof__(ptrdiff_t)) ptrdiff_t _M_a;
+ };
+_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..90cf3244647
--- /dev/null
+++ b/libstdc++-v3/include/std/semaphore
@@ -0,0 +1,86 @@
+// <semaphore> -*- C++ -*-
+
+// 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.
+ */
+
+#ifndef _GLIBCXX_SEMAPHORE
+#define _GLIBCXX_SEMAPHORE
+
+#pragma GCC system_header
+
+#if __cplusplus > 201703L
+#define __cpp_lib_semaphore 201907L
+#include <bits/semaphore_base.h>
+#include <ext/numeric_traits.h>
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+ template<ptrdiff_t __least_max_value =
+ __gnu_cxx::__numeric_traits<ptrdiff_t>::__max>
+ class counting_semaphore
+ {
+ static_assert(__least_max_value >=0, "");
+
+ __semaphore_base<__least_max_value> _M_sem;
+
+ 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>
+ 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>
+ 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 c6bde2cfbda..f09da3344f7 100644
--- a/libstdc++-v3/include/std/version
+++ b/libstdc++-v3/include/std/version
@@ -189,6 +189,8 @@
#endif
#define __cpp_lib_type_identity 201806L
#define __cpp_lib_unwrap_ref 201811L
+#define __cpp_lib_semaphore 201907L
+#define __cpp_lib_latch 201907L
#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" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <atomic>
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+#include <chrono>
+#include <type_traits>
+
+#include <testsuite_hooks.h>
+
+template<typename Tp>
+Tp check_wait_notify(Tp val1, Tp val2)
+{
+ using namespace std::literals::chrono_literals;
+
+ std::mutex m;
+ std::condition_variable cv;
+
+ Tp aa = val1;
+ std::atomic_ref<Tp> a(aa);
+ std::thread t([&]
+ {
+ cv.notify_one();
+ a.wait(val1);
+ if (a.load() != val2)
+ a = val1;
+ });
+ std::unique_lock<std::mutex> l(m);
+ cv.wait(l);
+ std::this_thread::sleep_for(100ms);
+ a.store(val2);
+ a.notify_one();
+ t.join();
+ return a.load();
+}
+
+template<typename Tp,
+ bool = std::is_integral_v<Tp>
+ || std::is_floating_point_v<Tp>>
+struct check;
+
+template<typename Tp>
+struct check<Tp, true>
+{
+ check()
+ {
+ Tp a = 0;
+ Tp b = 42;
+ VERIFY(check_wait_notify(a, b) == b);
+ }
+};
+
+template<typename Tp>
+struct check<Tp, false>
+{
+ check(Tp b)
+ {
+ Tp a;
+ VERIFY(check_wait_notify(a, b) == b);
+ }
+};
+
+struct foo
+{
+ long a = 0;
+ long b = 0;
+
+ foo& operator=(foo const&) = default;
+
+ friend bool
+ operator==(foo const& rhs, foo const& lhs)
+ { return rhs.a == lhs.a && rhs.b == lhs.b; }
+};
+
+int
+main ()
+{
+ check<long>();
+ check<double>();
+ check<foo>({42, 48});
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc
new file mode 100644
index 00000000000..b9fc063c66f
--- /dev/null
+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc
@@ -0,0 +1,59 @@
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <atomic>
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+#include <type_traits>
+#include <chrono>
+
+#include <testsuite_hooks.h>
+
+int
+main ()
+{
+ using namespace std::literals::chrono_literals;
+
+ std::mutex m;
+ std::condition_variable cv;
+
+ std::atomic<bool> a(false);
+ std::atomic<bool> b(false);
+ std::thread t([&]
+ {
+ cv.notify_one();
+ a.wait(false);
+ if (a.load())
+ {
+ b.store(true);
+ }
+ });
+ std::unique_lock<std::mutex> l(m);
+ cv.wait(l);
+ std::this_thread::sleep_for(100ms);
+ a.store(true);
+ a.notify_one();
+ t.join();
+ VERIFY( b.load() );
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc
new file mode 100644
index 00000000000..1d032085752
--- /dev/null
+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc
@@ -0,0 +1,32 @@
+// { dg-options "-std=gnu++2a -pthread -latomic -L../../libatomic/.libs" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include "generic.h"
+
+int
+main ()
+{
+ check<float> f;
+ check<double> d;
+ check<long double> l;
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h
new file mode 100644
index 00000000000..0da374ece87
--- /dev/null
+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h
@@ -0,0 +1,88 @@
+// -*- 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <atomic>
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+#include <chrono>
+
+#include <testsuite_hooks.h>
+
+template<typename Tp>
+Tp check_wait_notify(Tp val1, Tp val2)
+{
+ using namespace std::literals::chrono_literals;
+
+ std::mutex m;
+ std::condition_variable cv;
+
+ std::atomic<Tp> a(val1);
+ std::thread t([&]
+ {
+ cv.notify_one();
+ a.wait(val1);
+ if (a.load() != val2)
+ a = val1;
+ });
+ std::unique_lock<std::mutex> l(m);
+ cv.wait(l);
+ std::this_thread::sleep_for(100ms);
+ a.store(val2);
+ a.notify_one();
+ t.join();
+ return a.load();
+}
+
+template<typename Tp>
+Tp check_atomic_wait_notify(Tp val1, Tp val2)
+{
+ using namespace std::literals::chrono_literals;
+
+ std::mutex m;
+ std::condition_variable cv;
+
+ std::atomic<Tp> a(val1);
+ std::thread t([&]
+ {
+ cv.notify_one();
+ std::atomic_wait(&a, val1);
+ if (a.load() != val2)
+ a = val1;
+ });
+ std::unique_lock<std::mutex> l(m);
+ cv.wait(l);
+ std::this_thread::sleep_for(100ms);
+ a.store(val2);
+ std::atomic_notify_one(&a);
+ t.join();
+ return a.load();
+}
+
+template<typename Tp>
+struct check
+{
+ check()
+ {
+ Tp a = 0;
+ Tp b = 42;
+ VERIFY(check_wait_notify(a, b) == b);
+ VERIFY(check_atomic_wait_notify(a, b) == b);
+ }
+};
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc
new file mode 100644
index 00000000000..2afd19a7d14
--- /dev/null
+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc
@@ -0,0 +1,56 @@
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include "generic.h"
+
+int
+main ()
+{
+ // check<bool> bb;
+ check<char> ch;
+ check<signed char> sch;
+ check<unsigned char> uch;
+ check<short> s;
+ check<unsigned short> us;
+ check<int> i;
+ check<unsigned int> ui;
+ check<long> l;
+ check<unsigned long> ul;
+ check<long long> ll;
+ check<unsigned long long> ull;
+
+ check<wchar_t> wch;
+ check<char8_t> ch8;
+ check<char16_t> ch16;
+ check<char32_t> ch32;
+
+ check<int8_t> i8;
+ check<int16_t> i16;
+ check<int32_t> i32;
+ check<int64_t> i64;
+
+ check<uint8_t> u8;
+ check<uint16_t> u16;
+ check<uint32_t> u32;
+ check<uint64_t> u64;
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc
new file mode 100644
index 00000000000..8531bb2e788
--- /dev/null
+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc
@@ -0,0 +1,59 @@
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <atomic>
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+#include <type_traits>
+#include <chrono>
+
+#include <testsuite_hooks.h>
+
+int
+main ()
+{
+ using namespace std::literals::chrono_literals;
+
+ std::mutex m;
+ std::condition_variable cv;
+
+ long aa;
+ long bb;
+
+ std::atomic<long*> a(nullptr);
+ std::thread t([&]
+ {
+ cv.notify_one();
+ a.wait(nullptr);
+ if (a.load() == &aa)
+ a.store(&bb);
+ });
+ std::unique_lock<std::mutex> l(m);
+ cv.wait(l);
+ std::this_thread::sleep_for(100ms);
+ a.store(&aa);
+ a.notify_one();
+ t.join();
+ VERIFY( a.load() == &bb);
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/30_threads/latch/1.cc b/libstdc++-v3/testsuite/30_threads/latch/1.cc
new file mode 100644
index 00000000000..aa203cdf525
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/latch/1.cc
@@ -0,0 +1,27 @@
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <latch>
+
+#ifndef __cpp_lib_latch
+# error "Feature-test macro for latch missing in <latch>"
+#elif __cpp_lib_latch!= 201907L
+# error "Feature-test macro for latch has wrong value in <latch>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/latch/2.cc b/libstdc++-v3/testsuite/30_threads/latch/2.cc
new file mode 100644
index 00000000000..756727f33b3
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/latch/2.cc
@@ -0,0 +1,27 @@
+// Copyright (C) 2019-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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <version>
+
+#ifndef __cpp_lib_latch
+# error "Feature-test macro for latch missing in <version>"
+#elif __cpp_lib_latch != 201907L
+# error "Feature-test macro for latch has wrong value in <version>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/latch/3.cc b/libstdc++-v3/testsuite/30_threads/latch/3.cc
new file mode 100644
index 00000000000..10bb500d261
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/latch/3.cc
@@ -0,0 +1,50 @@
+// Copyright (C) 2019-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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+//
+#include <latch>
+#include <atomic>
+#include <thread>
+#include <testsuite_hooks.h>
+
+int main()
+{
+ std::atomic<int> a(0);
+
+ std::latch l(3);
+
+ VERIFY( !l.try_wait() );
+
+ auto fn = [&]
+ {
+ ++a;
+ l.count_down();
+ };
+
+ std::thread t0(fn);
+ std::thread t1(fn);
+
+ l.arrive_and_wait();
+ t0.join();
+ t1.join();
+
+ VERIFY( l.try_wait() );
+}
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/1.cc b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc
new file mode 100644
index 00000000000..1bbca687fc3
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc
@@ -0,0 +1,27 @@
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <semaphore>
+
+#ifndef __cpp_lib_semaphore
+# error "Feature-test macro for semaphore missing in <semaphore>"
+#elif __cpp_lib_semaphore != 201907L
+# error "Feature-test macro for semaphore has wrong value in <semaphore>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/2.cc b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc
new file mode 100644
index 00000000000..b96b8a59c64
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc
@@ -0,0 +1,27 @@
+// Copyright (C) 2019-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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <version>
+
+#ifndef __cpp_lib_semaphore
+# error "Feature-test macro for semaphore missing in <version>"
+#elif __cpp_lib_semaphore != 201907L
+# error "Feature-test macro for semaphore has wrong value in <version>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc b/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc
new file mode 100644
index 00000000000..1ac9d261ca5
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc
@@ -0,0 +1,28 @@
+// Copyright (C) 2019-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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <semaphore>
+
+int main()
+{
+ std::counting_semaphore<-1> sem(2);
+ return 0;
+}
+// { dg-error "static assertion failed" "" { target *-*-* } 0 }
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc
new file mode 100644
index 00000000000..d38cef86cfc
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc
@@ -0,0 +1,55 @@
+// Copyright (C) 2019-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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <semaphore>
+#include <limits>
+#include <cstddef>
+#include <testsuite_hooks.h>
+
+void test01()
+{
+ std::counting_semaphore<10> s(3);
+
+ s.acquire();
+ VERIFY( s.try_acquire() );
+ VERIFY( s.try_acquire() );
+ VERIFY( !s.try_acquire() );
+ s.release();
+ VERIFY( s.try_acquire() );
+}
+
+void test02()
+{
+ std::binary_semaphore s(1);
+
+ s.acquire();
+ VERIFY( !s.try_acquire() );
+ s.release();
+ VERIFY( s.try_acquire() );
+}
+
+
+int main()
+{
+ test01();
+ test02();
+}
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc
new file mode 100644
index 00000000000..965554a3c28
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc
@@ -0,0 +1,85 @@
+// Copyright (C) 2019-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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <semaphore>
+#include <chrono>
+#include <thread>
+#include <atomic>
+#include <testsuite_hooks.h>
+
+void test01()
+{
+ using namespace std::chrono_literals;
+ std::counting_semaphore<10> s(2);
+ s.acquire();
+
+ auto const dur = 250ms;
+ {
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( s.try_acquire_for(dur) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff < dur );
+ }
+
+ {
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( !s.try_acquire_for(dur) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff >= dur );
+ }
+}
+
+void test02()
+{
+ using namespace std::chrono_literals;
+ std::binary_semaphore s(1);
+ std::atomic<int> a(0), b(0);
+ std::thread t([&] {
+ a.wait(0);
+ auto const dur = 250ms;
+ VERIFY( !s.try_acquire_for(dur) );
+ b++;
+ b.notify_one();
+
+ a.wait(1);
+ VERIFY( s.try_acquire_for(dur) );
+ b++;
+ b.notify_one();
+ });
+ t.detach();
+
+ s.acquire();
+ a++;
+ a.notify_one();
+ b.wait(0);
+ s.release();
+ a++;
+ a.notify_one();
+
+ b.wait(1);
+}
+
+int main()
+{
+ test01();
+ test02();
+}
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_futex.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_futex.cc
new file mode 100644
index 00000000000..5e05606e97f
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_futex.cc
@@ -0,0 +1,51 @@
+// Copyright (C) 2019-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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <semaphore>
+#include <limits>
+#include <cstddef>
+#include <testsuite_hooks.h>
+
+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
+void test01()
+{
+ // the implementation optimizes for values of least_max_t that can fit
+ // in a futex, make sure we cover the case where least_max_t doesn't
+ auto constexpr least_max_t = std::numeric_limits<std::ptrdiff_t>::max();
+ std::counting_semaphore<least_max_t> s(3);
+
+ s.acquire();
+ VERIFY( s.try_acquire() );
+ VERIFY( s.try_acquire() );
+ VERIFY( !s.try_acquire() );
+ s.release();
+ VERIFY( s.try_acquire() );
+}
+
+#endif
+
+int main()
+{
+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
+ test01();
+#endif
+}
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc
new file mode 100644
index 00000000000..bf99fd3cf8f
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc
@@ -0,0 +1,169 @@
+// Copyright (C) 2019-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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <semaphore>
+#include <chrono>
+#include <thread>
+#include <atomic>
+#include <testsuite_hooks.h>
+
+#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
+ // The implementation supports posix as an implementation strategy
+ // make sure we cover that case
+#define _GLIBCXX_REQUIRE_POSIX_SEMAPHORE
+
+void test01()
+{
+ std::counting_semaphore<10> s(3);
+
+ s.acquire();
+ VERIFY( s.try_acquire() );
+ VERIFY( s.try_acquire() );
+ VERIFY( !s.try_acquire() );
+ s.release();
+ VERIFY( s.try_acquire() );
+}
+
+void test02()
+{
+ using namespace std::chrono_literals;
+ std::counting_semaphore<10> s(2);
+ s.acquire();
+
+ auto const dur = 250ms;
+ {
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( s.try_acquire_for(dur) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff < dur );
+ }
+
+ {
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( !s.try_acquire_for(dur) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff >= dur );
+ }
+}
+
+void test03()
+{
+ using namespace std::chrono_literals;
+ std::binary_semaphore s(1);
+ std::atomic<int> a(0), b(0);
+ std::thread t([&] {
+ a.wait(0);
+ auto const dur = 250ms;
+ VERIFY( !s.try_acquire_for(dur) );
+ b++;
+ b.notify_one();
+
+ a.wait(1);
+ VERIFY( s.try_acquire_for(dur) );
+ b++;
+ b.notify_one();
+ });
+ t.detach();
+
+ s.acquire();
+ a++;
+ a.notify_one();
+ b.wait(0);
+ s.release();
+ a++;
+ a.notify_one();
+
+ b.wait(1);
+}
+
+void test04()
+{
+ using namespace std::chrono_literals;
+ std::counting_semaphore<10> s(2);
+ s.acquire();
+
+ auto const dur = 250ms;
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( s.try_acquire_until(at) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff < dur );
+ }
+
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( !s.try_acquire_until(at) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff >= dur );
+ }
+}
+
+void test05()
+{
+ using namespace std::chrono_literals;
+ std::binary_semaphore s(1);
+ std::atomic<int> a(0), b(0);
+ std::thread t([&] {
+ a.wait(0);
+ auto const dur = 250ms;
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ VERIFY( !s.try_acquire_until(at) );
+
+ b++;
+ b.notify_one();
+ }
+
+ a.wait(1);
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ VERIFY( s.try_acquire_until(at) );
+ }
+ b++;
+ b.notify_one();
+ });
+ t.detach();
+
+ s.acquire();
+ a++;
+ a.notify_one();
+ b.wait(0);
+ s.release();
+ a++;
+ a.notify_one();
+
+ b.wait(1);
+}
+#endif
+
+int main()
+{
+#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
+ test01();
+ test02();
+ test03();
+ test04();
+ test05();
+#endif
+}
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc
new file mode 100644
index 00000000000..cc67c5c0bf0
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc
@@ -0,0 +1,94 @@
+// Copyright (C) 2019-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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <semaphore>
+#include <chrono>
+#include <thread>
+#include <atomic>
+#include <testsuite_hooks.h>
+
+void test01()
+{
+ using namespace std::chrono_literals;
+ std::counting_semaphore<10> s(2);
+ s.acquire();
+
+ auto const dur = 250ms;
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( s.try_acquire_until(at) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff < dur );
+ }
+
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( !s.try_acquire_until(at) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff >= dur );
+ }
+}
+
+void test02()
+{
+ using namespace std::chrono_literals;
+ std::binary_semaphore s(1);
+ std::atomic<int> a(0), b(0);
+ std::thread t([&] {
+ a.wait(0);
+ auto const dur = 250ms;
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ VERIFY( !s.try_acquire_until(at) );
+
+ b++;
+ b.notify_one();
+ }
+
+ a.wait(1);
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ VERIFY( s.try_acquire_until(at) );
+ }
+ b++;
+ b.notify_one();
+ });
+ t.detach();
+
+ s.acquire();
+ a++;
+ a.notify_one();
+ b.wait(0);
+ s.release();
+ a++;
+ a.notify_one();
+
+ b.wait(1);
+}
+
+int main()
+{
+ test01();
+ test02();
+}
--
2.26.2
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] Add C++2a synchronization support
2020-06-06 0:29 ` Thomas Rodgers
@ 2020-07-08 16:43 ` Jonathan Wakely
2020-08-03 14:09 ` Jonathan Wakely
1 sibling, 0 replies; 50+ messages in thread
From: Jonathan Wakely @ 2020-07-08 16:43 UTC (permalink / raw)
To: Thomas Rodgers; +Cc: gcc-patches, libstdc++, trodgers
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 ...
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] Add C++2a synchronization support
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
1 sibling, 1 reply; 50+ messages in thread
From: Jonathan Wakely @ 2020-08-03 14:09 UTC (permalink / raw)
To: Thomas Rodgers; +Cc: gcc-patches, libstdc++, trodgers
On 05/06/20 17:29 -0700, Thomas Rodgers wrote:
>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
>@@ -0,0 +1,272 @@
>+// -*- 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_base.h
>+ * This is an internal header file, included by other library headers.
>+ * Do not attempt to use it directly. @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>
>+
>+namespace std _GLIBCXX_VISIBILITY(default)
>+{
>+_GLIBCXX_BEGIN_NAMESPACE_VERSION
>+
>+#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
>+ template<ptrdiff_t __least_max_value>
>+ struct __platform_semaphore
>+ {
I think we want to delete the copy constructor and copy assignment
operator for this type and __atomic_semaphore.
>+ using __clock_t = chrono::system_clock;
>+
>+ explicit __platform_semaphore(ptrdiff_t __count) noexcept
>+ {
>+ static_assert( __least_max_value <= SEM_VALUE_MAX, "");
>+ auto __e = sem_init(&_M_semaphore, 0, __count);
>+ if (__e)
>+ std::terminate();
I don't think we should bother checking the return value. The failure
conditions are EINVAL if __count > SEM_VALUE_MAX, and ENOSYS if the
second argument is non-zero. Both conditions should be impossible.
>+ }
>+
>+ ~__platform_semaphore()
>+ {
>+ auto __e = sem_destroy(&_M_semaphore);
>+ if (__e)
>+ std::terminate();
Likewise here. The only error condition is EINVAL, which is impossible
if _M_semaphore is a valid semaphore, which the constructor already
ensured.
If somehow _M_semaphore is invalid it means the user broke something
(e.g. by scribbling over *this) and we don't need to detect and handle
that case.
>+ }
>+
>+ _GLIBCXX_ALWAYS_INLINE void
>+ acquire() noexcept
>+ {
>+ auto __err = sem_wait(&_M_semaphore);
>+ if (__err)
>+ std::terminate();
What about the EINTR case where the wait is interrupted by a signal?
I think that should retry, but it shouldn't terminate.
>+ template<typename _Duration>
>+ bool
>+ __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();
I think this needs to handle both EINTR and EINVAL:
EINVAL The value of abs_timeout.tv_nsecs is less than 0, or greater than or equal to 1000 million.
Alternatively, just return immediately without waiting for a time
before the epoch, i.e. __abs_time < time_point<__clock_t>{}
Otherwise time_point(-20ns) will cause EINVAL and terminate.
>+ return true;
>+ }
>+
>+ template<typename _Clock, typename _Duration>
>+ bool
>+ try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept
>+ {
>+ if constexpr (std::is_same<__clock_t, _Clock>::value)
>+ {
>+ 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
>+ {
Do we want a static_assert(is_integral_v<_Tp>) or similar?
>+ explicit __atomic_semaphore(_Tp __count)
Add noexcept?
>+ : _M_a(__count)
>+ { }
>+
>+ _GLIBCXX_ALWAYS_INLINE void
>+ acquire() noexcept
>+ {
>+ auto const __pred = [this]
>+ {
>+ auto __old = __atomic_impl::load(&this->_M_a,
>+ memory_order::acquire);
Would it be simpler to just use a local atomic_ref to manipulate _M_a
here, and in the other members performing atomic ops on that member?
>+ 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);
Strictly speaking, this __atomic_wait would violate the "all accesses
to that object shall exclusively occur through those atomic_ref
instances" rule. But we know that it's actually OK because
__atomic_wait is only performing atomic ops on the same location.
>+ }
>+
>+ 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(__alignof__(_Tp)) _Tp _M_a;
>+ };
>+
>+#ifdef _GLIBCXX_REQUIRE_POSIX_SEMAPHORE
What is this case for? This macro seems to only exist for use by a
single testcase.
Is it supposed to be something users can set? If so, it needs to be
documented as ABI-breaking and as implying that counting_semaphore
cannot be use with counts higher than SEM_VALUE_MAX.
>+ template<ptrdiff_t __least_max_value>
>+ using __semaphore_base = __platform_semaphore<__least_max_value>;
>+#else
>+# ifdef _GLIBCXX_HAVE_LINUX_FUTEX
>+ template<ptrdiff_t __least_max_value>
>+ using __semaphore_base = conditional_t<(
>+ __least_max_value >= 0
This condition is redundant, we already know that counting_semaphore
has checked the value is non-negative.
>+ && __least_max_value <= __detail::__platform_wait_max_value),
>+ __atomic_semaphore<__detail::__platform_wait_t>,
>+ __atomic_semaphore<ptrdiff_t>>;
>+
>+// __platform_semaphore
>+# elif defined _GLIBCXX_HAVE_POSIX_SEMAPHORE
>+ template<ptrdiff_t __least_max_value>
>+ using __semaphore_base = conditional_t<(
>+ __least_max_value >= 0
Redundant condition again.
>+ && __least_max_value <= SEM_VALUE_MAX),
>+ __platform_semaphore<__least_max_value>,
>+ __atomic_semaphore<ptrdiff_t>>;
>+# else
>+ template<ptrdiff_t __least_max_value>
>+ using __semaphore_base = __atomic_semaphore<ptrdiff_t>;
>+# 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); }
This can't be right. _M_i is of type _Tp in std::atomic<_Tp>, it doesn't
have a wait member function.
If this compiles it suggests there are no tests for these members on
the primary template, e.g. something like:
void
test01()
{
struct S { int i; };
std:::atomic<S> s;
s.wait();
}
>+
>+ // 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,
Newline after the return type.
>+ typename std::atomic<_Tp>::value_type __old) noexcept
>+ { __a->wait(__old); }
>+
>+ template<typename _Tp>
>+ inline void atomic_wait_explicit(const atomic<_Tp>* __a,
And here.
>+ 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..aa5299d9fdd
>--- /dev/null
>+++ b/libstdc++-v3/include/std/latch
>@@ -0,0 +1,90 @@
>+// <latch> -*- C++ -*-
>+
>+// 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.
>+ */
>+
>+#ifndef _GLIBCXX_LATCH
>+#define _GLIBCXX_LATCH
>+
>+#pragma GCC system_header
>+
>+#if __cplusplus > 201703L
>+#define __cpp_lib_latch 201907L
>+
>+#include <bits/atomic_base.h>
>+#include <ext/numeric_traits.h>
>+
>+namespace std _GLIBCXX_VISIBILITY(default)
>+{
>+_GLIBCXX_BEGIN_NAMESPACE_VERSION
>+
>+ class latch
>+ {
>+ public:
>+ static constexpr
>+ _GLIBCXX_ALWAYS_INLINE ptrdiff_t
I don't think we need _GLIBCXX_ALWAYS_INLINE here.
>+ max() noexcept
>+ { return __gnu_cxx::__numeric_traits<ptrdiff_t>::__max; }
>+
>+ constexpr explicit latch(ptrdiff_t __expected) : _M_a(__expected) { }
Add noexcept.
>+
>+ ~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);
It looks like this could use atomic_ref too, although there'd be less
benefit here.
>+ 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(__alignof__(ptrdiff_t)) ptrdiff_t _M_a;
>+ };
>+_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..90cf3244647
>--- /dev/null
>+++ b/libstdc++-v3/include/std/semaphore
>@@ -0,0 +1,86 @@
>+// <semaphore> -*- C++ -*-
>+
>+// 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.
>+ */
>+
>+#ifndef _GLIBCXX_SEMAPHORE
>+#define _GLIBCXX_SEMAPHORE
>+
>+#pragma GCC system_header
>+
>+#if __cplusplus > 201703L
>+#define __cpp_lib_semaphore 201907L
>+#include <bits/semaphore_base.h>
>+#include <ext/numeric_traits.h>
>+
>+namespace std _GLIBCXX_VISIBILITY(default)
>+{
>+_GLIBCXX_BEGIN_NAMESPACE_VERSION
>+
>+ template<ptrdiff_t __least_max_value =
>+ __gnu_cxx::__numeric_traits<ptrdiff_t>::__max>
>+ class counting_semaphore
>+ {
>+ static_assert(__least_max_value >=0, "");
Add a space before the 0. Get rid of the empty string literal.
>+
>+ __semaphore_base<__least_max_value> _M_sem;
"base" seems a bit misleading here when it's a member not a base
class. Would __semaphore_impl be better?
>+
>+ 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)
We can add noexcept here, since we know neither of the
__semaphore_base types will throw. If you envision alternative
implementations in future, then it would be futureproof to use
noexcept(noexcept(_M_sem.release(1)))
>+ { _M_sem.release(__update); }
>+
>+ void acquire()
Same here.
>+ { _M_sem.acquire(); }
>+
>+ bool try_acquire() noexcept
>+ { return _M_sem.try_acquire(); }
>+
>+ template<class _Rep, class _Period>
>+ bool try_acquire_for(const std::chrono::duration<_Rep, _Period>& __rel_time)
Newline after the return type (the line is too long otherwise).
This one can potentially throw because of the duration arithmetic on
_Rep objects.
>+ { return _M_sem.try_acquire_for(__rel_time); }
>+
>+ template<class _Clock, class _Duration>
>+ bool try_acquire_until(const std::chrono::time_point<_Clock, _Duration>& __abs_time)
Newline after the return type (I think it's still going to be too
long).
>+ { 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 c6bde2cfbda..f09da3344f7 100644
>--- a/libstdc++-v3/include/std/version
>+++ b/libstdc++-v3/include/std/version
>@@ -189,6 +189,8 @@
> #endif
> #define __cpp_lib_type_identity 201806L
> #define __cpp_lib_unwrap_ref 201811L
>+#define __cpp_lib_semaphore 201907L
>+#define __cpp_lib_latch 201907L
You're adding these macros in the freestanding section, and these
aren't freestanding headers. They need to be in the _GLIBCXX_HOSTED
block, and the macros need to 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" }
>+// { dg-do run { target c++2a } }
>+// { dg-require-effective-target pthread }
>+// { dg-require-gthreads "" }
>+
>+// 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.
>+
>+// You should have received a copy of the GNU General Public License along
>+// with this library; see the file COPYING3. If not see
>+// <http://www.gnu.org/licenses/>.
>+
>+#include <atomic>
>+#include <thread>
>+#include <mutex>
>+#include <condition_variable>
>+#include <chrono>
>+#include <type_traits>
>+
>+#include <testsuite_hooks.h>
>+
>+template<typename Tp>
>+Tp check_wait_notify(Tp val1, Tp val2)
>+{
>+ using namespace std::literals::chrono_literals;
>+
>+ std::mutex m;
>+ std::condition_variable cv;
>+
>+ Tp aa = val1;
>+ std::atomic_ref<Tp> a(aa);
>+ std::thread t([&]
>+ {
>+ cv.notify_one();
>+ a.wait(val1);
>+ if (a.load() != val2)
>+ a = val1;
>+ });
>+ std::unique_lock<std::mutex> l(m);
>+ cv.wait(l);
>+ std::this_thread::sleep_for(100ms);
>+ a.store(val2);
>+ a.notify_one();
>+ t.join();
>+ return a.load();
>+}
>+
>+template<typename Tp,
>+ bool = std::is_integral_v<Tp>
>+ || std::is_floating_point_v<Tp>>
>+struct check;
>+
>+template<typename Tp>
>+struct check<Tp, true>
>+{
>+ check()
>+ {
>+ Tp a = 0;
>+ Tp b = 42;
>+ VERIFY(check_wait_notify(a, b) == b);
>+ }
>+};
>+
>+template<typename Tp>
>+struct check<Tp, false>
>+{
>+ check(Tp b)
>+ {
>+ Tp a;
>+ VERIFY(check_wait_notify(a, b) == b);
>+ }
>+};
>+
>+struct foo
>+{
>+ long a = 0;
>+ long b = 0;
>+
>+ foo& operator=(foo const&) = default;
>+
>+ friend bool
>+ operator==(foo const& rhs, foo const& lhs)
>+ { return rhs.a == lhs.a && rhs.b == lhs.b; }
>+};
>+
>+int
>+main ()
>+{
>+ check<long>();
>+ check<double>();
>+ check<foo>({42, 48});
>+ return 0;
>+}
>diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc
>new file mode 100644
>index 00000000000..b9fc063c66f
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc
>@@ -0,0 +1,59 @@
>+// { dg-options "-std=gnu++2a -pthread" }
>+// { dg-do run { target c++2a } }
>+// { dg-require-effective-target pthread }
>+// { dg-require-gthreads "" }
>+
>+// 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.
>+
>+// You should have received a copy of the GNU General Public License along
>+// with this library; see the file COPYING3. If not see
>+// <http://www.gnu.org/licenses/>.
>+
>+#include <atomic>
>+#include <thread>
>+#include <mutex>
>+#include <condition_variable>
>+#include <type_traits>
>+#include <chrono>
>+
>+#include <testsuite_hooks.h>
>+
>+int
>+main ()
>+{
>+ using namespace std::literals::chrono_literals;
>+
>+ std::mutex m;
>+ std::condition_variable cv;
>+
>+ std::atomic<bool> a(false);
>+ std::atomic<bool> b(false);
>+ std::thread t([&]
>+ {
>+ cv.notify_one();
>+ a.wait(false);
>+ if (a.load())
>+ {
>+ b.store(true);
>+ }
>+ });
>+ std::unique_lock<std::mutex> l(m);
>+ cv.wait(l);
>+ std::this_thread::sleep_for(100ms);
>+ a.store(true);
>+ a.notify_one();
>+ t.join();
>+ VERIFY( b.load() );
>+ return 0;
>+}
>diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc
>new file mode 100644
>index 00000000000..1d032085752
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc
>@@ -0,0 +1,32 @@
>+// { dg-options "-std=gnu++2a -pthread -latomic -L../../libatomic/.libs" }
>+// { dg-do run { target c++2a } }
>+// { dg-require-effective-target pthread }
>+// { dg-require-gthreads "" }
>+
>+// 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.
>+
>+// You should have received a copy of the GNU General Public License along
>+// with this library; see the file COPYING3. If not see
>+// <http://www.gnu.org/licenses/>.
>+
>+#include "generic.h"
>+
>+int
>+main ()
>+{
>+ check<float> f;
>+ check<double> d;
>+ check<long double> l;
>+ return 0;
>+}
>diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h
>new file mode 100644
>index 00000000000..0da374ece87
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h
>@@ -0,0 +1,88 @@
>+// -*- C++ -*- header.
This isn't a header. I don't think this modeline should be here.
The test seems to be missing any dg-options, target selectors etc.
>+// 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.
>+
>+// You should have received a copy of the GNU General Public License along
>+// with this library; see the file COPYING3. If not see
>+// <http://www.gnu.org/licenses/>.
>+
>+#include <atomic>
>+#include <thread>
>+#include <mutex>
>+#include <condition_variable>
>+#include <chrono>
>+
>+#include <testsuite_hooks.h>
>+
>+template<typename Tp>
>+Tp check_wait_notify(Tp val1, Tp val2)
>+{
>+ using namespace std::literals::chrono_literals;
>+
>+ std::mutex m;
>+ std::condition_variable cv;
>+
>+ std::atomic<Tp> a(val1);
>+ std::thread t([&]
>+ {
>+ cv.notify_one();
>+ a.wait(val1);
>+ if (a.load() != val2)
>+ a = val1;
>+ });
>+ std::unique_lock<std::mutex> l(m);
>+ cv.wait(l);
>+ std::this_thread::sleep_for(100ms);
>+ a.store(val2);
>+ a.notify_one();
>+ t.join();
>+ return a.load();
>+}
>+
>+template<typename Tp>
>+Tp check_atomic_wait_notify(Tp val1, Tp val2)
>+{
>+ using namespace std::literals::chrono_literals;
>+
>+ std::mutex m;
>+ std::condition_variable cv;
>+
>+ std::atomic<Tp> a(val1);
>+ std::thread t([&]
>+ {
>+ cv.notify_one();
>+ std::atomic_wait(&a, val1);
>+ if (a.load() != val2)
>+ a = val1;
>+ });
>+ std::unique_lock<std::mutex> l(m);
>+ cv.wait(l);
>+ std::this_thread::sleep_for(100ms);
>+ a.store(val2);
>+ std::atomic_notify_one(&a);
>+ t.join();
>+ return a.load();
>+}
>+
>+template<typename Tp>
>+struct check
>+{
>+ check()
>+ {
>+ Tp a = 0;
>+ Tp b = 42;
>+ VERIFY(check_wait_notify(a, b) == b);
>+ VERIFY(check_atomic_wait_notify(a, b) == b);
>+ }
>+};
>diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc
>new file mode 100644
>index 00000000000..2afd19a7d14
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc
>@@ -0,0 +1,56 @@
>+// { dg-options "-std=gnu++2a -pthread" }
>+// { dg-do run { target c++2a } }
>+// { dg-require-effective-target pthread }
>+// { dg-require-gthreads "" }
>+
>+// 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.
>+
>+// You should have received a copy of the GNU General Public License along
>+// with this library; see the file COPYING3. If not see
>+// <http://www.gnu.org/licenses/>.
>+
>+#include "generic.h"
>+
>+int
>+main ()
>+{
>+ // check<bool> bb;
>+ check<char> ch;
>+ check<signed char> sch;
>+ check<unsigned char> uch;
>+ check<short> s;
>+ check<unsigned short> us;
>+ check<int> i;
>+ check<unsigned int> ui;
>+ check<long> l;
>+ check<unsigned long> ul;
>+ check<long long> ll;
>+ check<unsigned long long> ull;
>+
>+ check<wchar_t> wch;
>+ check<char8_t> ch8;
>+ check<char16_t> ch16;
>+ check<char32_t> ch32;
>+
>+ check<int8_t> i8;
>+ check<int16_t> i16;
>+ check<int32_t> i32;
>+ check<int64_t> i64;
>+
>+ check<uint8_t> u8;
>+ check<uint16_t> u16;
>+ check<uint32_t> u32;
>+ check<uint64_t> u64;
>+ return 0;
>+}
>diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc
>new file mode 100644
>index 00000000000..8531bb2e788
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc
>@@ -0,0 +1,59 @@
>+// { dg-options "-std=gnu++2a -pthread" }
>+// { dg-do run { target c++2a } }
>+// { dg-require-effective-target pthread }
>+// { dg-require-gthreads "" }
>+
>+// 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.
>+
>+// You should have received a copy of the GNU General Public License along
>+// with this library; see the file COPYING3. If not see
>+// <http://www.gnu.org/licenses/>.
>+
>+#include <atomic>
>+#include <thread>
>+#include <mutex>
>+#include <condition_variable>
>+#include <type_traits>
>+#include <chrono>
>+
>+#include <testsuite_hooks.h>
>+
>+int
>+main ()
>+{
>+ using namespace std::literals::chrono_literals;
>+
>+ std::mutex m;
>+ std::condition_variable cv;
>+
>+ long aa;
>+ long bb;
>+
>+ std::atomic<long*> a(nullptr);
>+ std::thread t([&]
>+ {
>+ cv.notify_one();
>+ a.wait(nullptr);
>+ if (a.load() == &aa)
>+ a.store(&bb);
>+ });
>+ std::unique_lock<std::mutex> l(m);
>+ cv.wait(l);
>+ std::this_thread::sleep_for(100ms);
>+ a.store(&aa);
>+ a.notify_one();
>+ t.join();
>+ VERIFY( a.load() == &bb);
>+ return 0;
>+}
>diff --git a/libstdc++-v3/testsuite/30_threads/latch/1.cc b/libstdc++-v3/testsuite/30_threads/latch/1.cc
>new file mode 100644
>index 00000000000..aa203cdf525
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/30_threads/latch/1.cc
>@@ -0,0 +1,27 @@
>+// 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.
>+
>+// You should have received a copy of the GNU General Public License along
>+// with this library; see the file COPYING3. If not see
>+// <http://www.gnu.org/licenses/>.
>+
>+// { dg-options "-std=gnu++2a" }
>+// { dg-do compile { target c++2a } }
>+
>+#include <latch>
>+
>+#ifndef __cpp_lib_latch
>+# error "Feature-test macro for latch missing in <latch>"
>+#elif __cpp_lib_latch!= 201907L
>+# error "Feature-test macro for latch has wrong value in <latch>"
>+#endif
>diff --git a/libstdc++-v3/testsuite/30_threads/latch/2.cc b/libstdc++-v3/testsuite/30_threads/latch/2.cc
>new file mode 100644
>index 00000000000..756727f33b3
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/30_threads/latch/2.cc
>@@ -0,0 +1,27 @@
>+// Copyright (C) 2019-2020 Free Software Foundation, Inc.
Just 2020.
>+//
>+// 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.
>+
>+// You should have received a copy of the GNU General Public License along
>+// with this library; see the file COPYING3. If not see
>+// <http://www.gnu.org/licenses/>.
>+
>+// { dg-options "-std=gnu++2a" }
>+// { dg-do compile { target c++2a } }
>+
>+#include <version>
>+
>+#ifndef __cpp_lib_latch
>+# error "Feature-test macro for latch missing in <version>"
>+#elif __cpp_lib_latch != 201907L
>+# error "Feature-test macro for latch has wrong value in <version>"
>+#endif
>diff --git a/libstdc++-v3/testsuite/30_threads/latch/3.cc b/libstdc++-v3/testsuite/30_threads/latch/3.cc
>new file mode 100644
>index 00000000000..10bb500d261
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/30_threads/latch/3.cc
>@@ -0,0 +1,50 @@
>+// Copyright (C) 2019-2020 Free Software Foundation, Inc.
>+//
Just 2020.
>+// 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.
>+
>+// You should have received a copy of the GNU General Public License along
>+// with this library; see the file COPYING3. If not see
>+// <http://www.gnu.org/licenses/>.
>+
>+// { dg-options "-std=gnu++2a -pthread" }
>+// { dg-do run { target c++2a } }
>+// { dg-require-effective-target pthread }
>+// { dg-require-gthreads "" }
>+//
>+#include <latch>
>+#include <atomic>
>+#include <thread>
>+#include <testsuite_hooks.h>
>+
>+int main()
>+{
>+ std::atomic<int> a(0);
>+
>+ std::latch l(3);
>+
>+ VERIFY( !l.try_wait() );
>+
>+ auto fn = [&]
>+ {
>+ ++a;
>+ l.count_down();
>+ };
>+
>+ std::thread t0(fn);
>+ std::thread t1(fn);
>+
>+ l.arrive_and_wait();
>+ t0.join();
>+ t1.join();
>+
>+ VERIFY( l.try_wait() );
>+}
>diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/1.cc b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc
>new file mode 100644
>index 00000000000..1bbca687fc3
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc
>@@ -0,0 +1,27 @@
>+// 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.
>+
>+// You should have received a copy of the GNU General Public License along
>+// with this library; see the file COPYING3. If not see
>+// <http://www.gnu.org/licenses/>.
>+
>+// { dg-options "-std=gnu++2a" }
>+// { dg-do compile { target c++2a } }
>+
>+#include <semaphore>
>+
>+#ifndef __cpp_lib_semaphore
>+# error "Feature-test macro for semaphore missing in <semaphore>"
>+#elif __cpp_lib_semaphore != 201907L
>+# error "Feature-test macro for semaphore has wrong value in <semaphore>"
>+#endif
>diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/2.cc b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc
>new file mode 100644
>index 00000000000..b96b8a59c64
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc
>@@ -0,0 +1,27 @@
>+// Copyright (C) 2019-2020 Free Software Foundation, Inc.
Just 2020.
>+//
>+// 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.
>+
>+// You should have received a copy of the GNU General Public License along
>+// with this library; see the file COPYING3. If not see
>+// <http://www.gnu.org/licenses/>.
>+
>+// { dg-options "-std=gnu++2a" }
>+// { dg-do compile { target c++2a } }
>+
>+#include <version>
>+
>+#ifndef __cpp_lib_semaphore
>+# error "Feature-test macro for semaphore missing in <version>"
>+#elif __cpp_lib_semaphore != 201907L
>+# error "Feature-test macro for semaphore has wrong value in <version>"
>+#endif
>diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc b/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc
>new file mode 100644
>index 00000000000..1ac9d261ca5
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc
>@@ -0,0 +1,28 @@
>+// Copyright (C) 2019-2020 Free Software Foundation, Inc.
Just 2020.
>+//
>+// 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.
>+
>+// You should have received a copy of the GNU General Public License along
>+// with this library; see the file COPYING3. If not see
>+// <http://www.gnu.org/licenses/>.
>+
>+// { dg-options "-std=gnu++2a" }
>+// { dg-do compile { target c++2a } }
>+
>+#include <semaphore>
>+
>+int main()
>+{
>+ std::counting_semaphore<-1> sem(2);
>+ return 0;
>+}
>+// { dg-error "static assertion failed" "" { target *-*-* } 0 }
>diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc
>new file mode 100644
>index 00000000000..d38cef86cfc
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc
>@@ -0,0 +1,55 @@
>+// Copyright (C) 2019-2020 Free Software Foundation, Inc.
Just 2020.
>+//
>+// 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.
>+
>+// You should have received a copy of the GNU General Public License along
>+// with this library; see the file COPYING3. If not see
>+// <http://www.gnu.org/licenses/>.
>+
>+// { dg-options "-std=gnu++2a -pthread" }
>+// { dg-do run { target c++2a } }
>+// { dg-require-effective-target pthread }
>+// { dg-require-gthreads "" }
>+
>+#include <semaphore>
>+#include <limits>
>+#include <cstddef>
>+#include <testsuite_hooks.h>
>+
>+void test01()
>+{
>+ std::counting_semaphore<10> s(3);
>+
>+ s.acquire();
>+ VERIFY( s.try_acquire() );
>+ VERIFY( s.try_acquire() );
>+ VERIFY( !s.try_acquire() );
>+ s.release();
>+ VERIFY( s.try_acquire() );
>+}
>+
>+void test02()
>+{
>+ std::binary_semaphore s(1);
>+
>+ s.acquire();
>+ VERIFY( !s.try_acquire() );
>+ s.release();
>+ VERIFY( s.try_acquire() );
>+}
>+
>+
>+int main()
>+{
>+ test01();
>+ test02();
>+}
>diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc
>new file mode 100644
>index 00000000000..965554a3c28
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc
>@@ -0,0 +1,85 @@
>+// Copyright (C) 2019-2020 Free Software Foundation, Inc.
Just 2020.
>+//
>+// 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.
>+
>+// You should have received a copy of the GNU General Public License along
>+// with this library; see the file COPYING3. If not see
>+// <http://www.gnu.org/licenses/>.
>+
>+// { dg-options "-std=gnu++2a -pthread" }
>+// { dg-do run { target c++2a } }
>+// { dg-require-effective-target pthread }
>+// { dg-require-gthreads "" }
>+
>+#include <semaphore>
>+#include <chrono>
>+#include <thread>
>+#include <atomic>
>+#include <testsuite_hooks.h>
>+
>+void test01()
>+{
>+ using namespace std::chrono_literals;
>+ std::counting_semaphore<10> s(2);
>+ s.acquire();
>+
>+ auto const dur = 250ms;
>+ {
>+ auto const t0 = std::chrono::steady_clock::now();
>+ VERIFY( s.try_acquire_for(dur) );
>+ auto const diff = std::chrono::steady_clock::now() - t0;
>+ VERIFY( diff < dur );
>+ }
>+
>+ {
>+ auto const t0 = std::chrono::steady_clock::now();
>+ VERIFY( !s.try_acquire_for(dur) );
>+ auto const diff = std::chrono::steady_clock::now() - t0;
>+ VERIFY( diff >= dur );
>+ }
>+}
>+
>+void test02()
>+{
>+ using namespace std::chrono_literals;
>+ std::binary_semaphore s(1);
>+ std::atomic<int> a(0), b(0);
>+ std::thread t([&] {
>+ a.wait(0);
>+ auto const dur = 250ms;
>+ VERIFY( !s.try_acquire_for(dur) );
>+ b++;
>+ b.notify_one();
>+
>+ a.wait(1);
>+ VERIFY( s.try_acquire_for(dur) );
>+ b++;
>+ b.notify_one();
>+ });
>+ t.detach();
>+
>+ s.acquire();
>+ a++;
>+ a.notify_one();
>+ b.wait(0);
>+ s.release();
>+ a++;
>+ a.notify_one();
>+
>+ b.wait(1);
>+}
>+
>+int main()
>+{
>+ test01();
>+ test02();
>+}
>diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_futex.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_futex.cc
>new file mode 100644
>index 00000000000..5e05606e97f
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_futex.cc
>@@ -0,0 +1,51 @@
>+// Copyright (C) 2019-2020 Free Software Foundation, Inc.
Just 2020.
>+//
>+// 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.
>+
>+// You should have received a copy of the GNU General Public License along
>+// with this library; see the file COPYING3. If not see
>+// <http://www.gnu.org/licenses/>.
>+
>+// { dg-options "-std=gnu++2a -pthread" }
>+// { dg-do run { target c++2a } }
>+// { dg-require-effective-target pthread }
>+// { dg-require-gthreads "" }
>+
>+#include <semaphore>
>+#include <limits>
>+#include <cstddef>
>+#include <testsuite_hooks.h>
>+
>+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
>+void test01()
>+{
>+ // the implementation optimizes for values of least_max_t that can fit
>+ // in a futex, make sure we cover the case where least_max_t doesn't
>+ auto constexpr least_max_t = std::numeric_limits<std::ptrdiff_t>::max();
>+ std::counting_semaphore<least_max_t> s(3);
>+
>+ s.acquire();
>+ VERIFY( s.try_acquire() );
>+ VERIFY( s.try_acquire() );
>+ VERIFY( !s.try_acquire() );
>+ s.release();
>+ VERIFY( s.try_acquire() );
>+}
>+
>+#endif
>+
>+int main()
>+{
>+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
>+ test01();
>+#endif
>+}
>diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc
>new file mode 100644
>index 00000000000..bf99fd3cf8f
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc
>@@ -0,0 +1,169 @@
>+// Copyright (C) 2019-2020 Free Software Foundation, Inc.
Just 2020.
>+//
>+// 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.
>+
>+// You should have received a copy of the GNU General Public License along
>+// with this library; see the file COPYING3. If not see
>+// <http://www.gnu.org/licenses/>.
>+
>+// { dg-options "-std=gnu++2a -pthread" }
>+// { dg-do run { target c++2a } }
>+// { dg-require-effective-target pthread }
>+// { dg-require-gthreads "" }
>+
>+#include <semaphore>
>+#include <chrono>
>+#include <thread>
>+#include <atomic>
>+#include <testsuite_hooks.h>
>+
>+#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
>+ // The implementation supports posix as an implementation strategy
>+ // make sure we cover that case
>+#define _GLIBCXX_REQUIRE_POSIX_SEMAPHORE
This macro is defined too late to do anything. It would ned to be
defined before including <semaphore> to have any effect.
>+void test01()
>+{
>+ std::counting_semaphore<10> s(3);
>+
>+ s.acquire();
>+ VERIFY( s.try_acquire() );
>+ VERIFY( s.try_acquire() );
>+ VERIFY( !s.try_acquire() );
>+ s.release();
>+ VERIFY( s.try_acquire() );
>+}
>+
>+void test02()
>+{
>+ using namespace std::chrono_literals;
>+ std::counting_semaphore<10> s(2);
>+ s.acquire();
>+
>+ auto const dur = 250ms;
>+ {
>+ auto const t0 = std::chrono::steady_clock::now();
>+ VERIFY( s.try_acquire_for(dur) );
>+ auto const diff = std::chrono::steady_clock::now() - t0;
>+ VERIFY( diff < dur );
>+ }
>+
>+ {
>+ auto const t0 = std::chrono::steady_clock::now();
>+ VERIFY( !s.try_acquire_for(dur) );
>+ auto const diff = std::chrono::steady_clock::now() - t0;
>+ VERIFY( diff >= dur );
>+ }
>+}
>+
>+void test03()
>+{
>+ using namespace std::chrono_literals;
>+ std::binary_semaphore s(1);
>+ std::atomic<int> a(0), b(0);
>+ std::thread t([&] {
>+ a.wait(0);
>+ auto const dur = 250ms;
>+ VERIFY( !s.try_acquire_for(dur) );
>+ b++;
>+ b.notify_one();
>+
>+ a.wait(1);
>+ VERIFY( s.try_acquire_for(dur) );
>+ b++;
>+ b.notify_one();
>+ });
>+ t.detach();
>+
>+ s.acquire();
>+ a++;
>+ a.notify_one();
>+ b.wait(0);
>+ s.release();
>+ a++;
>+ a.notify_one();
>+
>+ b.wait(1);
>+}
>+
>+void test04()
>+{
>+ using namespace std::chrono_literals;
>+ std::counting_semaphore<10> s(2);
>+ s.acquire();
>+
>+ auto const dur = 250ms;
>+ {
>+ auto const at = std::chrono::system_clock::now() + dur;
>+ auto const t0 = std::chrono::steady_clock::now();
>+ VERIFY( s.try_acquire_until(at) );
>+ auto const diff = std::chrono::steady_clock::now() - t0;
>+ VERIFY( diff < dur );
>+ }
>+
>+ {
>+ auto const at = std::chrono::system_clock::now() + dur;
>+ auto const t0 = std::chrono::steady_clock::now();
>+ VERIFY( !s.try_acquire_until(at) );
>+ auto const diff = std::chrono::steady_clock::now() - t0;
>+ VERIFY( diff >= dur );
>+ }
>+}
>+
>+void test05()
>+{
>+ using namespace std::chrono_literals;
>+ std::binary_semaphore s(1);
>+ std::atomic<int> a(0), b(0);
>+ std::thread t([&] {
>+ a.wait(0);
>+ auto const dur = 250ms;
>+ {
>+ auto const at = std::chrono::system_clock::now() + dur;
>+ VERIFY( !s.try_acquire_until(at) );
>+
>+ b++;
>+ b.notify_one();
>+ }
>+
>+ a.wait(1);
>+ {
>+ auto const at = std::chrono::system_clock::now() + dur;
>+ VERIFY( s.try_acquire_until(at) );
>+ }
>+ b++;
>+ b.notify_one();
>+ });
>+ t.detach();
>+
>+ s.acquire();
>+ a++;
>+ a.notify_one();
>+ b.wait(0);
>+ s.release();
>+ a++;
>+ a.notify_one();
>+
>+ b.wait(1);
>+}
>+#endif
>+
>+int main()
>+{
>+#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
>+ test01();
>+ test02();
>+ test03();
>+ test04();
>+ test05();
>+#endif
>+}
>diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc
>new file mode 100644
>index 00000000000..cc67c5c0bf0
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc
>@@ -0,0 +1,94 @@
>+// Copyright (C) 2019-2020 Free Software Foundation, Inc.
Just 2020.
>+//
>+// 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.
>+
>+// You should have received a copy of the GNU General Public License along
>+// with this library; see the file COPYING3. If not see
>+// <http://www.gnu.org/licenses/>.
>+
>+// { dg-options "-std=gnu++2a -pthread" }
>+// { dg-do run { target c++2a } }
>+// { dg-require-effective-target pthread }
>+// { dg-require-gthreads "" }
>+
>+#include <semaphore>
>+#include <chrono>
>+#include <thread>
>+#include <atomic>
>+#include <testsuite_hooks.h>
>+
>+void test01()
>+{
>+ using namespace std::chrono_literals;
>+ std::counting_semaphore<10> s(2);
>+ s.acquire();
>+
>+ auto const dur = 250ms;
>+ {
>+ auto const at = std::chrono::system_clock::now() + dur;
>+ auto const t0 = std::chrono::steady_clock::now();
>+ VERIFY( s.try_acquire_until(at) );
>+ auto const diff = std::chrono::steady_clock::now() - t0;
>+ VERIFY( diff < dur );
>+ }
>+
>+ {
>+ auto const at = std::chrono::system_clock::now() + dur;
>+ auto const t0 = std::chrono::steady_clock::now();
>+ VERIFY( !s.try_acquire_until(at) );
>+ auto const diff = std::chrono::steady_clock::now() - t0;
>+ VERIFY( diff >= dur );
>+ }
>+}
>+
>+void test02()
>+{
>+ using namespace std::chrono_literals;
>+ std::binary_semaphore s(1);
>+ std::atomic<int> a(0), b(0);
>+ std::thread t([&] {
>+ a.wait(0);
>+ auto const dur = 250ms;
>+ {
>+ auto const at = std::chrono::system_clock::now() + dur;
>+ VERIFY( !s.try_acquire_until(at) );
>+
>+ b++;
>+ b.notify_one();
>+ }
>+
>+ a.wait(1);
>+ {
>+ auto const at = std::chrono::system_clock::now() + dur;
>+ VERIFY( s.try_acquire_until(at) );
>+ }
>+ b++;
>+ b.notify_one();
>+ });
>+ t.detach();
>+
>+ s.acquire();
>+ a++;
>+ a.notify_one();
>+ b.wait(0);
>+ s.release();
>+ a++;
>+ a.notify_one();
>+
>+ b.wait(1);
>+}
>+
>+int main()
>+{
>+ test01();
>+ test02();
>+}
>--
>2.26.2
>
^ permalink raw reply [flat|nested] 50+ messages in thread
* Re: [PATCH] Add C++2a synchronization support
2020-08-03 14:09 ` Jonathan Wakely
@ 2020-08-03 20:19 ` Jonathan Wakely
2020-09-03 0:47 ` Thomas Rodgers
0 siblings, 1 reply; 50+ messages in thread
From: Jonathan Wakely @ 2020-08-03 20:19 UTC (permalink / raw)
To: Thomas Rodgers; +Cc: gcc-patches, libstdc++, trodgers
On 03/08/20 15:09 +0100, Jonathan Wakely wrote:
>On 05/06/20 17:29 -0700, Thomas Rodgers wrote:
>>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
>>@@ -0,0 +1,272 @@
>>+// -*- 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_base.h
>>+ * This is an internal header file, included by other library headers.
>>+ * Do not attempt to use it directly. @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>
>>+
>>+namespace std _GLIBCXX_VISIBILITY(default)
>>+{
>>+_GLIBCXX_BEGIN_NAMESPACE_VERSION
>>+
>>+#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
>>+ template<ptrdiff_t __least_max_value>
>>+ struct __platform_semaphore
Why is this a template?
It means we'll instantiate identical code for counting_semaphore<3>
and counting_semaphore<4>, but they're distinct types that will bloat
the binary.
Can't we just have a single __platform_semaphore type for all max
values, and just make sure we don't instantiate it for values larger
than SEM_VALUE_MAX?
>>+ {
>
>I think we want to delete the copy constructor and copy assignment
>operator for this type and __atomic_semaphore.
>
>>+ using __clock_t = chrono::system_clock;
>>+
>>+ explicit __platform_semaphore(ptrdiff_t __count) noexcept
>>+ {
>>+ static_assert( __least_max_value <= SEM_VALUE_MAX, "");
I think it would be useful for __platform_semaphore to define a static
constexpr member like:
static constexpr ptrdiff_t _S_max = SEM_VALUE_MAX;
And then __atomic_semaphore could define:
static constexpr _Tp _S_max = __gnu_cxx::__int_traits<_Tp>::__max;
And then the alias __semaphore_base could be defined as:
#if _GLIBCXX_HAVE_LINUX_FUTEX && ! _GLIBCXX_REQUIRE_POSIX_SEMAPHORE
// Use futex if available and user didn't force use of POSIX:
using __fast_semaphore_base = __atomic_semaphore<__platform_wait_t>;
#elif _GLIBCXX_HAVE_POSIX_SEMAPHORE
// Otherwise, use POSIX if available:
using __fast_semaphore_base = __platform_semaphore<SEM_VALUE_MAX>;
#else
// Otherwise, use atomics:
using __fast_semaphore_base = __atomic_semaphore<ptrdiff_t>;
#endif
template<ptrdiff_t __least_max_value>
using __semaphore_base = conditional_t<
__least_max_value <= __fast_semaphore_base::_S_max,
__fast_semaphore_base, __atomic_semaphore<ptrdiff_t>;
Would that make sense?
Do the comments above reflect the intent?
>>+#ifdef _GLIBCXX_REQUIRE_POSIX_SEMAPHORE
>
>What is this case for? This macro seems to only exist for use by a
>single testcase.
>
>Is it supposed to be something users can set? If so, it needs to be
>documented as ABI-breaking and as implying that counting_semaphore
>cannot be use with counts higher than SEM_VALUE_MAX.
>
>>+ template<ptrdiff_t __least_max_value>
>>+ using __semaphore_base = __platform_semaphore<__least_max_value>;
>>+#else
>>+# ifdef _GLIBCXX_HAVE_LINUX_FUTEX
>>+ template<ptrdiff_t __least_max_value>
>>+ using __semaphore_base = conditional_t<(
>>+ __least_max_value >= 0
>
>This condition is redundant, we already know that counting_semaphore
>has checked the value is non-negative.
>
>>+ && __least_max_value <= __detail::__platform_wait_max_value),
>>+ __atomic_semaphore<__detail::__platform_wait_t>,
>>+ __atomic_semaphore<ptrdiff_t>>;
>>+
>>+// __platform_semaphore
>>+# elif defined _GLIBCXX_HAVE_POSIX_SEMAPHORE
>>+ template<ptrdiff_t __least_max_value>
>>+ using __semaphore_base = conditional_t<(
>>+ __least_max_value >= 0
>
>Redundant condition again.
>
>>+ && __least_max_value <= SEM_VALUE_MAX),
>>+ __platform_semaphore<__least_max_value>,
>>+ __atomic_semaphore<ptrdiff_t>>;
>>+# else
>>+ template<ptrdiff_t __least_max_value>
>>+ using __semaphore_base = __atomic_semaphore<ptrdiff_t>;
>>+# endif
>>+#endif
>>+
>>+_GLIBCXX_END_NAMESPACE_VERSION
>>+} // namespace std
>>+
>>+#endif
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH] Add C++2a synchronization support
2020-08-03 20:19 ` Jonathan Wakely
@ 2020-09-03 0:47 ` Thomas Rodgers
2020-09-03 0:54 ` Thomas Rodgers
0 siblings, 1 reply; 50+ messages in thread
From: Thomas Rodgers @ 2020-09-03 0:47 UTC (permalink / raw)
To: gcc-patches, libstdc++; +Cc: trodgers, Thomas Rodgers
Adds 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.cc: Liekwise.
* 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_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.
---
libstdc++-v3/include/Makefile.am | 5 +
libstdc++-v3/include/Makefile.in | 5 +
libstdc++-v3/include/bits/atomic_base.h | 172 +++++++++-
libstdc++-v3/include/bits/atomic_timed_wait.h | 281 ++++++++++++++++
libstdc++-v3/include/bits/atomic_wait.h | 301 ++++++++++++++++++
libstdc++-v3/include/bits/semaphore_base.h | 283 ++++++++++++++++
libstdc++-v3/include/std/atomic | 73 +++++
libstdc++-v3/include/std/latch | 90 ++++++
libstdc++-v3/include/std/semaphore | 92 ++++++
libstdc++-v3/include/std/version | 2 +
.../atomic/wait_notify/atomic_refs.cc | 103 ++++++
.../29_atomics/atomic/wait_notify/bool.cc | 59 ++++
.../29_atomics/atomic/wait_notify/floats.cc | 32 ++
.../29_atomics/atomic/wait_notify/generic.cc | 31 ++
.../29_atomics/atomic/wait_notify/generic.h | 160 ++++++++++
.../atomic/wait_notify/integrals.cc | 65 ++++
.../29_atomics/atomic/wait_notify/pointers.cc | 59 ++++
libstdc++-v3/testsuite/30_threads/latch/1.cc | 27 ++
libstdc++-v3/testsuite/30_threads/latch/2.cc | 27 ++
libstdc++-v3/testsuite/30_threads/latch/3.cc | 50 +++
.../testsuite/30_threads/semaphore/1.cc | 27 ++
.../testsuite/30_threads/semaphore/2.cc | 27 ++
.../semaphore/least_max_value_neg.cc | 30 ++
.../30_threads/semaphore/try_acquire.cc | 55 ++++
.../30_threads/semaphore/try_acquire_for.cc | 85 +++++
.../30_threads/semaphore/try_acquire_posix.cc | 153 +++++++++
.../30_threads/semaphore/try_acquire_until.cc | 94 ++++++
27 files changed, 2387 insertions(+), 1 deletion(-)
create mode 100644 libstdc++-v3/include/bits/atomic_timed_wait.h
create mode 100644 libstdc++-v3/include/bits/atomic_wait.h
create mode 100644 libstdc++-v3/include/bits/semaphore_base.h
create mode 100644 libstdc++-v3/include/std/latch
create mode 100644 libstdc++-v3/include/std/semaphore
create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc
create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc
create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc
create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc
create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h
create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc
create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/latch/1.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/latch/2.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/latch/3.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/1.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/2.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc
diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
index 1dff3862e35..ef8acd4a389 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 \
${std_srcdir}/limits \
${std_srcdir}/list \
${std_srcdir}/locale \
@@ -69,6 +70,7 @@ std_headers = \
${std_srcdir}/ratio \
${std_srcdir}/regex \
${std_srcdir}/scoped_allocator \
+ ${std_srcdir}/semaphore \
${std_srcdir}/set \
${std_srcdir}/shared_mutex \
${std_srcdir}/span \
@@ -100,6 +102,8 @@ bits_headers = \
${bits_srcdir}/allocated_ptr.h \
${bits_srcdir}/allocator.h \
${bits_srcdir}/atomic_base.h \
+ ${bits_srcdir}/atomic_wait.h \
+ ${bits_srcdir}/atomic_timed_wait.h \
${bits_srcdir}/atomic_futex.h \
${bits_srcdir}/basic_ios.h \
${bits_srcdir}/basic_ios.tcc \
@@ -174,6 +178,7 @@ bits_headers = \
${bits_srcdir}/regex_compiler.tcc \
${bits_srcdir}/regex_executor.h \
${bits_srcdir}/regex_executor.tcc \
+ ${bits_srcdir}/semaphore_base.h \
${bits_srcdir}/shared_ptr.h \
${bits_srcdir}/shared_ptr_atomic.h \
${bits_srcdir}/shared_ptr_base.h \
diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in
index 16371015071..0dfb2df9ab3 100644
--- a/libstdc++-v3/include/Makefile.in
+++ b/libstdc++-v3/include/Makefile.in
@@ -398,6 +398,7 @@ std_headers = \
${std_srcdir}/iostream \
${std_srcdir}/istream \
${std_srcdir}/iterator \
+ ${std_srcdir}/latch\
${std_srcdir}/limits \
${std_srcdir}/list \
${std_srcdir}/locale \
@@ -415,6 +416,7 @@ std_headers = \
${std_srcdir}/ratio \
${std_srcdir}/regex \
${std_srcdir}/scoped_allocator \
+ ${std_srcdir}/semaphore \
${std_srcdir}/set \
${std_srcdir}/shared_mutex \
${std_srcdir}/span \
@@ -446,6 +448,8 @@ bits_headers = \
${bits_srcdir}/allocated_ptr.h \
${bits_srcdir}/allocator.h \
${bits_srcdir}/atomic_base.h \
+ ${bits_srcdir}/atomic_wait.h \
+ ${bits_srcdir}/atomic_timed_wait.h \
${bits_srcdir}/atomic_futex.h \
${bits_srcdir}/basic_ios.h \
${bits_srcdir}/basic_ios.tcc \
@@ -520,6 +524,7 @@ bits_headers = \
${bits_srcdir}/regex_compiler.tcc \
${bits_srcdir}/regex_executor.h \
${bits_srcdir}/regex_executor.tcc \
+ ${bits_srcdir}/semaphore_base.h \
${bits_srcdir}/shared_ptr.h \
${bits_srcdir}/shared_ptr_atomic.h \
${bits_srcdir}/shared_ptr_base.h \
diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h
index 015acef83c4..c121d993fee 100644
--- a/libstdc++-v3/include/bits/atomic_base.h
+++ b/libstdc++-v3/include/bits/atomic_base.h
@@ -37,6 +37,10 @@
#include <bits/atomic_lockfree_defines.h>
#include <bits/move.h>
+#if __cplusplus > 201703L
+#include <bits/atomic_wait.h>
+#endif
+
#ifndef _GLIBCXX_ALWAYS_INLINE
#define _GLIBCXX_ALWAYS_INLINE inline __attribute__((__always_inline__))
#endif
@@ -134,7 +138,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
return __ret;
}
-
// Base types for atomics.
template<typename _IntTp>
struct __atomic_base;
@@ -562,6 +565,31 @@ _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
+ {
+ std::__atomic_wait(&_M_i, __old,
+ [__m, this, __old]
+ { return this->load(__m) != __old; });
+ }
+
+ // TODO add const volatile overload
+
+ _GLIBCXX_ALWAYS_INLINE void
+ notify_one() const noexcept
+ { std::__atomic_notify(&_M_i, false); }
+
+ // TODO add const volatile overload
+
+ _GLIBCXX_ALWAYS_INLINE void
+ notify_all() const noexcept
+ { std::__atomic_notify(&_M_i, true); }
+
+ // TODO add const volatile overload
+#endif // C++2a
+
_GLIBCXX_ALWAYS_INLINE __int_type
fetch_add(__int_type __i,
memory_order __m = memory_order_seq_cst) noexcept
@@ -823,6 +851,31 @@ _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
+ {
+ std::__atomic_wait(&_M_p, __old,
+ [__m, this, __old]()
+ { return this->load(__m) != __old; });
+ }
+
+ // TODO add const volatile overload
+
+ _GLIBCXX_ALWAYS_INLINE void
+ notify_one() const noexcept
+ { std::__atomic_notify(&_M_p, false); }
+
+ // TODO add const volatile overload
+
+ _GLIBCXX_ALWAYS_INLINE void
+ notify_all() const noexcept
+ { std::__atomic_notify(&_M_p, true); }
+
+ // TODO add const volatile overload
+#endif // C++2a
+
_GLIBCXX_ALWAYS_INLINE __pointer_type
fetch_add(ptrdiff_t __d,
memory_order __m = memory_order_seq_cst) noexcept
@@ -911,6 +964,33 @@ _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
+ {
+ std::__atomic_wait(__ptr, __old,
+ [=]() { return load(__ptr, __m) == __old; });
+ }
+
+ // TODO add const volatile overload
+
+ template<typename _Tp>
+ _GLIBCXX_ALWAYS_INLINE void
+ notify_one(const _Tp* __ptr) noexcept
+ { std::__atomic_notify(__ptr, false); }
+
+ // TODO add const volatile overload
+
+ template<typename _Tp>
+ _GLIBCXX_ALWAYS_INLINE void
+ notify_all(const _Tp* __ptr) noexcept
+ { std::__atomic_notify(__ptr, true); }
+
+ // TODO add const volatile overload
+#endif // C++2a
+
template<typename _Tp>
_GLIBCXX_ALWAYS_INLINE _Tp
fetch_add(_Tp* __ptr, _Diff<_Tp> __i, memory_order __m) noexcept
@@ -1164,6 +1244,24 @@ _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
+
value_type
fetch_add(value_type __i,
memory_order __m = memory_order_seq_cst) noexcept
@@ -1301,6 +1399,24 @@ _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); }
+
+ // TODO add const volatile overload
+
private:
_Tp* _M_ptr;
};
@@ -1396,6 +1512,24 @@ _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); }
+
+ // TODO add const volatile overload
+
value_type
fetch_add(value_type __i,
memory_order __m = memory_order_seq_cst) const noexcept
@@ -1551,6 +1685,24 @@ _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_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); }
+
+ // TODO add const volatile overload
+
value_type
fetch_add(value_type __i,
memory_order __m = memory_order_seq_cst) const noexcept
@@ -1660,6 +1812,24 @@ _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); }
+
+ // TODO add const volatile overload
+
_GLIBCXX_ALWAYS_INLINE value_type
fetch_add(difference_type __d,
memory_order __m = memory_order_seq_cst) const noexcept
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..2f57356b366
--- /dev/null
+++ b/libstdc++-v3/include/bits/atomic_timed_wait.h
@@ -0,0 +1,281 @@
+// -*- 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 };
+
+ namespace __detail
+ {
+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
+ 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,
+ static_cast<int>(__futex_wait_flags::__wait_bitset_private),
+ __val, &__rt, nullptr,
+ static_cast<int>(__futex_wait_flags::__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 (is_same_v<__platform_wait_clock_t, _Clock>)
+ {
+ return std::__detail::__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 (std::__detail::__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 (std::__detail::__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(__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 (std::__detail::__cond_wait_until(&_M_cv, __l, __atime) ==
+ __atomic_wait_status::timeout)
+ 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 static_cast<__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 (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..21ec3d38c94
--- /dev/null
+++ b/libstdc++-v3/include/bits/atomic_wait.h
@@ -0,0 +1,301 @@
+// -*- 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
+
+
+// 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;
+
+ constexpr auto __atomic_spin_count_1 = 16;
+ constexpr auto __atomic_spin_count_2 = 12;
+
+ inline constexpr
+ auto __platform_wait_max_value =
+ __gnu_cxx::__int_traits<__platform_wait_t>::__max;
+
+ 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 class __futex_wait_flags : int
+ {
+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE
+ __private_flag = 128,
+#else
+ __private_flag = 0,
+#endif
+ __wait = 0,
+ __wake = 1,
+ __wait_bitset = 9,
+ __wake_bitset = 10,
+ __wait_private = __wait | __private_flag,
+ __wake_private = __wake | __private_flag,
+ __wait_bitset_private = __wait_bitset | __private_flag,
+ __wake_bitset_private = __wake_bitset | __private_flag,
+ __bitset_match_any = -1
+ };
+
+ template<typename _Tp>
+ void
+ __platform_wait(const _Tp* __addr, __platform_wait_t __val) noexcept
+ {
+ auto __e = syscall (SYS_futex, static_cast<const void*>(__addr),
+ static_cast<int>(__futex_wait_flags::__wait_private),
+ __val, nullptr);
+ if (__e && !(errno == EINTR || errno == EAGAIN))
+ std::terminate();
+ }
+
+ template<typename _Tp>
+ void
+ __platform_notify(const _Tp* __addr, bool __all) noexcept
+ {
+ syscall (SYS_futex, static_cast<const void*>(__addr),
+ static_cast<int>(__futex_wait_flags::__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;
+ __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(const 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(static_cast<const 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<typename _Pred>
+ bool
+ __atomic_spin(_Pred __pred) noexcept
+ {
+ for (auto __i = 0; __i < __detail::__atomic_spin_count_1; ++__i)
+ {
+ if (__pred())
+ return true;
+
+ if (__i < __detail::__atomic_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(__addr, __old);
+ }
+ 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..ed127a7a953
--- /dev/null
+++ b/libstdc++-v3/include/bits/semaphore_base.h
@@ -0,0 +1,283 @@
+// -*- 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_base.h
+ * This is an internal header file, included by other library headers.
+ * Do not attempt to use it directly. @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 __has_include(<semaphore.h>)
+#define _GLIBCXX_HAVE_POSIX_SEMAPHORE 1
+#include <semaphore.h>
+#endif
+
+#include <chrono>
+#include <type_traits>
+
+#include <iostream>
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
+ struct __platform_semaphore
+ {
+ using __clock_t = chrono::system_clock;
+ static constexpr ptrdiff_t _S_max = SEM_VALUE_MAX;
+
+ explicit __platform_semaphore(ptrdiff_t __count) noexcept
+ {
+ sem_init(&_M_semaphore, 0, __count);
+ }
+
+ __platform_semaphore(const __platform_semaphore&) = delete;
+ __platform_semaphore& operator=(const __platform_semaphore&) = delete;
+
+ ~__platform_semaphore()
+ { sem_destroy(&_M_semaphore); }
+
+ _GLIBCXX_ALWAYS_INLINE void
+ _M_acquire() noexcept
+ {
+ for (;;)
+ {
+ auto __err = sem_wait(&_M_semaphore);
+ if (__err && (errno == EINTR))
+ continue;
+ else if (__err)
+ std::terminate();
+ else
+ break;
+ }
+ }
+
+ _GLIBCXX_ALWAYS_INLINE void
+ _M_release(std::ptrdiff_t __update) noexcept
+ {
+ for(; __update != 0; --__update)
+ {
+ auto __err = sem_post(&_M_semaphore);
+ if (__err)
+ std::terminate();
+ }
+ }
+
+ bool
+ _M_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())
+ };
+
+ for (;;)
+ {
+ auto __err = sem_timedwait(&_M_semaphore, &__ts);
+ if (__err && (errno == EINTR))
+ continue;
+ else if (__err && (errno == ETIMEDOUT))
+ return false;
+ else if (__err && (errno == EINVAL))
+ return false; // caller supplied an invalid __atime
+ else if (__err)
+ std::terminate();
+ else
+ break;
+ }
+ return true;
+ }
+
+ template<typename _Clock, typename _Duration>
+ bool
+ _M_try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept
+ {
+ if constexpr (std::is_same<__clock_t, _Clock>::value)
+ {
+ return _M_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 (_M_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
+ _M_try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) noexcept
+ { return _M_try_acquire_until(__clock_t::now() + __rtime); }
+
+ private:
+ sem_t _M_semaphore;
+ };
+#endif // _GLIBCXX_HAVE_POSIX_SEMAPHORE
+
+ template<typename _Tp>
+ struct __atomic_semaphore
+ {
+ static_assert(std::is_integral_v<_Tp>);
+ static constexpr ptrdiff_t _S_max = __gnu_cxx::__int_traits<_Tp>::__max;
+
+ explicit __atomic_semaphore(_Tp __count) noexcept
+ : _M_a(__count)
+ { }
+
+ __atomic_semaphore(const __atomic_semaphore&) = delete;
+ __atomic_semaphore& operator=(const __atomic_semaphore&) = delete;
+
+ _GLIBCXX_ALWAYS_INLINE void
+ _M_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
+ _M_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
+ _M_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
+ _M_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
+ _M_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(__alignof__(_Tp)) _Tp _M_a;
+ };
+
+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX && !_GLIBCXX_REQUIRE_POSIX_SEMAPHORE
+ // Use futex if available and didn't force use of POSIX
+ using __fast_semaphore = __atomic_semaphore<__detail::__platform_wait_t>;
+#elif _GLIBCXX_HAVE_POSIX_SEMAPHORE
+ using __fast_semaphore = __platform_semaphore;
+#else
+ using __fast_semaphore = __atomic_semaphore<ptrdiff_t>;
+#endif
+
+template<ptrdiff_t __least_max_value>
+ using __semaphore_impl = conditional_t<
+ (__least_max_value > 1),
+ conditional_t<
+ (__least_max_value <= __fast_semaphore::_S_max),
+ __fast_semaphore,
+ __atomic_semaphore<ptrdiff_t>>,
+ __fast_semaphore>;
+
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace std
+
+#endif
diff --git a/libstdc++-v3/include/std/atomic b/libstdc++-v3/include/std/atomic
index 1a304261fe7..c15909d9ccb 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
@@ -363,6 +376,27 @@ _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) const noexcept
+ {
+ std::__atomic_wait(&_M_i, __old,
+ [__m, this, __old]
+ {
+ const auto __v = this->load(__m);
+ // TODO make this ignore padding bits when we can do that
+ return __builtin_memcmp(&__old, &__v, sizeof(_Tp)) != 0;
+ });
+ }
+
+ // TODO add const volatile overload
+
+ void notify_one() const noexcept
+ { std::__atomic_notify(&_M_i, false); }
+
+ void notify_all() const noexcept
+ { std::__atomic_notify(&_M_i, true); }
+#endif
+
};
#undef _GLIBCXX20_INIT
@@ -601,6 +635,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
@@ -1353,6 +1399,33 @@ _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..bd06db5aa7f
--- /dev/null
+++ b/libstdc++-v3/include/std/latch
@@ -0,0 +1,90 @@
+// <latch> -*- C++ -*-
+
+// 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.
+ */
+
+#ifndef _GLIBCXX_LATCH
+#define _GLIBCXX_LATCH
+
+#pragma GCC system_header
+
+#if __cplusplus > 201703L
+#define __cpp_lib_latch 201907L
+
+#include <bits/atomic_base.h>
+#include <ext/numeric_traits.h>
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+ class latch
+ {
+ public:
+ static constexpr ptrdiff_t
+ max() noexcept
+ { return __gnu_cxx::__numeric_traits<ptrdiff_t>::__max; }
+
+ constexpr explicit latch(ptrdiff_t __expected) noexcept
+ : _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(__alignof__(ptrdiff_t)) ptrdiff_t _M_a;
+ };
+_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..865d6c4aecb
--- /dev/null
+++ b/libstdc++-v3/include/std/semaphore
@@ -0,0 +1,92 @@
+// <semaphore> -*- C++ -*-
+
+// 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.
+ */
+
+#ifndef _GLIBCXX_SEMAPHORE
+#define _GLIBCXX_SEMAPHORE
+
+#pragma GCC system_header
+
+#if __cplusplus > 201703L
+#define __cpp_lib_semaphore 201907L
+#include <bits/semaphore_base.h>
+#include <ext/numeric_traits.h>
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+ template<ptrdiff_t __least_max_value =
+ __gnu_cxx::__numeric_traits<ptrdiff_t>::__max>
+ class counting_semaphore
+ {
+ static_assert(__least_max_value >= 0);
+
+ __semaphore_impl<__least_max_value> _M_sem;
+
+ 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) noexcept(noexcept(_M_sem._M_release(1)))
+ { _M_sem._M_release(__update); }
+
+ void
+ acquire() noexcept(noexcept(_M_sem._M_acquire()))
+ { _M_sem._M_acquire(); }
+
+ bool
+ try_acquire() noexcept(noexcept(_M_sem._M_try_acquire()))
+ { return _M_sem._M_try_acquire(); }
+
+ template<class _Rep, class _Period>
+ bool
+ try_acquire_for(const std::chrono::duration<_Rep, _Period>& __rtime)
+ { return _M_sem._M_try_acquire_for(__rtime); }
+
+ template<class _Clock, class _Dur>
+ bool
+ try_acquire_until(const std::chrono::time_point<_Clock, _Dur>& __atime)
+ { return _M_sem._M_try_acquire_until(__atime); }
+ };
+
+ 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 f64aff4f520..72d7769ebcf 100644
--- a/libstdc++-v3/include/std/version
+++ b/libstdc++-v3/include/std/version
@@ -214,12 +214,14 @@
#ifdef _GLIBCXX_HAS_GTHREADS
# define __cpp_lib_jthread 201911L
#endif
+#define __cpp_lib_latch 201907L
#define __cpp_lib_list_remove_return_type 201806L
#define __cpp_lib_math_constants 201907L
#define __cpp_lib_polymorphic_allocator 201902L
#if __cpp_lib_concepts
# define __cpp_lib_ranges 201911L
#endif
+#define __cpp_lib_semaphore 201907L
#define __cpp_lib_shift 201806L
#define __cpp_lib_span 202002L
#define __cpp_lib_ssize 201902L
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" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <atomic>
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+#include <chrono>
+#include <type_traits>
+
+#include <testsuite_hooks.h>
+
+template<typename Tp>
+Tp check_wait_notify(Tp val1, Tp val2)
+{
+ using namespace std::literals::chrono_literals;
+
+ std::mutex m;
+ std::condition_variable cv;
+
+ Tp aa = val1;
+ std::atomic_ref<Tp> a(aa);
+ std::thread t([&]
+ {
+ cv.notify_one();
+ a.wait(val1);
+ if (a.load() != val2)
+ a = val1;
+ });
+ std::unique_lock<std::mutex> l(m);
+ cv.wait(l);
+ std::this_thread::sleep_for(100ms);
+ a.store(val2);
+ a.notify_one();
+ t.join();
+ return a.load();
+}
+
+template<typename Tp,
+ bool = std::is_integral_v<Tp>
+ || std::is_floating_point_v<Tp>>
+struct check;
+
+template<typename Tp>
+struct check<Tp, true>
+{
+ check()
+ {
+ Tp a = 0;
+ Tp b = 42;
+ VERIFY(check_wait_notify(a, b) == b);
+ }
+};
+
+template<typename Tp>
+struct check<Tp, false>
+{
+ check(Tp b)
+ {
+ Tp a;
+ VERIFY(check_wait_notify(a, b) == b);
+ }
+};
+
+struct foo
+{
+ long a = 0;
+ long b = 0;
+
+ foo& operator=(foo const&) = default;
+
+ friend bool
+ operator==(foo const& rhs, foo const& lhs)
+ { return rhs.a == lhs.a && rhs.b == lhs.b; }
+};
+
+int
+main ()
+{
+ check<long>();
+ check<double>();
+ check<foo>({42, 48});
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc
new file mode 100644
index 00000000000..b9fc063c66f
--- /dev/null
+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc
@@ -0,0 +1,59 @@
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <atomic>
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+#include <type_traits>
+#include <chrono>
+
+#include <testsuite_hooks.h>
+
+int
+main ()
+{
+ using namespace std::literals::chrono_literals;
+
+ std::mutex m;
+ std::condition_variable cv;
+
+ std::atomic<bool> a(false);
+ std::atomic<bool> b(false);
+ std::thread t([&]
+ {
+ cv.notify_one();
+ a.wait(false);
+ if (a.load())
+ {
+ b.store(true);
+ }
+ });
+ std::unique_lock<std::mutex> l(m);
+ cv.wait(l);
+ std::this_thread::sleep_for(100ms);
+ a.store(true);
+ a.notify_one();
+ t.join();
+ VERIFY( b.load() );
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc
new file mode 100644
index 00000000000..1d032085752
--- /dev/null
+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc
@@ -0,0 +1,32 @@
+// { dg-options "-std=gnu++2a -pthread -latomic -L../../libatomic/.libs" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include "generic.h"
+
+int
+main ()
+{
+ check<float> f;
+ check<double> d;
+ check<long double> l;
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc
new file mode 100644
index 00000000000..d15b9c86ae6
--- /dev/null
+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc
@@ -0,0 +1,31 @@
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include "generic.h"
+
+int
+main ()
+{
+ struct S{ int i; };
+ check<S> check_s{S{0},S{42}};
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h
new file mode 100644
index 00000000000..a319e8b60a6
--- /dev/null
+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h
@@ -0,0 +1,160 @@
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <atomic>
+#include <chrono>
+#include <condition_variable>
+#include <concepts>
+#include <mutex>
+#include <thread>
+
+#include <testsuite_hooks.h>
+
+#include <iostream>
+
+template<typename Tp>
+Tp check_wait_notify(Tp val1, Tp val2)
+ requires std::equality_comparable<Tp>
+{
+ using namespace std::literals::chrono_literals;
+
+ std::mutex m;
+ std::condition_variable cv;
+
+ std::atomic<Tp> a(val1);
+ std::thread t([&]
+ {
+ cv.notify_one();
+ a.wait(val1);
+ if (a.load() != val2)
+ a = val1;
+ });
+ std::unique_lock<std::mutex> l(m);
+ cv.wait(l);
+ std::this_thread::sleep_for(100ms);
+ a.store(val2);
+ a.notify_one();
+ t.join();
+ return a.load();
+}
+
+template<typename Tp>
+Tp check_wait_notify(Tp val1, Tp val2)
+{
+ using namespace std::literals::chrono_literals;
+
+ std::mutex m;
+ std::condition_variable cv;
+
+ std::atomic<Tp> a(val1);
+ std::thread t([&]
+ {
+ cv.notify_one();
+ a.wait(val1);
+ auto v = a.load();
+ // TODO this needs to zero padding bits when we can do that
+ if (__builtin_memcmp(&v, &val2, sizeof(Tp)) != 0)
+ a = val1;
+ });
+ std::unique_lock<std::mutex> l(m);
+ cv.wait(l);
+ std::this_thread::sleep_for(100ms);
+ a.store(val2);
+ a.notify_one();
+ t.join();
+ return a.load();
+}
+
+template<typename Tp>
+Tp check_atomic_wait_notify(Tp val1, Tp val2)
+ requires std::equality_comparable<Tp>
+{
+ using namespace std::literals::chrono_literals;
+
+ std::mutex m;
+ std::condition_variable cv;
+
+ std::atomic<Tp> a(val1);
+ std::thread t([&]
+ {
+ cv.notify_one();
+ std::atomic_wait(&a, val1);
+ if (a.load() != val2)
+ a = val1;
+ });
+ std::unique_lock<std::mutex> l(m);
+ cv.wait(l);
+ std::this_thread::sleep_for(100ms);
+ a.store(val2);
+ std::atomic_notify_one(&a);
+ t.join();
+ return a.load();
+}
+
+template<typename Tp>
+Tp check_atomic_wait_notify(Tp val1, Tp val2)
+{
+ using namespace std::literals::chrono_literals;
+
+ std::mutex m;
+ std::condition_variable cv;
+
+ std::atomic<Tp> a(val1);
+ std::thread t([&]
+ {
+ cv.notify_one();
+ std::atomic_wait(&a, val1);
+ auto v = a.load();
+ // TODO this needs to zero padding bits when we can do that
+ if (__builtin_memcmp(&v, &val2, sizeof(Tp)) != 0)
+ a = val1;
+ });
+ std::unique_lock<std::mutex> l(m);
+ cv.wait(l);
+ std::this_thread::sleep_for(100ms);
+ a.store(val2);
+ std::atomic_notify_one(&a);
+ t.join();
+ return a.load();
+}
+
+template<typename Tp>
+struct check
+{
+ check(Tp a = 0, Tp b = 42)
+ {
+ if constexpr (std::equality_comparable<Tp>)
+ {
+ VERIFY( check_wait_notify(a, b) == b);
+ VERIFY( check_atomic_wait_notify(a, b) == b);
+ }
+ else
+ {
+ {
+ // TODO this needs to zero padding bits when we can do that
+ auto v = check_wait_notify(a, b);
+ VERIFY( __builtin_memcmp(&v, &b, sizeof(Tp)) == 0 );
+ }
+
+ {
+ // TODO this needs to zero padding bits when we can do that
+ auto v = check_atomic_wait_notify(a, b);
+ VERIFY( __builtin_memcmp(&v, &b, sizeof(Tp)) == 0);
+ }
+ }
+ }
+};
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc
new file mode 100644
index 00000000000..115cb79a040
--- /dev/null
+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc
@@ -0,0 +1,65 @@
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include "generic.h"
+
+void
+test01()
+{
+ struct S{ int i; };
+ std::atomic<S> s;
+
+ s.wait(S{42});
+}
+
+int
+main ()
+{
+ // check<bool> bb;
+ check<char> ch;
+ check<signed char> sch;
+ check<unsigned char> uch;
+ check<short> s;
+ check<unsigned short> us;
+ check<int> i;
+ check<unsigned int> ui;
+ check<long> l;
+ check<unsigned long> ul;
+ check<long long> ll;
+ check<unsigned long long> ull;
+
+ check<wchar_t> wch;
+ check<char8_t> ch8;
+ check<char16_t> ch16;
+ check<char32_t> ch32;
+
+ check<int8_t> i8;
+ check<int16_t> i16;
+ check<int32_t> i32;
+ check<int64_t> i64;
+
+ check<uint8_t> u8;
+ check<uint16_t> u16;
+ check<uint32_t> u32;
+ check<uint64_t> u64;
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc
new file mode 100644
index 00000000000..8531bb2e788
--- /dev/null
+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc
@@ -0,0 +1,59 @@
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <atomic>
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+#include <type_traits>
+#include <chrono>
+
+#include <testsuite_hooks.h>
+
+int
+main ()
+{
+ using namespace std::literals::chrono_literals;
+
+ std::mutex m;
+ std::condition_variable cv;
+
+ long aa;
+ long bb;
+
+ std::atomic<long*> a(nullptr);
+ std::thread t([&]
+ {
+ cv.notify_one();
+ a.wait(nullptr);
+ if (a.load() == &aa)
+ a.store(&bb);
+ });
+ std::unique_lock<std::mutex> l(m);
+ cv.wait(l);
+ std::this_thread::sleep_for(100ms);
+ a.store(&aa);
+ a.notify_one();
+ t.join();
+ VERIFY( a.load() == &bb);
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/30_threads/latch/1.cc b/libstdc++-v3/testsuite/30_threads/latch/1.cc
new file mode 100644
index 00000000000..aa203cdf525
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/latch/1.cc
@@ -0,0 +1,27 @@
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <latch>
+
+#ifndef __cpp_lib_latch
+# error "Feature-test macro for latch missing in <latch>"
+#elif __cpp_lib_latch!= 201907L
+# error "Feature-test macro for latch has wrong value in <latch>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/latch/2.cc b/libstdc++-v3/testsuite/30_threads/latch/2.cc
new file mode 100644
index 00000000000..318a859ee21
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/latch/2.cc
@@ -0,0 +1,27 @@
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <version>
+
+#ifndef __cpp_lib_latch
+# error "Feature-test macro for latch missing in <version>"
+#elif __cpp_lib_latch != 201907L
+# error "Feature-test macro for latch has wrong value in <version>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/latch/3.cc b/libstdc++-v3/testsuite/30_threads/latch/3.cc
new file mode 100644
index 00000000000..cf1a31f996b
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/latch/3.cc
@@ -0,0 +1,50 @@
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+//
+#include <latch>
+#include <atomic>
+#include <thread>
+#include <testsuite_hooks.h>
+
+int main()
+{
+ std::atomic<int> a(0);
+
+ std::latch l(3);
+
+ VERIFY( !l.try_wait() );
+
+ auto fn = [&]
+ {
+ ++a;
+ l.count_down();
+ };
+
+ std::thread t0(fn);
+ std::thread t1(fn);
+
+ l.arrive_and_wait();
+ t0.join();
+ t1.join();
+
+ VERIFY( l.try_wait() );
+}
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/1.cc b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc
new file mode 100644
index 00000000000..1bbca687fc3
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc
@@ -0,0 +1,27 @@
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <semaphore>
+
+#ifndef __cpp_lib_semaphore
+# error "Feature-test macro for semaphore missing in <semaphore>"
+#elif __cpp_lib_semaphore != 201907L
+# error "Feature-test macro for semaphore has wrong value in <semaphore>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/2.cc b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc
new file mode 100644
index 00000000000..98743f5e27c
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc
@@ -0,0 +1,27 @@
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <version>
+
+#ifndef __cpp_lib_semaphore
+# error "Feature-test macro for semaphore missing in <version>"
+#elif __cpp_lib_semaphore != 201907L
+# error "Feature-test macro for semaphore has wrong value in <version>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc b/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc
new file mode 100644
index 00000000000..d74cfad53e9
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc
@@ -0,0 +1,30 @@
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <semaphore>
+
+int main()
+{
+ std::counting_semaphore<-1> sem(2);
+ return 0;
+}
+// { dg-error "static assertion failed" "" { target *-*-* } 0 }
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc
new file mode 100644
index 00000000000..25280441d07
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc
@@ -0,0 +1,55 @@
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <semaphore>
+#include <limits>
+#include <cstddef>
+#include <testsuite_hooks.h>
+
+void test01()
+{
+ std::counting_semaphore<10> s(3);
+
+ s.acquire();
+ VERIFY( s.try_acquire() );
+ VERIFY( s.try_acquire() );
+ VERIFY( !s.try_acquire() );
+ s.release();
+ VERIFY( s.try_acquire() );
+}
+
+void test02()
+{
+ std::binary_semaphore s(1);
+
+ s.acquire();
+ VERIFY( !s.try_acquire() );
+ s.release();
+ VERIFY( s.try_acquire() );
+}
+
+
+int main()
+{
+ test01();
+ test02();
+}
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc
new file mode 100644
index 00000000000..3f450e74661
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc
@@ -0,0 +1,85 @@
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <semaphore>
+#include <chrono>
+#include <thread>
+#include <atomic>
+#include <testsuite_hooks.h>
+
+void test01()
+{
+ using namespace std::chrono_literals;
+ std::counting_semaphore<10> s(2);
+ s.acquire();
+
+ auto const dur = 250ms;
+ {
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( s.try_acquire_for(dur) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff < dur );
+ }
+
+ {
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( !s.try_acquire_for(dur) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff >= dur );
+ }
+}
+
+void test02()
+{
+ using namespace std::chrono_literals;
+ std::binary_semaphore s(1);
+ std::atomic<int> a(0), b(0);
+ std::thread t([&] {
+ a.wait(0);
+ auto const dur = 250ms;
+ VERIFY( !s.try_acquire_for(dur) );
+ b++;
+ b.notify_one();
+
+ a.wait(1);
+ VERIFY( s.try_acquire_for(dur) );
+ b++;
+ b.notify_one();
+ });
+ t.detach();
+
+ s.acquire();
+ a++;
+ a.notify_one();
+ b.wait(0);
+ s.release();
+ a++;
+ a.notify_one();
+
+ b.wait(1);
+}
+
+int main()
+{
+ test01();
+ test02();
+}
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc
new file mode 100644
index 00000000000..13bd7487d56
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc
@@ -0,0 +1,153 @@
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <semaphore>
+#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
+#include <chrono>
+#include <thread>
+#include <atomic>
+#include <testsuite_hooks.h>
+
+void test01()
+{
+ using namespace std::chrono_literals;
+ std::__platform_semaphore s(2);
+ s._M_acquire();
+
+ auto const dur = 250ms;
+ {
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( s._M_try_acquire_for(dur) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff < dur );
+ }
+
+ {
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( !s._M_try_acquire_for(dur) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff >= dur );
+ }
+}
+
+void test02()
+{
+ using namespace std::chrono_literals;
+ std::__platform_semaphore s(1);
+ std::atomic<int> a(0), b(0);
+ std::thread t([&] {
+ a.wait(0);
+ auto const dur = 250ms;
+ VERIFY( !s._M_try_acquire_for(dur) );
+ b++;
+ b.notify_one();
+
+ a.wait(1);
+ VERIFY( s._M_try_acquire_for(dur) );
+ b++;
+ b.notify_one();
+ });
+ t.detach();
+
+ s._M_acquire();
+ a++;
+ a.notify_one();
+ b.wait(0);
+ s._M_release(1);
+ a++;
+ a.notify_one();
+
+ b.wait(1);
+}
+
+void test03()
+{
+ using namespace std::chrono_literals;
+ std::__platform_semaphore s(2);
+ s._M_acquire();
+
+ auto const dur = 250ms;
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( s._M_try_acquire_until(at) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff < dur );
+ }
+
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( !s._M_try_acquire_until(at) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff >= dur );
+ }
+}
+
+void test04()
+{
+ using namespace std::chrono_literals;
+ std::__platform_semaphore s(1);
+ std::atomic<int> a(0), b(0);
+ std::thread t([&] {
+ a.wait(0);
+ auto const dur = 250ms;
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ VERIFY( !s._M_try_acquire_until(at) );
+
+ b++;
+ b.notify_one();
+ }
+
+ a.wait(1);
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ VERIFY( s._M_try_acquire_until(at) );
+ }
+ b++;
+ b.notify_one();
+ });
+ t.detach();
+
+ s._M_acquire();
+ a++;
+ a.notify_one();
+ b.wait(0);
+ s._M_release(1);
+ a++;
+ a.notify_one();
+
+ b.wait(1);
+}
+#endif
+
+int main()
+{
+#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
+ test01();
+ test02();
+ test03();
+ test04();
+#endif
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc
new file mode 100644
index 00000000000..af7ab7bac39
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc
@@ -0,0 +1,94 @@
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <semaphore>
+#include <chrono>
+#include <thread>
+#include <atomic>
+#include <testsuite_hooks.h>
+
+void test01()
+{
+ using namespace std::chrono_literals;
+ std::counting_semaphore<10> s(2);
+ s.acquire();
+
+ auto const dur = 250ms;
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( s.try_acquire_until(at) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff < dur );
+ }
+
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ auto const t0 = std::chrono::steady_clock::now();
+ VERIFY( !s.try_acquire_until(at) );
+ auto const diff = std::chrono::steady_clock::now() - t0;
+ VERIFY( diff >= dur );
+ }
+}
+
+void test02()
+{
+ using namespace std::chrono_literals;
+ std::binary_semaphore s(1);
+ std::atomic<int> a(0), b(0);
+ std::thread t([&] {
+ a.wait(0);
+ auto const dur = 250ms;
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ VERIFY( !s.try_acquire_until(at) );
+
+ b++;
+ b.notify_one();
+ }
+
+ a.wait(1);
+ {
+ auto const at = std::chrono::system_clock::now() + dur;
+ VERIFY( s.try_acquire_until(at) );
+ }
+ b++;
+ b.notify_one();
+ });
+ t.detach();
+
+ s.acquire();
+ a++;
+ a.notify_one();
+ b.wait(0);
+ s.release();
+ a++;
+ a.notify_one();
+
+ b.wait(1);
+}
+
+int main()
+{
+ test01();
+ test02();
+}
--
2.26.2
^ permalink raw reply [flat|nested] 50+ messages in thread
* [PATCH] Add C++2a synchronization support
2020-09-03 0:47 ` Thomas Rodgers
@ 2020-09-03 0:54 ` Thomas Rodgers
2020-09-11 23:58 ` [PATCH] libstdc++: " Thomas Rodgers
0 siblings, 1 reply; 50+ messages in thread
From: Thomas Rodgers @ 2020-09-03 0:54 UTC (permalink / raw)
To: gcc-patches, libstdc++; +Cc: trodgers, Thomas Rodgers
Note - ignore previous version of this patch, didn't filter out
Makefile.in
Adds 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.cc: Liekwise.
* 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_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.
---
libstdc++-v3/include/Makefile.am | 5 +
libstdc++-v3/include/Makefile.in | 5 +
libstdc++-v3/include/bits/atomic_base.h | 172 +++++++++-
libstdc++-v3/include/bits/atomic_timed_wait.h | 281 ++++++++++++++++
libstdc++-v3/include/bits/atomic_wait.h | 301 ++++++++++++++++++
libstdc++-v3/include/bits/semaphore_base.h | 283 ++++++++++++++++
libstdc++-v3/include/std/atomic | 73 +++++
libstdc++-v3/include/std/latch | 90 ++++++
libstdc++-v3/include/std/semaphore | 92 ++++++
libstdc++-v3/include/std/version | 2 +
.../atomic/wait_notify/atomic_refs.cc | 103 ++++++
.../29_atomics/atomic/wait_notify/bool.cc | 59 ++++
.../29_atomics/atomic/wait_notify/floats.cc | 32 ++
.../29_atomics/atomic/wait_notify/generic.cc | 31 ++
.../29_atomics/atomic/wait_notify/generic.h | 160 ++++++++++
.../atomic/wait_notify/integrals.cc | 65 ++++
.../29_atomics/atomic/wait_notify/pointers.cc | 59 ++++
libstdc++-v3/testsuite/30_threads/latch/1.cc | 27 ++
libstdc++-v3/testsuite/30_threads/latch/2.cc | 27 ++
libstdc++-v3/testsuite/30_threads/latch/3.cc | 50 +++
.../testsuite/30_threads/semaphore/1.cc | 27 ++
.../testsuite/30_threads/semaphore/2.cc | 27 ++
.../semaphore/least_max_value_neg.cc | 30 ++
.../30_threads/semaphore/try_acquire.cc | 55 ++++
.../30_threads/semaphore/try_acquire_for.cc | 85 +++++
.../30_threads/semaphore/try_acquire_posix.cc | 153 +++++++++
.../30_threads/semaphore/try_acquire_until.cc | 94 ++++++
27 files changed, 2387 insertions(+), 1 deletion(-)
create mode 100644 libstdc++-v3/include/bits/atomic_timed_wait.h
create mode 100644 libstdc++-v3/include/bits/atomic_wait.h
create mode 100644 libstdc++-v3/include/bits/semaphore_base.h
create mode 100644 libstdc++-v3/include/std/latch
create mode 100644 libstdc++-v3/include/std/semaphore
create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/atomic_refs.cc
create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc
create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc
create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc
create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h
create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc
create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/latch/1.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/latch/2.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/latch/3.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/1.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/2.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc
create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc
diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
index 1dff3862e35..ef8acd4a389 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 \
${std_srcdir}/limits \
${std_srcdir}/list \
${std_srcdir}/locale \
@@ -69,6 +70,7 @@ std_headers = \
${std_srcdir}/ratio \
${std_srcdir}/regex \
${std_srcdir}/scoped_allocator \
+ ${std_srcdir}/semaphore \
${std_srcdir}/set \
${std_srcdir}/shared_mutex \
${std_srcdir}/span \
@@ -100,6 +102,8 @@ bits_headers = \
${bits_srcdir}/allocated_ptr.h \
${bits_srcdir}/allocator.h \
${bits_srcdir}/atomic_base.h \
+ ${bits_srcdir}/atomic_wait.h \
+ ${bits_srcdir}/atomic_timed_wait.h \
${bits_srcdir}/atomic_futex.h \
${bits_srcdir}/basic_ios.h \
${bits_srcdir}/basic_ios.tcc \
@@ -174,6 +178,7 @@ bits_headers = \
${bits_srcdir}/regex_compiler.tcc \
${bits_srcdir}/regex_executor.h \
${bits_srcdir}/regex_executor.tcc \
+ ${bits_srcdir}/semaphore_base.h \
${bits_srcdir}/shared_ptr.h \
${bits_srcdir}/shared_ptr_atomic.h \
${bits_srcdir}/shared_ptr_base.h \
diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h
index 015acef83c4..c121d993fee 100644
--- a/libstdc++-v3/include/bits/atomic_base.h
+++ b/libstdc++-v3/include/bits/atomic_base.h
@@ -37,6 +37,10 @@
#include <bits/atomic_lockfree_defines.h>
#include <bits/move.h>
+#if __cplusplus > 201703L
+#include <bits/atomic_wait.h>
+#endif
+
#ifndef _GLIBCXX_ALWAYS_INLINE
#define _GLIBCXX_ALWAYS_INLINE inline __attribute__((__always_inline__))
#endif
@@ -134,7 +138,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
return __ret;
}
-
// Base types for atomics.
template<typename _IntTp>
struct __atomic_base;
@@ -562,6 +565,31 @@ _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
+ {
+ std::__atomic_wait(&_M_i, __old,
+ [__m, this, __old]
+ { return this->load(__m) != __old; });
+ }
+
+ // TODO add const volatile overload
+
+ _GLIBCXX_ALWAYS_INLINE void
+ notify_one() const noexcept
+ { std::__atomic_notify(&_M_i, false); }
+
+ // TODO add const volatile overload
+
+ _GLIBCXX_ALWAYS_INLINE void
+ notify_all() const noexcept
+ { std::__atomic_notify(&_M_i, true); }
+
+ // TODO add const volatile overload
+#endif // C++2a
+
_GLIBCXX_ALWAYS_INLINE __int_type
fetch_add(__int_type __i,
memory_order __m = memory_order_seq_cst) noexcept
@@ -823,6 +851,31 @@ _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
+ {
+ std::__atomic_wait(&_M_p, __old,
+ [__m, this, __old]()
+ { return this->load(__m) != __old; });
+ }
+
+ // TODO add const volatile overload
+
+ _GLIBCXX_ALWAYS_INLINE void
+ notify_one() const noexcept
+ { std::__atomic_notify(&_M_p, false); }
+
+ // TODO add const volatile overload
+
+ _GLIBCXX_ALWAYS_INLINE void
+ notify_all() const noexcept
+ { std::__atomic_notify(&_M_p, true); }
+
+ // TODO add const volatile overload
+#endif // C++2a
+
_GLIBCXX_ALWAYS_INLINE __pointer_type
fetch_add(ptrdiff_t __d,
memory_order __m = memory_order_seq_cst) noexcept
@@ -911,6 +964,33 @@ _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
+ {
+ std::__atomic_wait(__ptr, __old,
+ [=]() { return load(__ptr, __m) == __old; });
+ }
+
+ // TODO add const volatile overload
+
+ template<typename _Tp>
+ _GLIBCXX_ALWAYS_INLINE void
+ notify_one(const _Tp* __ptr) noexcept
+ { std::__atomic_notify(__ptr, false); }
+
+ // TODO add const volatile overload
+
+ template<typename _Tp>
+ _GLIBCXX_ALWAYS_INLINE void
+ notify_all(const _Tp* __ptr) noexcept
+ { std::__atomic_notify(__ptr, true); }
+
+ // TODO add const volatile overload
+#endif // C++2a
+
template<typename _Tp>
_GLIBCXX_ALWAYS_INLINE _Tp
fetch_add(_Tp* __ptr, _Diff<_Tp> __i, memory_order __m) noexcept
@@ -1164,6 +1244,24 @@ _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
+
value_type
fetch_add(value_type __i,
memory_order __m = memory_order_seq_cst) noexcept
@@ -1301,6 +1399,24 @@ _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); }
+
+ // TODO add const volatile overload
+
private:
_Tp* _M_ptr;
};
@@ -1396,6 +1512,24 @@ _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); }
+
+ // TODO add const volatile overload
+
value_type
fetch_add(value_type __i,
memory_order __m = memory_order_seq_cst) const noexcept
@@ -1551,6 +1685,24 @@ _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_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); }
+
+ // TODO add const volatile overload
+
value_type
fetch_add(value_type __i,
memory_order __m = memory_order_seq_cst) const noexcept
@@ -1660,6 +1812,24 @@ _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); }
+
+ // TODO add const volatile overload
+
_GLIBCXX_ALWAYS_INLINE value_type
fetch_add(difference_type __d,
memory_order __m = memory_order_seq_cst) const noexcept
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..2f57356b366
--- /dev/null
+++ b/libstdc++-v3/include/bits/atomic_timed_wait.h
@@ -0,0 +1,281 @@
+// -*- 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 };
+
+ namespace __detail
+ {
+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
+ 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,
+ static_cast<int>(__futex_wait_flags::__wait_bitset_private),
+ __val, &__rt, nullptr,
+ static_cast<int>(__futex_wait_flags::__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 (is_same_v<__platform_wait_clock_t, _Clock>)
+ {
+ return std::__detail::__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 (std::__detail::__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 (std::__detail::__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(__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 (std::__detail::__cond_wait_until(&_M_cv, __l, __atime) ==
+ __atomic_wait_status::timeout)
+ 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 static_cast<__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 (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..21ec3d38c94
--- /dev/null
+++ b/libstdc++-v3/include/bits/atomic_wait.h
@@ -0,0 +1,301 @@
+// -*- 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
+
+
+// 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;
+
+ constexpr auto __atomic_spin_count_1 = 16;
+ constexpr auto __atomic_spin_count_2 = 12;
+
+ inline constexpr
+ auto __platform_wait_max_value =
+ __gnu_cxx::__int_traits<__platform_wait_t>::__max;
+
+ 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 class __futex_wait_flags : int
+ {
+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE
+ __private_flag = 128,
+#else
+ __private_flag = 0,
+#endif
+ __wait = 0,
+ __wake = 1,
+ __wait_bitset = 9,
+ __wake_bitset = 10,
+ __wait_private = __wait | __private_flag,
+ __wake_private = __wake | __private_flag,
+ __wait_bitset_private = __wait_bitset | __private_flag,
+ __wake_bitset_private = __wake_bitset | __private_flag,
+ __bitset_match_any = -1
+ };
+
+ template<typename _Tp>
+ void
+ __platform_wait(const _Tp* __addr, __platform_wait_t __val) noexcept
+ {
+ auto __e = syscall (SYS_futex, static_cast<const void*>(__addr),
+ static_cast<int>(__futex_wait_flags::__wait_private),
+ __val, nullptr);
+ if (__e && !(errno == EINTR || errno == EAGAIN))
+ std::terminate();
+ }
+
+ template<typename _Tp>
+ void
+ __platform_notify(const _Tp* __addr, bool __all) noexcept
+ {
+ syscall (SYS_futex, static_cast<const void*>(__addr),
+ static_cast<int>(__futex_wait_flags::__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;
+ __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(const 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(static_cast<const 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<typename _Pred>
+ bool
+ __atomic_spin(_Pred __pred) noexcept
+ {
+ for (auto __i = 0; __i < __detail::__atomic_spin_count_1; ++__i)
+ {
+ if (__pred())
+ return true;
+
+ if (__i < __detail::__atomic_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(__addr, __old);
+ }
+ 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..ed127a7a953
--- /dev/null
+++ b/libstdc++-v3/include/bits/semaphore_base.h
@@ -0,0 +1,283 @@
+// -*- 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_base.h
+ * This is an internal header file, included by other library headers.
+ * Do not attempt to use it directly. @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 __has_include(<semaphore.h>)
+#define _GLIBCXX_HAVE_POSIX_SEMAPHORE 1
+#include <semaphore.h>
+#endif
+
+#include <chrono>
+#include <type_traits>
+
+#include <iostream>
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
+ struct __platform_semaphore
+ {
+ using __clock_t = chrono::system_clock;
+ static constexpr ptrdiff_t _S_max = SEM_VALUE_MAX;
+
+ explicit __platform_semaphore(ptrdiff_t __count) noexcept
+ {
+ sem_init(&_M_semaphore, 0, __count);
+ }
+
+ __platform_semaphore(const __platform_semaphore&) = delete;
+ __platform_semaphore& operator=(const __platform_semaphore&) = delete;
+
+ ~__platform_semaphore()
+ { sem_destroy(&_M_semaphore); }
+
+ _GLIBCXX_ALWAYS_INLINE void
+ _M_acquire() noexcept
+ {
+ for (;;)
+ {
+ auto __err = sem_wait(&_M_semaphore);
+ if (__err && (errno == EINTR))
+ continue;
+ else if (__err)
+ std::terminate();
+ else
+ break;
+ }
+ }
+
+ _GLIBCXX_ALWAYS_INLINE void
+ _M_release(std::ptrdiff_t __update) noexcept
+ {
+ for(; __update != 0; --__update)
+ {
+ auto __err = sem_post(&_M_semaphore);
+ if (__err)
+ std::terminate();
+ }
+ }
+
+ bool
+ _M_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())
+ };
+
+ for (;;)
+ {
+ auto __err = sem_timedwait(&_M_semaphore, &__ts);
+ if (__err && (errno == EINTR))
+ continue;
+ else if (__err && (errno == ETIMEDOUT))
+ return false;
+ else if (__err && (errno == EINVAL))
+ return false; // caller supplied an invalid __atime
+ else if (__err)
+ std::terminate();
+ else
+ break;
+ }
+ return true;
+ }
+
+ template<typename _Clock, typename _Duration>
+ bool
+ _M_try_acquire_until(const chrono::time_point<_Clock, _Duration>& __atime) noexcept
+ {
+ if constexpr (std::is_same<__clock_t, _Clock>::value)
+ {
+ return _M_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 (_M_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
+ _M_try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime) noexcept
+ { return _M_try_acquire_until(__clock_t::now() + __rtime); }
+
+ private:
+ sem_t _M_semaphore;
+ };
+#endif // _GLIBCXX_HAVE_POSIX_SEMAPHORE
+
+ template<typename _Tp>
+ struct __atomic_semaphore
+ {
+ static_assert(std::is_integral_v<_Tp>);
+ static constexpr ptrdiff_t _S_max = __gnu_cxx::__int_traits<_Tp>::__max;
+
+ explicit __atomic_semaphore(_Tp __count) noexcept
+ : _M_a(__count)
+ { }
+
+ __atomic_semaphore(const __atomic_semaphore&) = delete;
+ __atomic_semaphore& operator=(const __atomic_semaphore&) = delete;
+
+ _GLIBCXX_ALWAYS_INLINE void
+ _M_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
+ _M_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
+ _M_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
+ _M_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
+ _M_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(__alignof__(_Tp)) _Tp _M_a;
+ };
+
+#ifdef _GLIBCXX_HAVE_LINUX_FUTEX && !_GLIBCXX_REQUIRE_POSIX_SEMAPHORE
+ // Use futex if available and didn't force use of POSIX
+ using __fast_semaphore = __atomic_semaphore<__detail::__platform_wait_t>;
+#elif _GLIBCXX_HAVE_POSIX_SEMAPHORE
+ using __fast_semaphore = __platform_semaphore;
+#else
+ using __fast_semaphore = __atomic_semaphore<ptrdiff_t>;
+#endif
+
+template<ptrdiff_t __least_max_value>
+ using __semaphore_impl = conditional_t<
+ (__least_max_value > 1),
+ conditional_t<
+ (__least_max_value <= __fast_semaphore::_S_max),
+ __fast_semaphore,
+ __atomic_semaphore<ptrdiff_t>>,
+ __fast_semaphore>;
+
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace std
+
+#endif
diff --git a/libstdc++-v3/include/std/atomic b/libstdc++-v3/include/std/atomic
index 1a304261fe7..c15909d9ccb 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
@@ -363,6 +376,27 @@ _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) const noexcept
+ {
+ std::__atomic_wait(&_M_i, __old,
+ [__m, this, __old]
+ {
+ const auto __v = this->load(__m);
+ // TODO make this ignore padding bits when we can do that
+ return __builtin_memcmp(&__old, &__v, sizeof(_Tp)) != 0;
+ });
+ }
+
+ // TODO add const volatile overload
+
+ void notify_one() const noexcept
+ { std::__atomic_notify(&_M_i, false); }
+
+ void notify_all() const noexcept
+ { std::__atomic_notify(&_M_i, true); }
+#endif
+
};
#undef _GLIBCXX20_INIT
@@ -601,6 +635,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
@@ -1353,6 +1399,33 @@ _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..bd06db5aa7f
--- /dev/null
+++ b/libstdc++-v3/include/std/latch
@@ -0,0 +1,90 @@
+// <latch> -*- C++ -*-
+
+// 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.
+ */
+
+#ifndef _GLIBCXX_LATCH
+#define _GLIBCXX_LATCH
+
+#pragma GCC system_header
+
+#if __cplusplus > 201703L
+#define __cpp_lib_latch 201907L
+
+#include <bits/atomic_base.h>
+#include <ext/numeric_traits.h>
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+ class latch
+ {
+ public:
+ static constexpr ptrdiff_t
+ max() noexcept
+ { return __gnu_cxx::__numeric_traits<ptrdiff_t>::__max; }
+
+ constexpr explicit latch(ptrdiff_t __expected) noexcept
+ : _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(__alignof__(ptrdiff_t)) ptrdiff_t _M_a;
+ };
+_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..865d6c4aecb
--- /dev/null
+++ b/libstdc++-v3/include/std/semaphore
@@ -0,0 +1,92 @@
+// <semaphore> -*- C++ -*-
+
+// 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.
+ */
+
+#ifndef _GLIBCXX_SEMAPHORE
+#define _GLIBCXX_SEMAPHORE
+
+#pragma GCC system_header
+
+#if __cplusplus > 201703L
+#define __cpp_lib_semaphore 201907L
+#include <bits/semaphore_base.h>
+#include <ext/numeric_traits.h>
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+ template<ptrdiff_t __least_max_value =
+ __gnu_cxx::__numeric_traits<ptrdiff_t>::__max>
+ class counting_semaphore
+ {
+ static_assert(__least_max_value >= 0);
+
+ __semaphore_impl<__least_max_value> _M_sem;
+
+ 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) noexcept(noexcept(_M_sem._M_release(1)))
+ { _M_sem._M_release(__update); }
+
+ void
+ acquire() noexcept(noexcept(_M_sem._M_acquire()))
+ { _M_sem._M_acquire(); }
+
+ bool
+ try_acquire() noexcept(noexcept(_M_sem._M_try_acquire()))
+ { return _M_sem._M_try_acquire(); }
+
+ template<class _Rep, class _Period>
+ bool
+ try_acquire_for(const std::chrono::duration<_Rep, _Period>& __rtime)
+ { return _M_sem._M_try_acquire_for(__rtime); }
+
+ template<class _Clock, class _Dur>
+ bool
+ try_acquire_until(const std::chrono::time_point<_Clock, _Dur>& __atime)
+ { return _M_sem._M_try_acquire_until(__atime); }
+ };
+
+ 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 f64aff4f520..72d7769ebcf 100644
--- a/libstdc++-v3/include/std/version
+++ b/libstdc++-v3/include/std/version
@@ -214,12 +214,14 @@
#ifdef _GLIBCXX_HAS_GTHREADS
# define __cpp_lib_jthread 201911L
#endif
+#define __cpp_lib_latch 201907L
#define __cpp_lib_list_remove_return_type 201806L
#define __cpp_lib_math_constants 201907L
#define __cpp_lib_polymorphic_allocator 201902L
#if __cpp_lib_concepts
# define __cpp_lib_ranges 201911L
#endif
+#define __cpp_lib_semaphore 201907L
#define __cpp_lib_shift 201806L
#define __cpp_lib_span 202002L
#define __cpp_lib_ssize 201902L
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" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <atomic>
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+#include <chrono>
+#include <type_traits>
+
+#include <testsuite_hooks.h>
+
+template<typename Tp>
+Tp check_wait_notify(Tp val1, Tp val2)
+{
+ using namespace std::literals::chrono_literals;
+
+ std::mutex m;
+ std::condition_variable cv;
+
+ Tp aa = val1;
+ std::atomic_ref<Tp> a(aa);
+ std::thread t([&]
+ {
+ cv.notify_one();
+ a.wait(val1);
+ if (a.load() != val2)
+ a = val1;
+ });
+ std::unique_lock<std::mutex> l(m);
+ cv.wait(l);
+ std::this_thread::sleep_for(100ms);
+ a.store(val2);
+ a.notify_one();
+ t.join();
+ return a.load();
+}
+
+template<typename Tp,
+ bool = std::is_integral_v<Tp>
+ || std::is_floating_point_v<Tp>>
+struct check;
+
+template<typename Tp>
+struct check<Tp, true>
+{
+ check()
+ {
+ Tp a = 0;
+ Tp b = 42;
+ VERIFY(check_wait_notify(a, b) == b);
+ }
+};
+
+template<typename Tp>
+struct check<Tp, false>
+{
+ check(Tp b)
+ {
+ Tp a;
+ VERIFY(check_wait_notify(a, b) == b);
+ }
+};
+
+struct foo
+{
+ long a = 0;
+ long b = 0;
+
+ foo& operator=(foo const&) = default;
+
+ friend bool
+ operator==(foo const& rhs, foo const& lhs)
+ { return rhs.a == lhs.a && rhs.b == lhs.b; }
+};
+
+int
+main ()
+{
+ check<long>();
+ check<double>();
+ check<foo>({42, 48});
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc
new file mode 100644
index 00000000000..b9fc063c66f
--- /dev/null
+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/bool.cc
@@ -0,0 +1,59 @@
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <atomic>
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+#include <type_traits>
+#include <chrono>
+
+#include <testsuite_hooks.h>
+
+int
+main ()
+{
+ using namespace std::literals::chrono_literals;
+
+ std::mutex m;
+ std::condition_variable cv;
+
+ std::atomic<bool> a(false);
+ std::atomic<bool> b(false);
+ std::thread t([&]
+ {
+ cv.notify_one();
+ a.wait(false);
+ if (a.load())
+ {
+ b.store(true);
+ }
+ });
+ std::unique_lock<std::mutex> l(m);
+ cv.wait(l);
+ std::this_thread::sleep_for(100ms);
+ a.store(true);
+ a.notify_one();
+ t.join();
+ VERIFY( b.load() );
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc
new file mode 100644
index 00000000000..1d032085752
--- /dev/null
+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/floats.cc
@@ -0,0 +1,32 @@
+// { dg-options "-std=gnu++2a -pthread -latomic -L../../libatomic/.libs" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include "generic.h"
+
+int
+main ()
+{
+ check<float> f;
+ check<double> d;
+ check<long double> l;
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc
new file mode 100644
index 00000000000..d15b9c86ae6
--- /dev/null
+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.cc
@@ -0,0 +1,31 @@
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include "generic.h"
+
+int
+main ()
+{
+ struct S{ int i; };
+ check<S> check_s{S{0},S{42}};
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h
new file mode 100644
index 00000000000..a319e8b60a6
--- /dev/null
+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/generic.h
@@ -0,0 +1,160 @@
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <atomic>
+#include <chrono>
+#include <condition_variable>
+#include <concepts>
+#include <mutex>
+#include <thread>
+
+#include <testsuite_hooks.h>
+
+#include <iostream>
+
+template<typename Tp>
+Tp check_wait_notify(Tp val1, Tp val2)
+ requires std::equality_comparable<Tp>
+{
+ using namespace std::literals::chrono_literals;
+
+ std::mutex m;
+ std::condition_variable cv;
+
+ std::atomic<Tp> a(val1);
+ std::thread t([&]
+ {
+ cv.notify_one();
+ a.wait(val1);
+ if (a.load() != val2)
+ a = val1;
+ });
+ std::unique_lock<std::mutex> l(m);
+ cv.wait(l);
+ std::this_thread::sleep_for(100ms);
+ a.store(val2);
+ a.notify_one();
+ t.join();
+ return a.load();
+}
+
+template<typename Tp>
+Tp check_wait_notify(Tp val1, Tp val2)
+{
+ using namespace std::literals::chrono_literals;
+
+ std::mutex m;
+ std::condition_variable cv;
+
+ std::atomic<Tp> a(val1);
+ std::thread t([&]
+ {
+ cv.notify_one();
+ a.wait(val1);
+ auto v = a.load();
+ // TODO this needs to zero padding bits when we can do that
+ if (__builtin_memcmp(&v, &val2, sizeof(Tp)) != 0)
+ a = val1;
+ });
+ std::unique_lock<std::mutex> l(m);
+ cv.wait(l);
+ std::this_thread::sleep_for(100ms);
+ a.store(val2);
+ a.notify_one();
+ t.join();
+ return a.load();
+}
+
+template<typename Tp>
+Tp check_atomic_wait_notify(Tp val1, Tp val2)
+ requires std::equality_comparable<Tp>
+{
+ using namespace std::literals::chrono_literals;
+
+ std::mutex m;
+ std::condition_variable cv;
+
+ std::atomic<Tp> a(val1);
+ std::thread t([&]
+ {
+ cv.notify_one();
+ std::atomic_wait(&a, val1);
+ if (a.load() != val2)
+ a = val1;
+ });
+ std::unique_lock<std::mutex> l(m);
+ cv.wait(l);
+ std::this_thread::sleep_for(100ms);
+ a.store(val2);
+ std::atomic_notify_one(&a);
+ t.join();
+ return a.load();
+}
+
+template<typename Tp>
+Tp check_atomic_wait_notify(Tp val1, Tp val2)
+{
+ using namespace std::literals::chrono_literals;
+
+ std::mutex m;
+ std::condition_variable cv;
+
+ std::atomic<Tp> a(val1);
+ std::thread t([&]
+ {
+ cv.notify_one();
+ std::atomic_wait(&a, val1);
+ auto v = a.load();
+ // TODO this needs to zero padding bits when we can do that
+ if (__builtin_memcmp(&v, &val2, sizeof(Tp)) != 0)
+ a = val1;
+ });
+ std::unique_lock<std::mutex> l(m);
+ cv.wait(l);
+ std::this_thread::sleep_for(100ms);
+ a.store(val2);
+ std::atomic_notify_one(&a);
+ t.join();
+ return a.load();
+}
+
+template<typename Tp>
+struct check
+{
+ check(Tp a = 0, Tp b = 42)
+ {
+ if constexpr (std::equality_comparable<Tp>)
+ {
+ VERIFY( check_wait_notify(a, b) == b);
+ VERIFY( check_atomic_wait_notify(a, b) == b);
+ }
+ else
+ {
+ {
+ // TODO this needs to zero padding bits when we can do that
+ auto v = check_wait_notify(a, b);
+ VERIFY( __builtin_memcmp(&v, &b, sizeof(Tp)) == 0 );
+ }
+
+ {
+ // TODO this needs to zero padding bits when we can do that
+ auto v = check_atomic_wait_notify(a, b);
+ VERIFY( __builtin_memcmp(&v, &b, sizeof(Tp)) == 0);
+ }
+ }
+ }
+};
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc
new file mode 100644
index 00000000000..115cb79a040
--- /dev/null
+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/integrals.cc
@@ -0,0 +1,65 @@
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include "generic.h"
+
+void
+test01()
+{
+ struct S{ int i; };
+ std::atomic<S> s;
+
+ s.wait(S{42});
+}
+
+int
+main ()
+{
+ // check<bool> bb;
+ check<char> ch;
+ check<signed char> sch;
+ check<unsigned char> uch;
+ check<short> s;
+ check<unsigned short> us;
+ check<int> i;
+ check<unsigned int> ui;
+ check<long> l;
+ check<unsigned long> ul;
+ check<long long> ll;
+ check<unsigned long long> ull;
+
+ check<wchar_t> wch;
+ check<char8_t> ch8;
+ check<char16_t> ch16;
+ check<char32_t> ch32;
+
+ check<int8_t> i8;
+ check<int16_t> i16;
+ check<int32_t> i32;
+ check<int64_t> i64;
+
+ check<uint8_t> u8;
+ check<uint16_t> u16;
+ check<uint32_t> u32;
+ check<uint64_t> u64;
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc
new file mode 100644
index 00000000000..8531bb2e788
--- /dev/null
+++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/pointers.cc
@@ -0,0 +1,59 @@
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+#include <atomic>
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+#include <type_traits>
+#include <chrono>
+
+#include <testsuite_hooks.h>
+
+int
+main ()
+{
+ using namespace std::literals::chrono_literals;
+
+ std::mutex m;
+ std::condition_variable cv;
+
+ long aa;
+ long bb;
+
+ std::atomic<long*> a(nullptr);
+ std::thread t([&]
+ {
+ cv.notify_one();
+ a.wait(nullptr);
+ if (a.load() == &aa)
+ a.store(&bb);
+ });
+ std::unique_lock<std::mutex> l(m);
+ cv.wait(l);
+ std::this_thread::sleep_for(100ms);
+ a.store(&aa);
+ a.notify_one();
+ t.join();
+ VERIFY( a.load() == &bb);
+ return 0;
+}
diff --git a/libstdc++-v3/testsuite/30_threads/latch/1.cc b/libstdc++-v3/testsuite/30_threads/latch/1.cc
new file mode 100644
index 00000000000..aa203cdf525
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/latch/1.cc
@@ -0,0 +1,27 @@
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <latch>
+
+#ifndef __cpp_lib_latch
+# error "Feature-test macro for latch missing in <latch>"
+#elif __cpp_lib_latch!= 201907L
+# error "Feature-test macro for latch has wrong value in <latch>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/latch/2.cc b/libstdc++-v3/testsuite/30_threads/latch/2.cc
new file mode 100644
index 00000000000..318a859ee21
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/latch/2.cc
@@ -0,0 +1,27 @@
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <version>
+
+#ifndef __cpp_lib_latch
+# error "Feature-test macro for latch missing in <version>"
+#elif __cpp_lib_latch != 201907L
+# error "Feature-test macro for latch has wrong value in <version>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/latch/3.cc b/libstdc++-v3/testsuite/30_threads/latch/3.cc
new file mode 100644
index 00000000000..cf1a31f996b
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/latch/3.cc
@@ -0,0 +1,50 @@
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+//
+#include <latch>
+#include <atomic>
+#include <thread>
+#include <testsuite_hooks.h>
+
+int main()
+{
+ std::atomic<int> a(0);
+
+ std::latch l(3);
+
+ VERIFY( !l.try_wait() );
+
+ auto fn = [&]
+ {
+ ++a;
+ l.count_down();
+ };
+
+ std::thread t0(fn);
+ std::thread t1(fn);
+
+ l.arrive_and_wait();
+ t0.join();
+ t1.join();
+
+ VERIFY( l.try_wait() );
+}
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/1.cc b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc
new file mode 100644
index 00000000000..1bbca687fc3
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc
@@ -0,0 +1,27 @@
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <semaphore>
+
+#ifndef __cpp_lib_semaphore
+# error "Feature-test macro for semaphore missing in <semaphore>"
+#elif __cpp_lib_semaphore != 201907L
+# error "Feature-test macro for semaphore has wrong value in <semaphore>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/2.cc b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc
new file mode 100644
index 00000000000..98743f5e27c
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc
@@ -0,0 +1,27 @@
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <version>
+
+#ifndef __cpp_lib_semaphore
+# error "Feature-test macro for semaphore missing in <version>"
+#elif __cpp_lib_semaphore != 201907L
+# error "Feature-test macro for semaphore has wrong value in <version>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc b/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc
new file mode 100644
index 00000000000..d74cfad53e9
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc
@@ -0,0 +1,30 @@
+// 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.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <semaphore>
+
+int main()
+{
+ std::counting_semaphore<-1> sem(2);
+ return 0;
+}
+// { dg-error "static assertion failed" "" { target *-*-* } 0 }
diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc
new file mode 100644
index 00000000000..25280441d07
--- /dev/null
+++ b/