[libcxx-commits] [libcxx] 54fa9ec - [libc++] Implementation of C++20's P1135R6 for libcxx
Louis Dionne via libcxx-commits
libcxx-commits at lists.llvm.org
Mon Feb 24 07:59:50 PST 2020
Author: Olivier Giroux
Date: 2020-02-24T10:59:35-05:00
New Revision: 54fa9ecd3088508b05b0c5b5cb52da8a3c188655
URL: https://github.com/llvm/llvm-project/commit/54fa9ecd3088508b05b0c5b5cb52da8a3c188655
DIFF: https://github.com/llvm/llvm-project/commit/54fa9ecd3088508b05b0c5b5cb52da8a3c188655.diff
LOG: [libc++] Implementation of C++20's P1135R6 for libcxx
Differential Revision: https://reviews.llvm.org/D68480
Added:
libcxx/include/barrier
libcxx/include/latch
libcxx/include/semaphore
libcxx/src/atomic.cpp
libcxx/src/barrier.cpp
libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.wait/atomic_wait.pass.cpp
libcxx/test/std/atomics/types.pass.cpp
libcxx/test/std/thread/thread.barrier/arrive.pass.cpp
libcxx/test/std/thread/thread.barrier/arrive_and_drop.pass.cpp
libcxx/test/std/thread/thread.barrier/arrive_and_wait.pass.cpp
libcxx/test/std/thread/thread.barrier/completion.pass.cpp
libcxx/test/std/thread/thread.barrier/max.pass.cpp
libcxx/test/std/thread/thread.barrier/version.pass.cpp
libcxx/test/std/thread/thread.latch/arrive_and_wait.pass.cpp
libcxx/test/std/thread/thread.latch/count_down.pass.cpp
libcxx/test/std/thread/thread.latch/max.pass.cpp
libcxx/test/std/thread/thread.latch/try_wait.pass.cpp
libcxx/test/std/thread/thread.latch/version.pass.cpp
libcxx/test/std/thread/thread.semaphore/acquire.pass.cpp
libcxx/test/std/thread/thread.semaphore/binary.pass.cpp
libcxx/test/std/thread/thread.semaphore/max.pass.cpp
libcxx/test/std/thread/thread.semaphore/release.pass.cpp
libcxx/test/std/thread/thread.semaphore/timed.pass.cpp
libcxx/test/std/thread/thread.semaphore/try_acquire.pass.cpp
libcxx/test/std/thread/thread.semaphore/version.pass.cpp
Modified:
libcxx/include/CMakeLists.txt
libcxx/include/__threading_support
libcxx/include/atomic
libcxx/include/module.modulemap
libcxx/src/CMakeLists.txt
libcxx/src/include/apple_availability.h
libcxx/test/libcxx/double_include.sh.cpp
libcxx/www/cxx2a_status.html
Removed:
################################################################################
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 302da8a131b4..ca6c7c5c5675 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -25,6 +25,7 @@ set(files
any
array
atomic
+ barrier
bit
bitset
cassert
@@ -103,6 +104,7 @@ set(files
iostream
istream
iterator
+ latch
limits
limits.h
list
@@ -122,6 +124,7 @@ set(files
ratio
regex
scoped_allocator
+ semaphore
set
setjmp.h
shared_mutex
diff --git a/libcxx/include/__threading_support b/libcxx/include/__threading_support
index dbf313a1bf22..fef172c70ea7 100644
--- a/libcxx/include/__threading_support
+++ b/libcxx/include/__threading_support
@@ -26,6 +26,10 @@
#if defined(_LIBCPP_HAS_THREAD_API_PTHREAD)
# include <pthread.h>
# include <sched.h>
+# include <semaphore.h>
+# ifdef __APPLE__
+# define _LIBCPP_NO_NATIVE_SEMAPHORES
+# endif
#elif defined(_LIBCPP_HAS_THREAD_API_C11)
# include <threads.h>
#endif
@@ -65,6 +69,10 @@ typedef pthread_mutex_t __libcpp_recursive_mutex_t;
typedef pthread_cond_t __libcpp_condvar_t;
#define _LIBCPP_CONDVAR_INITIALIZER PTHREAD_COND_INITIALIZER
+// Semaphore
+typedef sem_t __libcpp_semaphore_t;
+#define _LIBCPP_SEMAPHORE_MAX SEM_VALUE_MAX
+
// Execute once
typedef pthread_once_t __libcpp_exec_once_flag;
#define _LIBCPP_EXEC_ONCE_INITIALIZER PTHREAD_ONCE_INIT
@@ -127,6 +135,9 @@ typedef void* __libcpp_recursive_mutex_t[5];
typedef void* __libcpp_condvar_t;
#define _LIBCPP_CONDVAR_INITIALIZER 0
+// Semaphore
+typedef void* __libcpp_semaphore_t;
+
// Execute Once
typedef void* __libcpp_exec_once_flag;
#define _LIBCPP_EXEC_ONCE_INITIALIZER 0
@@ -191,6 +202,22 @@ int __libcpp_condvar_timedwait(__libcpp_condvar_t *__cv, __libcpp_mutex_t *__m,
_LIBCPP_THREAD_ABI_VISIBILITY
int __libcpp_condvar_destroy(__libcpp_condvar_t* __cv);
+// Semaphore
+_LIBCPP_THREAD_ABI_VISIBILITY
+bool __libcpp_semaphore_init(__libcpp_semaphore_t* __sem, int __init);
+
+_LIBCPP_THREAD_ABI_VISIBILITY
+bool __libcpp_semaphore_destroy(__libcpp_semaphore_t* __sem);
+
+_LIBCPP_THREAD_ABI_VISIBILITY
+bool __libcpp_semaphore_post(__libcpp_semaphore_t* __sem);
+
+_LIBCPP_THREAD_ABI_VISIBILITY
+bool __libcpp_semaphore_wait(__libcpp_semaphore_t* __sem);
+
+_LIBCPP_THREAD_ABI_VISIBILITY
+bool __libcpp_semaphore_wait_timed(__libcpp_semaphore_t* __sem, chrono::nanoseconds const& __ns);
+
// Execute once
_LIBCPP_THREAD_ABI_VISIBILITY
int __libcpp_execute_once(__libcpp_exec_once_flag *flag,
@@ -229,6 +256,16 @@ void __libcpp_thread_yield();
_LIBCPP_THREAD_ABI_VISIBILITY
void __libcpp_thread_sleep_for(const chrono::nanoseconds& __ns);
+struct __libcpp_timed_backoff_policy {
+ _LIBCPP_THREAD_ABI_VISIBILITY
+ bool operator()(chrono::nanoseconds __elapsed) const;
+};
+
+template<class _Fn, class _BFn>
+_LIBCPP_INLINE_VISIBILITY
+bool __libcpp_thread_poll_with_backoff(
+ _Fn && __f, _BFn && __bf, chrono::nanoseconds __max_elapsed = chrono::nanoseconds::zero());
+
// Thread local storage
_LIBCPP_THREAD_ABI_VISIBILITY
int __libcpp_tls_create(__libcpp_tls_key* __key,
@@ -364,6 +401,38 @@ int __libcpp_condvar_destroy(__libcpp_condvar_t *__cv)
return pthread_cond_destroy(__cv);
}
+#ifndef _LIBCPP_NO_NATIVE_SEMAPHORES
+
+// Semaphore
+bool __libcpp_semaphore_init(__libcpp_semaphore_t* __sem, int __init)
+{
+ return sem_init(__sem, 0, __init) == 0;
+}
+
+bool __libcpp_semaphore_destroy(__libcpp_semaphore_t* __sem)
+{
+ return sem_destroy(__sem) == 0;
+}
+
+bool __libcpp_semaphore_post(__libcpp_semaphore_t* __sem)
+{
+ return sem_post(__sem) == 0;
+}
+
+bool __libcpp_semaphore_wait(__libcpp_semaphore_t* __sem)
+{
+ return sem_wait(__sem) == 0;
+}
+
+bool __libcpp_semaphore_wait_timed(__libcpp_semaphore_t* __sem, chrono::nanoseconds const& __ns)
+{
+ auto const __abs_time = chrono::system_clock::now().time_since_epoch() + __ns;
+ __libcpp_timespec_t __ts = __thread_detail::__convert_to_timespec(__abs_time);
+ return sem_timedwait(__sem, &__ts) == 0;
+}
+
+#endif //_LIBCPP_NO_NATIVE_SEMAPHORES
+
// Execute once
int __libcpp_execute_once(__libcpp_exec_once_flag *flag,
void (*init_routine)()) {
@@ -425,6 +494,40 @@ void __libcpp_thread_sleep_for(const chrono::nanoseconds& __ns)
while (nanosleep(&__ts, &__ts) == -1 && errno == EINTR);
}
+bool __libcpp_timed_backoff_policy::operator()(chrono::nanoseconds __elapsed) const
+{
+ if(__elapsed > chrono::milliseconds(128))
+ __libcpp_thread_sleep_for(chrono::milliseconds(8));
+ else if(__elapsed > chrono::microseconds(64))
+ __libcpp_thread_sleep_for(__elapsed / 2);
+ else if(__elapsed > chrono::microseconds(4))
+ __libcpp_thread_yield();
+ else
+ ; // poll
+ return false;
+}
+
+static constexpr int __libcpp_polling_count = 64;
+
+template<class _Fn, class _BFn>
+bool __libcpp_thread_poll_with_backoff(_Fn && __f, _BFn && __bf, chrono::nanoseconds __max_elapsed)
+{
+ auto const __start = chrono::high_resolution_clock::now();
+ for(int __count = 0;;) {
+ if(__f())
+ return true; // _Fn completion means success
+ if(__count < __libcpp_polling_count) {
+ __count += 1;
+ continue;
+ }
+ chrono::nanoseconds const __elapsed = chrono::high_resolution_clock::now() - __start;
+ if(__max_elapsed != chrono::nanoseconds::zero() && __max_elapsed < __elapsed)
+ return false; // timeout failure
+ if(__bf(__elapsed))
+ return false; // _BFn completion means failure
+ }
+}
+
// Thread local storage
int __libcpp_tls_create(__libcpp_tls_key *__key, void (*__at_exit)(void *))
{
diff --git a/libcxx/include/atomic b/libcxx/include/atomic
index 6904dd400032..02784df7d54c 100644
--- a/libcxx/include/atomic
+++ b/libcxx/include/atomic
@@ -547,8 +547,10 @@ void atomic_signal_fence(memory_order m) noexcept;
*/
#include <__config>
+#include <__threading_support>
#include <cstddef>
#include <cstdint>
+#include <cstring>
#include <type_traits>
#include <version>
@@ -629,6 +631,11 @@ typedef enum memory_order {
#endif // _LIBCPP_STD_VER > 17
+template <typename _Tp> _LIBCPP_INLINE_VISIBILITY
+bool __cxx_nonatomic_compare_equal(_Tp const& __lhs, _Tp const& __rhs) {
+ return memcmp(&__lhs, &__rhs, sizeof(_Tp)) == 0;
+}
+
static_assert((is_same<underlying_type<memory_order>::type, __memory_order_underlying_t>::value),
"unexpected underlying type for std::memory_order");
@@ -1218,9 +1225,9 @@ _LIBCPP_INLINE_VISIBILITY
bool __cxx_atomic_compare_exchange_strong(volatile __cxx_atomic_lock_impl<_Tp>* __a,
_Tp* __expected, _Tp __value, memory_order, memory_order) {
__a->__lock();
- _Tp temp;
- __cxx_atomic_assign_volatile(temp, __a->__a_value);
- bool __ret = temp == *__expected;
+ _Tp __temp;
+ __cxx_atomic_assign_volatile(__temp, __a->__a_value);
+ bool __ret = __temp == *__expected;
if(__ret)
__cxx_atomic_assign_volatile(__a->__a_value, __value);
else
@@ -1247,9 +1254,9 @@ _LIBCPP_INLINE_VISIBILITY
bool __cxx_atomic_compare_exchange_weak(volatile __cxx_atomic_lock_impl<_Tp>* __a,
_Tp* __expected, _Tp __value, memory_order, memory_order) {
__a->__lock();
- _Tp temp;
- __cxx_atomic_assign_volatile(temp, __a->__a_value);
- bool __ret = temp == *__expected;
+ _Tp __temp;
+ __cxx_atomic_assign_volatile(__temp, __a->__a_value);
+ bool __ret = __temp == *__expected;
if(__ret)
__cxx_atomic_assign_volatile(__a->__a_value, __value);
else
@@ -1452,6 +1459,73 @@ struct __cxx_atomic_impl : public _Base {
: _Base(value) {}
};
+#ifdef __linux__
+ using __cxx_contention_t = int32_t;
+#else
+ using __cxx_contention_t = int64_t;
+#endif //__linux__
+
+#if _LIBCPP_STD_VER >= 11
+
+using __cxx_atomic_contention_t = __cxx_atomic_impl<__cxx_contention_t>;
+
+#ifndef _LIBCPP_HAS_NO_PLATFORM_WAIT
+
+_LIBCPP_EXPORTED_FROM_ABI void __cxx_atomic_notify_one(void const volatile*);
+_LIBCPP_EXPORTED_FROM_ABI void __cxx_atomic_notify_all(void const volatile*);
+_LIBCPP_EXPORTED_FROM_ABI __cxx_contention_t __libcpp_atomic_monitor(void const volatile*);
+_LIBCPP_EXPORTED_FROM_ABI void __libcpp_atomic_wait(void const volatile*, __cxx_contention_t);
+
+_LIBCPP_EXPORTED_FROM_ABI void __cxx_atomic_notify_one(__cxx_atomic_contention_t const volatile*);
+_LIBCPP_EXPORTED_FROM_ABI void __cxx_atomic_notify_all(__cxx_atomic_contention_t const volatile*);
+_LIBCPP_EXPORTED_FROM_ABI __cxx_contention_t __libcpp_atomic_monitor(__cxx_atomic_contention_t const volatile*);
+_LIBCPP_EXPORTED_FROM_ABI void __libcpp_atomic_wait(__cxx_atomic_contention_t const volatile*, __cxx_contention_t);
+
+template <class _Atp, class _Fn>
+_LIBCPP_INLINE_VISIBILITY bool __cxx_atomic_wait(_Atp* __a, _Fn && __test_fn)
+{
+ auto const __libcpp_atomic_wait_backoff = [=](chrono::nanoseconds __elapsed) -> bool {
+ if(__elapsed > chrono::microseconds(64))
+ {
+ auto const __monitor = __libcpp_atomic_monitor(__a);
+ if(__test_fn())
+ return true;
+ __libcpp_atomic_wait(__a, __monitor);
+ }
+ else if(__elapsed > chrono::microseconds(4))
+ __libcpp_thread_yield();
+ else
+ ; // poll
+ return false;
+ };
+ return __libcpp_thread_poll_with_backoff(__test_fn, __libcpp_atomic_wait_backoff);
+}
+
+#else // _LIBCPP_HAS_NO_PLATFORM_WAIT
+
+template <class _Tp>
+_LIBCPP_INLINE_VISIBILITY void __cxx_atomic_notify_all(__cxx_atomic_impl<_Tp> const volatile*) { }
+template <class _Tp>
+_LIBCPP_INLINE_VISIBILITY void __cxx_atomic_notify_one(__cxx_atomic_impl<_Tp> const volatile*) { }
+template <class _Atp, class _Fn>
+_LIBCPP_INLINE_VISIBILITY bool __cxx_atomic_wait(_Atp*, _Fn && __test_fn)
+{
+ return __libcpp_thread_poll_with_backoff(__test_fn, __libcpp_timed_backoff_policy());
+}
+
+#endif // _LIBCPP_HAS_NO_PLATFORM_WAIT
+
+template <class _Atp, class _Tp>
+_LIBCPP_INLINE_VISIBILITY bool __cxx_atomic_wait(_Atp* __a, _Tp const __val, memory_order __order)
+{
+ auto const __test_fn = [=]() -> bool {
+ return !__cxx_nonatomic_compare_equal(__cxx_atomic_load(__a, __order), __val);
+ };
+ return __cxx_atomic_wait(__a, __test_fn);
+}
+
+#endif //_LIBCPP_STD_VER >= 11
+
// general atomic<T>
template <class _Tp, bool = is_integral<_Tp>::value && !is_same<_Tp, bool>::value>
@@ -1532,6 +1606,19 @@ struct __atomic_base // false
memory_order __m = memory_order_seq_cst) _NOEXCEPT
{return __cxx_atomic_compare_exchange_strong(&__a_, &__e, __d, __m, __m);}
+ _LIBCPP_INLINE_VISIBILITY void wait(_Tp __v, memory_order __m = memory_order_seq_cst) const volatile _NOEXCEPT
+ {__cxx_atomic_wait(&__a_, __v, __m);}
+ _LIBCPP_INLINE_VISIBILITY void wait(_Tp __v, memory_order __m = memory_order_seq_cst) const _NOEXCEPT
+ {__cxx_atomic_wait(&__a_, __v, __m);}
+ _LIBCPP_INLINE_VISIBILITY void notify_one() volatile _NOEXCEPT
+ {__cxx_atomic_notify_one(&__a_);}
+ _LIBCPP_INLINE_VISIBILITY void notify_one() _NOEXCEPT
+ {__cxx_atomic_notify_one(&__a_);}
+ _LIBCPP_INLINE_VISIBILITY void notify_all() volatile _NOEXCEPT
+ {__cxx_atomic_notify_all(&__a_);}
+ _LIBCPP_INLINE_VISIBILITY void notify_all() _NOEXCEPT
+ {__cxx_atomic_notify_all(&__a_);}
+
_LIBCPP_INLINE_VISIBILITY
__atomic_base() _NOEXCEPT _LIBCPP_DEFAULT
@@ -1544,8 +1631,11 @@ struct __atomic_base // false
__atomic_base& operator=(const __atomic_base&) volatile = delete;
#else
private:
+ _LIBCPP_INLINE_VISIBILITY
__atomic_base(const __atomic_base&);
+ _LIBCPP_INLINE_VISIBILITY
__atomic_base& operator=(const __atomic_base&);
+ _LIBCPP_INLINE_VISIBILITY
__atomic_base& operator=(const __atomic_base&) volatile;
#endif
};
@@ -1643,6 +1733,7 @@ struct atomic
: public __atomic_base<_Tp>
{
typedef __atomic_base<_Tp> __base;
+ typedef _Tp value_type;
_LIBCPP_INLINE_VISIBILITY
atomic() _NOEXCEPT _LIBCPP_DEFAULT
_LIBCPP_INLINE_VISIBILITY
@@ -1663,6 +1754,7 @@ struct atomic<_Tp*>
: public __atomic_base<_Tp*>
{
typedef __atomic_base<_Tp*> __base;
+ typedef _Tp* value_type;
_LIBCPP_INLINE_VISIBILITY
atomic() _NOEXCEPT _LIBCPP_DEFAULT
_LIBCPP_INLINE_VISIBILITY
@@ -1947,6 +2039,76 @@ atomic_compare_exchange_strong_explicit(atomic<_Tp>* __o, _Tp* __e,
return __o->compare_exchange_strong(*__e, __d, __s, __f);
}
+// atomic_wait
+
+template <class _Tp>
+_LIBCPP_INLINE_VISIBILITY
+void atomic_wait(const volatile atomic<_Tp>* __o,
+ typename atomic<_Tp>::value_type __v) _NOEXCEPT
+{
+ return __o->wait(__v);
+}
+
+template <class _Tp>
+_LIBCPP_INLINE_VISIBILITY
+void atomic_wait(const atomic<_Tp>* __o,
+ typename atomic<_Tp>::value_type __v) _NOEXCEPT
+{
+ return __o->wait(__v);
+}
+
+// atomic_wait_explicit
+
+template <class _Tp>
+_LIBCPP_INLINE_VISIBILITY
+void atomic_wait_explicit(const volatile atomic<_Tp>* __o,
+ typename atomic<_Tp>::value_type __v,
+ memory_order __m) _NOEXCEPT
+ _LIBCPP_CHECK_LOAD_MEMORY_ORDER(__m)
+{
+ return __o->wait(__v, __m);
+}
+
+template <class _Tp>
+_LIBCPP_INLINE_VISIBILITY
+void atomic_wait_explicit(const atomic<_Tp>* __o,
+ typename atomic<_Tp>::value_type __v,
+ memory_order __m) _NOEXCEPT
+ _LIBCPP_CHECK_LOAD_MEMORY_ORDER(__m)
+{
+ return __o->wait(__v, __m);
+}
+
+// atomic_notify_one
+
+template <class _Tp>
+_LIBCPP_INLINE_VISIBILITY
+void atomic_notify_one(volatile atomic<_Tp>* __o) _NOEXCEPT
+{
+ __o->notify_one();
+}
+template <class _Tp>
+_LIBCPP_INLINE_VISIBILITY
+void atomic_notify_one(atomic<_Tp>* __o) _NOEXCEPT
+{
+ __o->notify_one();
+}
+
+// atomic_notify_one
+
+template <class _Tp>
+_LIBCPP_INLINE_VISIBILITY
+void atomic_notify_all(volatile atomic<_Tp>* __o) _NOEXCEPT
+{
+ __o->notify_all();
+}
+template <class _Tp>
+_LIBCPP_INLINE_VISIBILITY
+void atomic_notify_all(atomic<_Tp>* __o) _NOEXCEPT
+{
+ __o->notify_all();
+}
+
// atomic_fetch_add
template <class _Tp>
@@ -2279,6 +2441,13 @@ typedef struct atomic_flag
{
__cxx_atomic_impl<_LIBCPP_ATOMIC_FLAG_TYPE> __a_;
+ _LIBCPP_INLINE_VISIBILITY
+ bool test(memory_order __m = memory_order_seq_cst) const volatile _NOEXCEPT
+ {return _LIBCPP_ATOMIC_FLAG_TYPE(true) == __cxx_atomic_load(&__a_, __m);}
+ _LIBCPP_INLINE_VISIBILITY
+ bool test(memory_order __m = memory_order_seq_cst) const _NOEXCEPT
+ {return _LIBCPP_ATOMIC_FLAG_TYPE(true) == __cxx_atomic_load(&__a_, __m);}
+
_LIBCPP_INLINE_VISIBILITY
bool test_and_set(memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT
{return __cxx_atomic_exchange(&__a_, _LIBCPP_ATOMIC_FLAG_TYPE(true), __m);}
@@ -2292,6 +2461,25 @@ typedef struct atomic_flag
void clear(memory_order __m = memory_order_seq_cst) _NOEXCEPT
{__cxx_atomic_store(&__a_, _LIBCPP_ATOMIC_FLAG_TYPE(false), __m);}
+ _LIBCPP_INLINE_VISIBILITY
+ void wait(bool __v, memory_order __m = memory_order_seq_cst) const volatile _NOEXCEPT
+ {__cxx_atomic_wait(&__a_, _LIBCPP_ATOMIC_FLAG_TYPE(__v), __m);}
+ _LIBCPP_INLINE_VISIBILITY
+ void wait(bool __v, memory_order __m = memory_order_seq_cst) const _NOEXCEPT
+ {__cxx_atomic_wait(&__a_, _LIBCPP_ATOMIC_FLAG_TYPE(__v), __m);}
+ _LIBCPP_INLINE_VISIBILITY
+ void notify_one() volatile _NOEXCEPT
+ {__cxx_atomic_notify_one(&__a_);}
+ _LIBCPP_INLINE_VISIBILITY
+ void notify_one() _NOEXCEPT
+ {__cxx_atomic_notify_one(&__a_);}
+ _LIBCPP_INLINE_VISIBILITY
+ void notify_all() volatile _NOEXCEPT
+ {__cxx_atomic_notify_all(&__a_);}
+ _LIBCPP_INLINE_VISIBILITY
+ void notify_all() _NOEXCEPT
+ {__cxx_atomic_notify_all(&__a_);}
+
_LIBCPP_INLINE_VISIBILITY
atomic_flag() _NOEXCEPT _LIBCPP_DEFAULT
@@ -2304,12 +2492,44 @@ typedef struct atomic_flag
atomic_flag& operator=(const atomic_flag&) volatile = delete;
#else
private:
+ _LIBCPP_INLINE_VISIBILITY
atomic_flag(const atomic_flag&);
+ _LIBCPP_INLINE_VISIBILITY
atomic_flag& operator=(const atomic_flag&);
+ _LIBCPP_INLINE_VISIBILITY
atomic_flag& operator=(const atomic_flag&) volatile;
#endif
} atomic_flag;
+
+inline _LIBCPP_INLINE_VISIBILITY
+bool
+atomic_flag_test(const volatile atomic_flag* __o) _NOEXCEPT
+{
+ return __o->test();
+}
+
+inline _LIBCPP_INLINE_VISIBILITY
+bool
+atomic_flag_test(const atomic_flag* __o) _NOEXCEPT
+{
+ return __o->test();
+}
+
+inline _LIBCPP_INLINE_VISIBILITY
+bool
+atomic_flag_test_explicit(const volatile atomic_flag* __o, memory_order __m) _NOEXCEPT
+{
+ return __o->test(__m);
+}
+
+inline _LIBCPP_INLINE_VISIBILITY
+bool
+atomic_flag_test_explicit(const atomic_flag* __o, memory_order __m) _NOEXCEPT
+{
+ return __o->test(__m);
+}
+
inline _LIBCPP_INLINE_VISIBILITY
bool
atomic_flag_test_and_set(volatile atomic_flag* __o) _NOEXCEPT
@@ -2366,6 +2586,64 @@ atomic_flag_clear_explicit(atomic_flag* __o, memory_order __m) _NOEXCEPT
__o->clear(__m);
}
+inline _LIBCPP_INLINE_VISIBILITY
+void
+atomic_flag_wait(const volatile atomic_flag* __o, bool __v) _NOEXCEPT
+{
+ __o->wait(__v);
+}
+
+inline _LIBCPP_INLINE_VISIBILITY
+void
+atomic_flag_wait(const atomic_flag* __o, bool __v) _NOEXCEPT
+{
+ __o->wait(__v);
+}
+
+inline _LIBCPP_INLINE_VISIBILITY
+void
+atomic_flag_wait_explicit(const volatile atomic_flag* __o,
+ bool __v, memory_order __m) _NOEXCEPT
+{
+ __o->wait(__v, __m);
+}
+
+inline _LIBCPP_INLINE_VISIBILITY
+void
+atomic_flag_wait_explicit(const atomic_flag* __o,
+ bool __v, memory_order __m) _NOEXCEPT
+{
+ __o->wait(__v, __m);
+}
+
+inline _LIBCPP_INLINE_VISIBILITY
+void
+atomic_flag_notify_one(volatile atomic_flag* __o) _NOEXCEPT
+{
+ __o->notify_one();
+}
+
+inline _LIBCPP_INLINE_VISIBILITY
+void
+atomic_flag_notify_one(atomic_flag* __o) _NOEXCEPT
+{
+ __o->notify_one();
+}
+
+inline _LIBCPP_INLINE_VISIBILITY
+void
+atomic_flag_notify_all(volatile atomic_flag* __o) _NOEXCEPT
+{
+ __o->notify_all();
+}
+
+inline _LIBCPP_INLINE_VISIBILITY
+void
+atomic_flag_notify_all(atomic_flag* __o) _NOEXCEPT
+{
+ __o->notify_all();
+}
+
// fences
inline _LIBCPP_INLINE_VISIBILITY
@@ -2434,6 +2712,33 @@ typedef atomic<ptr
diff _t> atomic_ptr
diff _t;
typedef atomic<intmax_t> atomic_intmax_t;
typedef atomic<uintmax_t> atomic_uintmax_t;
+// atomic_*_lock_free : prefer the contention type most highly, then the largest lock-free type
+
+#ifdef __cpp_lib_atomic_is_always_lock_free
+# define _LIBCPP_CONTENTION_LOCK_FREE __atomic_always_lock_free(sizeof(__cxx_contention_t), 0)
+#else
+# define _LIBCPP_CONTENTION_LOCK_FREE false
+#endif
+
+#if ATOMIC_LLONG_LOCK_FREE == 2
+typedef conditional<_LIBCPP_CONTENTION_LOCK_FREE, __cxx_contention_t, long long>::type __libcpp_signed_lock_free;
+typedef conditional<_LIBCPP_CONTENTION_LOCK_FREE, __cxx_contention_t, unsigned long long>::type __libcpp_unsigned_lock_free;
+#elif ATOMIC_INT_LOCK_FREE == 2
+typedef conditional<_LIBCPP_CONTENTION_LOCK_FREE, __cxx_contention_t, int>::type __libcpp_signed_lock_free;
+typedef conditional<_LIBCPP_CONTENTION_LOCK_FREE, __cxx_contention_t, unsigned int>::type __libcpp_unsigned_lock_free;
+#elif ATOMIC_SHORT_LOCK_FREE == 2
+typedef conditional<_LIBCPP_CONTENTION_LOCK_FREE, __cxx_contention_t, short>::type __libcpp_signed_lock_free;
+typedef conditional<_LIBCPP_CONTENTION_LOCK_FREE, __cxx_contention_t, unsigned short>::type __libcpp_unsigned_lock_free;
+#elif ATOMIC_CHAR_LOCK_FREE == 2
+typedef conditional<_LIBCPP_CONTENTION_LOCK_FREE, __cxx_contention_t, char>::type __libcpp_signed_lock_free;
+typedef conditional<_LIBCPP_CONTENTION_LOCK_FREE, __cxx_contention_t, unsigned char>::type __libcpp_unsigned_lock_free;
+#else
+ // No signed/unsigned lock-free types
+#endif
+
+typedef atomic<__libcpp_signed_lock_free> atomic_signed_lock_free;
+typedef atomic<__libcpp_unsigned_lock_free> atomic_unsigned_lock_free;
+
#define ATOMIC_FLAG_INIT {false}
#define ATOMIC_VAR_INIT(__v) {__v}
diff --git a/libcxx/include/barrier b/libcxx/include/barrier
new file mode 100644
index 000000000000..bf2b9a88297a
--- /dev/null
+++ b/libcxx/include/barrier
@@ -0,0 +1,322 @@
+// -*- C++ -*-
+//===--------------------------- barrier ----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_BARRIER
+#define _LIBCPP_BARRIER
+
+/*
+ barrier synopsis
+
+namespace std
+{
+
+ template<class CompletionFunction = see below>
+ class barrier
+ {
+ public:
+ using arrival_token = see below;
+
+ constexpr explicit barrier(ptr
diff _t phase_count,
+ CompletionFunction f = CompletionFunction());
+ ~barrier();
+
+ barrier(const barrier&) = delete;
+ barrier& operator=(const barrier&) = delete;
+
+ [[nodiscard]] arrival_token arrive(ptr
diff _t update = 1);
+ void wait(arrival_token&& arrival) const;
+
+ void arrive_and_wait();
+ void arrive_and_drop();
+
+ private:
+ CompletionFunction completion; // exposition only
+ };
+
+}
+
+*/
+
+#include <__config>
+#include <atomic>
+#ifndef _LIBCPP_HAS_NO_TREE_BARRIER
+# include <memory>
+#endif
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#pragma GCC system_header
+#endif
+
+#ifdef _LIBCPP_HAS_NO_THREADS
+# error <barrier> is not supported on this single threaded system
+#endif
+
+#if _LIBCPP_STD_VER < 14
+# error <barrier> requires C++14 or later
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+struct __empty_completion
+{
+ inline _LIBCPP_INLINE_VISIBILITY
+ void operator()() noexcept
+ {
+ }
+};
+
+#ifndef _LIBCPP_HAS_NO_TREE_BARRIER
+
+/*
+
+The default implementation of __barrier_base is a classic tree barrier.
+
+It looks
diff erent from literature pseudocode for two main reasons:
+ 1. Threads that call into std::barrier functions do not provide indices,
+ so a numbering step is added before the actual barrier algorithm,
+ appearing as an N+1 round to the N rounds of the tree barrier.
+ 2. A great deal of attention has been paid to avoid cache line thrashing
+ by flattening the tree structure into cache-line sized arrays, that
+ are indexed in an efficient way.
+
+*/
+
+using __barrier_phase_t = uint8_t;
+
+class __barrier_algorithm_base;
+
+_LIBCPP_EXPORTED_FROM_ABI
+__barrier_algorithm_base* __construct_barrier_algorithm_base(ptr
diff _t& __expected);
+
+_LIBCPP_EXPORTED_FROM_ABI
+bool __arrive_barrier_algorithm_base(__barrier_algorithm_base* __barrier,
+ __barrier_phase_t __old_phase);
+
+_LIBCPP_EXPORTED_FROM_ABI
+void __destroy_barrier_algorithm_base(__barrier_algorithm_base* __barrier);
+
+template<class _CompletionF>
+class __barrier_base {
+
+ ptr
diff _t __expected;
+ unique_ptr<__barrier_algorithm_base,
+ decltype(&__destroy_barrier_algorithm_base)> __base;
+ __atomic_base<ptr
diff _t> __expected_adjustment;
+ _CompletionF __completion;
+ __atomic_base<__barrier_phase_t> __phase;
+
+public:
+ using arrival_token = __barrier_phase_t;
+
+ static constexpr ptr
diff _t max() noexcept {
+ return numeric_limits<ptr
diff _t>::max();
+ }
+
+ _LIBCPP_INLINE_VISIBILITY
+ __barrier_base(ptr
diff _t __expected, _CompletionF __completion = _CompletionF())
+ : __expected(__expected), __base(__construct_barrier_algorithm_base(this->__expected),
+ &__destroy_barrier_algorithm_base),
+ __expected_adjustment(0), __completion(move(__completion)), __phase(0)
+ {
+ }
+ [[nodiscard]] _LIBCPP_INLINE_VISIBILITY
+ arrival_token arrive(ptr
diff _t update)
+ {
+ auto const __old_phase = __phase.load(memory_order_relaxed);
+ for(; update; --update)
+ if(__arrive_barrier_algorithm_base(__base.get(), __old_phase)) {
+ __completion();
+ __expected += __expected_adjustment.load(memory_order_relaxed);
+ __expected_adjustment.store(0, memory_order_relaxed);
+ __phase.store(__old_phase + 2, memory_order_release);
+ __phase.notify_all();
+ }
+ return __old_phase;
+ }
+ _LIBCPP_INLINE_VISIBILITY
+ void wait(arrival_token&& __old_phase) const
+ {
+ auto const __test_fn = [=]() -> bool {
+ return __phase.load(memory_order_acquire) != __old_phase;
+ };
+ __libcpp_thread_poll_with_backoff(__test_fn, __libcpp_timed_backoff_policy());
+ }
+ _LIBCPP_INLINE_VISIBILITY
+ void arrive_and_drop()
+ {
+ __expected_adjustment.fetch_sub(1, memory_order_relaxed);
+ (void)arrive(1);
+ }
+};
+
+#else
+
+/*
+
+The alternative implementation of __barrier_base is a central barrier.
+
+Two versions of this algorithm are provided:
+ 1. A fairly straightforward implementation of the litterature for the
+ general case where the completion function is not empty.
+ 2. An optimized implementation that exploits 2's complement arithmetic
+ and well-defined overflow in atomic arithmetic, to handle the phase
+ roll-over for free.
+
+*/
+
+template<class _CompletionF>
+class __barrier_base {
+
+ __atomic_base<ptr
diff _t> __expected;
+ __atomic_base<ptr
diff _t> __arrived;
+ _CompletionF __completion;
+ __atomic_base<bool> __phase;
+public:
+ using arrival_token = bool;
+
+ static constexpr ptr
diff _t max() noexcept {
+ return numeric_limits<ptr
diff _t>::max();
+ }
+
+ _LIBCPP_INLINE_VISIBILITY
+ __barrier_base(ptr
diff _t __expected, _CompletionF __completion = _CompletionF())
+ : __expected(__expected), __arrived(__expected), __completion(move(__completion)), __phase(false)
+ {
+ }
+ [[nodiscard]] _LIBCPP_INLINE_VISIBILITY
+ arrival_token arrive(ptr
diff _t update)
+ {
+ auto const __old_phase = __phase.load(memory_order_relaxed);
+ auto const __result = __arrived.fetch_sub(update, memory_order_acq_rel) - update;
+ auto const new_expected = __expected.load(memory_order_relaxed);
+ if(0 == __result) {
+ __completion();
+ __arrived.store(new_expected, memory_order_relaxed);
+ __phase.store(!__old_phase, memory_order_release);
+ __phase.notify_all();
+ }
+ return __old_phase;
+ }
+ _LIBCPP_INLINE_VISIBILITY
+ void wait(arrival_token&& __old_phase) const
+ {
+ __phase.wait(__old_phase, memory_order_acquire);
+ }
+ _LIBCPP_INLINE_VISIBILITY
+ void arrive_and_drop()
+ {
+ __expected.fetch_sub(1, memory_order_relaxed);
+ (void)arrive(1);
+ }
+};
+
+template<>
+class __barrier_base<__empty_completion> {
+
+ static constexpr uint64_t __expected_unit = 1ull;
+ static constexpr uint64_t __arrived_unit = 1ull << 32;
+ static constexpr uint64_t __expected_mask = __arrived_unit - 1;
+ static constexpr uint64_t __phase_bit = 1ull << 63;
+ static constexpr uint64_t __arrived_mask = (__phase_bit - 1) & ~__expected_mask;
+
+ __atomic_base<uint64_t> __phase_arrived_expected;
+
+ static _LIBCPP_INLINE_VISIBILITY
+ constexpr uint64_t __init(ptr
diff _t __count) _NOEXCEPT
+ {
+ return ((uint64_t(1u << 31) - __count) << 32)
+ | (uint64_t(1u << 31) - __count);
+ }
+
+public:
+ using arrival_token = uint64_t;
+
+ static constexpr ptr
diff _t max() noexcept {
+ return ptr
diff _t(1u << 31) - 1;
+ }
+
+ _LIBCPP_INLINE_VISIBILITY
+ explicit inline __barrier_base(ptr
diff _t __count, __empty_completion = __empty_completion())
+ : __phase_arrived_expected(__init(__count))
+ {
+ }
+ [[nodiscard]] inline _LIBCPP_INLINE_VISIBILITY
+ arrival_token arrive(ptr
diff _t update)
+ {
+ auto const __inc = __arrived_unit * update;
+ auto const __old = __phase_arrived_expected.fetch_add(__inc, memory_order_acq_rel);
+ if((__old ^ (__old + __inc)) & __phase_bit) {
+ __phase_arrived_expected.fetch_add((__old & __expected_mask) << 32, memory_order_relaxed);
+ __phase_arrived_expected.notify_all();
+ }
+ return __old & __phase_bit;
+ }
+ inline _LIBCPP_INLINE_VISIBILITY
+ void wait(arrival_token&& __phase) const
+ {
+ auto const __test_fn = [=]() -> bool {
+ uint64_t const __current = __phase_arrived_expected.load(memory_order_acquire);
+ return ((__current & __phase_bit) != __phase);
+ };
+ __libcpp_thread_poll_with_backoff(__test_fn, __libcpp_timed_backoff_policy());
+ }
+ inline _LIBCPP_INLINE_VISIBILITY
+ void arrive_and_drop()
+ {
+ __phase_arrived_expected.fetch_add(__expected_unit, memory_order_relaxed);
+ (void)arrive(1);
+ }
+};
+
+#endif //_LIBCPP_HAS_NO_TREE_BARRIER
+
+template<class _CompletionF = __empty_completion>
+class barrier {
+
+ __barrier_base<_CompletionF> __b;
+public:
+ using arrival_token = typename __barrier_base<_CompletionF>::arrival_token;
+
+ static constexpr ptr
diff _t max() noexcept {
+ return __barrier_base<_CompletionF>::max();
+ }
+
+ _LIBCPP_INLINE_VISIBILITY
+ barrier(ptr
diff _t __count, _CompletionF __completion = _CompletionF())
+ : __b(__count, std::move(__completion)) {
+ }
+
+ barrier(barrier const&) = delete;
+ barrier& operator=(barrier const&) = delete;
+
+ [[nodiscard]] _LIBCPP_INLINE_VISIBILITY
+ arrival_token arrive(ptr
diff _t update = 1)
+ {
+ return __b.arrive(update);
+ }
+ _LIBCPP_INLINE_VISIBILITY
+ void wait(arrival_token&& __phase) const
+ {
+ __b.wait(std::move(__phase));
+ }
+ _LIBCPP_INLINE_VISIBILITY
+ void arrive_and_wait()
+ {
+ wait(arrive());
+ }
+ _LIBCPP_INLINE_VISIBILITY
+ void arrive_and_drop()
+ {
+ __b.arrive_and_drop();
+ }
+};
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif //_LIBCPP_BARRIER
diff --git a/libcxx/include/latch b/libcxx/include/latch
new file mode 100644
index 000000000000..7c8b2f52205b
--- /dev/null
+++ b/libcxx/include/latch
@@ -0,0 +1,104 @@
+// -*- C++ -*-
+//===--------------------------- latch -----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_LATCH
+#define _LIBCPP_LATCH
+
+/*
+ latch synopsis
+
+namespace std
+{
+
+ class latch
+ {
+ public:
+ constexpr explicit latch(ptr
diff _t __expected);
+ ~latch();
+
+ latch(const latch&) = delete;
+ latch& operator=(const latch&) = delete;
+
+ void count_down(ptr
diff _t __update = 1);
+ bool try_wait() const noexcept;
+ void wait() const;
+ void arrive_and_wait(ptr
diff _t __update = 1);
+
+ private:
+ ptr
diff _t __counter; // exposition only
+ };
+
+}
+
+*/
+
+#include <__config>
+#include <atomic>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#pragma GCC system_header
+#endif
+
+#ifdef _LIBCPP_HAS_NO_THREADS
+# error <latch> is not supported on this single threaded system
+#endif
+
+#if _LIBCPP_STD_VER < 14
+# error <latch> requires C++14 or later
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+class latch
+{
+ __atomic_base<ptr
diff _t> __a;
+
+public:
+ static constexpr ptr
diff _t max() noexcept {
+ return numeric_limits<ptr
diff _t>::max();
+ }
+
+ inline _LIBCPP_INLINE_VISIBILITY
+ constexpr explicit latch(ptr
diff _t __expected) : __a(__expected) { }
+
+ ~latch() = default;
+ latch(const latch&) = delete;
+ latch& operator=(const latch&) = delete;
+
+ inline _LIBCPP_INLINE_VISIBILITY
+ void count_down(ptr
diff _t __update = 1)
+ {
+ auto const __old = __a.fetch_sub(__update, memory_order_release);
+ if(__old == __update)
+ __a.notify_all();
+ }
+ inline _LIBCPP_INLINE_VISIBILITY
+ bool try_wait() const noexcept
+ {
+ return 0 == __a.load(memory_order_acquire);
+ }
+ inline _LIBCPP_INLINE_VISIBILITY
+ void wait() const
+ {
+ auto const __test_fn = [=]() -> bool {
+ return try_wait();
+ };
+ __cxx_atomic_wait(&__a.__a_, __test_fn);
+ }
+ inline _LIBCPP_INLINE_VISIBILITY
+ void arrive_and_wait(ptr
diff _t __update = 1)
+ {
+ count_down(__update);
+ wait();
+ }
+};
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif //_LIBCPP_LATCH
diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index 31d39ddf7c8a..698cc193de53 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -231,6 +231,10 @@ module std [system] {
header "atomic"
export *
}
+ module barrier {
+ header "barrier"
+ export *
+ }
module bit {
header "bit"
export *
@@ -334,6 +338,10 @@ module std [system] {
header "iterator"
export *
}
+ module latch {
+ header "latch"
+ export *
+ }
module limits {
header "limits"
export *
@@ -400,6 +408,10 @@ module std [system] {
header "scoped_allocator"
export *
}
+ module semaphore {
+ header "semaphore"
+ export *
+ }
module set {
header "set"
export initializer_list
diff --git a/libcxx/include/semaphore b/libcxx/include/semaphore
new file mode 100644
index 000000000000..77e3797f7ac6
--- /dev/null
+++ b/libcxx/include/semaphore
@@ -0,0 +1,233 @@
+// -*- C++ -*-
+//===--------------------------- semaphore --------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_SEMAPHORE
+#define _LIBCPP_SEMAPHORE
+
+/*
+ semaphore synopsis
+
+namespace std {
+
+template<ptr
diff _t least_max_value = implementation-defined>
+class counting_semaphore
+{
+public:
+static constexpr ptr
diff _t max() noexcept;
+
+constexpr explicit counting_semaphore(ptr
diff _t desired);
+~counting_semaphore();
+
+counting_semaphore(const counting_semaphore&) = delete;
+counting_semaphore& operator=(const counting_semaphore&) = delete;
+
+void release(ptr
diff _t update = 1);
+void acquire();
+bool try_acquire() noexcept;
+template<class Rep, class Period>
+ bool try_acquire_for(const chrono::duration<Rep, Period>& rel_time);
+template<class Clock, class Duration>
+ bool try_acquire_until(const chrono::time_point<Clock, Duration>& abs_time);
+
+private:
+ptr
diff _t counter; // exposition only
+};
+
+using binary_semaphore = counting_semaphore<1>;
+
+}
+
+*/
+
+#include <__config>
+#include <__threading_support>
+#include <atomic>
+#include <cassert>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#pragma GCC system_header
+#endif
+
+#ifdef _LIBCPP_HAS_NO_THREADS
+# error <semaphore> is not supported on this single threaded system
+#endif
+
+#if _LIBCPP_STD_VER < 14
+# error <semaphore> is requires C++14 or later
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+/*
+
+__atomic_semaphore_base is the general-case implementation, to be used for
+user-requested least-max values that exceed the OS implementation support
+(incl. when the OS has no support of its own) and for binary semaphores.
+
+It is a typical Dijsktra semaphore algorithm over atomics, wait and notify
+functions. It avoids contention against users' own use of those facilities.
+
+*/
+
+class __atomic_semaphore_base
+{
+ __atomic_base<ptr
diff _t> __a;
+
+public:
+ _LIBCPP_INLINE_VISIBILITY
+ __atomic_semaphore_base(ptr
diff _t __count) : __a(__count)
+ {
+ }
+ _LIBCPP_INLINE_VISIBILITY
+ void release(ptr
diff _t __update = 1)
+ {
+ if(0 < __a.fetch_add(__update, memory_order_release))
+ ;
+ else if(__update > 1)
+ __a.notify_all();
+ else
+ __a.notify_one();
+ }
+ _LIBCPP_INLINE_VISIBILITY
+ void acquire()
+ {
+ auto const __test_fn = [=]() -> bool {
+ auto __old = __a.load(memory_order_relaxed);
+ return (__old != 0) && __a.compare_exchange_strong(__old, __old - 1, memory_order_acquire, memory_order_relaxed);
+ };
+ __cxx_atomic_wait(&__a.__a_, __test_fn);
+ }
+ template <class Rep, class Period>
+ _LIBCPP_INLINE_VISIBILITY
+ bool try_acquire_for(chrono::duration<Rep, Period> const& __rel_time)
+ {
+ auto const __test_fn = [=]() -> bool {
+ auto __old = __a.load(memory_order_acquire);
+ while(1) {
+ if (__old == 0)
+ return false;
+ if(__a.compare_exchange_strong(__old, __old - 1, memory_order_acquire, memory_order_relaxed))
+ return true;
+ }
+ };
+ return __libcpp_thread_poll_with_backoff(__test_fn, __libcpp_timed_backoff_policy(), __rel_time);
+ }
+};
+
+#ifndef _LIBCPP_NO_NATIVE_SEMAPHORES
+
+/*
+
+__platform_semaphore_base a simple wrapper for the OS semaphore type. That
+is, every call is routed to the OS in the most direct manner possible.
+
+*/
+
+class __platform_semaphore_base
+{
+ __libcpp_semaphore_t __semaphore;
+
+public:
+ _LIBCPP_INLINE_VISIBILITY
+ __platform_semaphore_base(ptr
diff _t __count) :
+ __semaphore()
+ {
+ __libcpp_semaphore_init(&__semaphore, __count);
+ }
+ _LIBCPP_INLINE_VISIBILITY
+ ~__platform_semaphore_base() {
+ __libcpp_semaphore_destroy(&__semaphore);
+ }
+ _LIBCPP_INLINE_VISIBILITY
+ void release(ptr
diff _t __update)
+ {
+ for(; __update; --__update)
+ __libcpp_semaphore_post(&__semaphore);
+ }
+ _LIBCPP_INLINE_VISIBILITY
+ void acquire()
+ {
+ __libcpp_semaphore_wait(&__semaphore);
+ }
+ _LIBCPP_INLINE_VISIBILITY
+ bool try_acquire_for(chrono::nanoseconds __rel_time)
+ {
+ return __libcpp_semaphore_wait_timed(&__semaphore, __rel_time);
+ }
+};
+
+template<ptr
diff _t __least_max_value>
+using __semaphore_base =
+ typename conditional<(__least_max_value > 1 && __least_max_value <= _LIBCPP_SEMAPHORE_MAX),
+ __platform_semaphore_base,
+ __atomic_semaphore_base>::type;
+
+#else
+
+template<ptr
diff _t __least_max_value>
+using __semaphore_base =
+ __atomic_semaphore_base;
+
+#endif //_LIBCPP_NO_NATIVE_SEMAPHORES
+
+template<ptr
diff _t __least_max_value = _LIBCPP_SEMAPHORE_MAX>
+class counting_semaphore
+{
+ __semaphore_base<__least_max_value> __semaphore;
+
+public:
+ static constexpr ptr
diff _t max() noexcept {
+ return __least_max_value;
+ }
+
+ _LIBCPP_INLINE_VISIBILITY
+ counting_semaphore(ptr
diff _t __count = 0) : __semaphore(__count) { }
+ ~counting_semaphore() = default;
+
+ counting_semaphore(const counting_semaphore&) = delete;
+ counting_semaphore& operator=(const counting_semaphore&) = delete;
+
+ _LIBCPP_INLINE_VISIBILITY
+ void release(ptr
diff _t __update = 1)
+ {
+ __semaphore.release(__update);
+ }
+ _LIBCPP_INLINE_VISIBILITY
+ void acquire()
+ {
+ __semaphore.acquire();
+ }
+ template<class Rep, class Period>
+ _LIBCPP_INLINE_VISIBILITY
+ bool try_acquire_for(chrono::duration<Rep, Period> const& __rel_time)
+ {
+ return __semaphore.try_acquire_for(chrono::duration_cast<chrono::nanoseconds>(__rel_time));
+ }
+ _LIBCPP_INLINE_VISIBILITY
+ bool try_acquire()
+ {
+ return try_acquire_for(chrono::nanoseconds::zero());
+ }
+ template <class Clock, class Duration>
+ _LIBCPP_INLINE_VISIBILITY
+ bool try_acquire_until(chrono::time_point<Clock, Duration> const& __abs_time)
+ {
+ auto const current = Clock::now();
+ if(current >= __abs_time)
+ return try_acquire();
+ else
+ return try_acquire_for(__abs_time - current);
+ }
+};
+
+using binary_semaphore = counting_semaphore<1>;
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif //_LIBCPP_SEMAPHORE
diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index 120505fe18da..28af7b92430f 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -4,6 +4,8 @@ set(LIBCXX_LIB_CMAKEFILES_DIR "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTOR
set(LIBCXX_SOURCES
algorithm.cpp
any.cpp
+ atomic.cpp
+ barrier.cpp
bind.cpp
charconv.cpp
chrono.cpp
diff --git a/libcxx/src/atomic.cpp b/libcxx/src/atomic.cpp
new file mode 100644
index 000000000000..65d4837bb4a7
--- /dev/null
+++ b/libcxx/src/atomic.cpp
@@ -0,0 +1,189 @@
+//===------------------------- atomic.cpp ---------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <__config>
+#ifndef _LIBCPP_HAS_NO_THREADS
+
+#include <climits>
+#include <atomic>
+#include <functional>
+
+#include <iostream>
+
+#ifdef __linux__
+
+#include <unistd.h>
+#include <linux/futex.h>
+#include <sys/syscall.h>
+
+#else // <- Add other operating systems here
+
+// Baseline needs no new headers
+
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#ifdef __linux__
+
+static void __libcpp_platform_wait_on_address(__cxx_atomic_contention_t const volatile* __ptr,
+ __cxx_contention_t __val)
+{
+ static constexpr timespec __timeout = { 2, 0 };
+ syscall(SYS_futex, __ptr, FUTEX_WAIT_PRIVATE, __val, &__timeout, 0, 0);
+}
+
+static void __libcpp_platform_wake_by_address(__cxx_atomic_contention_t const volatile* __ptr,
+ bool __notify_one)
+{
+ syscall(SYS_futex, __ptr, FUTEX_WAKE_PRIVATE, __notify_one ? 1 : INT_MAX, 0, 0, 0);
+}
+
+#elif defined(__APPLE__) && defined(_LIBCPP_USE_ULOCK)
+
+extern "C" int __ulock_wait(uint32_t operation, void *addr, uint64_t value,
+ uint32_t timeout); /* timeout is specified in microseconds */
+extern "C" int __ulock_wake(uint32_t operation, void *addr, uint64_t wake_value);
+
+#define UL_COMPARE_AND_WAIT 1
+#define ULF_WAKE_ALL 0x00000100
+
+static void __libcpp_platform_wait_on_address(__cxx_atomic_contention_t const volatile* __ptr,
+ __cxx_contention_t __val)
+{
+ __ulock_wait(UL_COMPARE_AND_WAIT,
+ const_cast<__cxx_atomic_contention_t*>(__ptr), __val, 0);
+}
+
+static void __libcpp_platform_wake_by_address(__cxx_atomic_contention_t const volatile* __ptr,
+ bool __notify_one)
+{
+ __ulock_wake(UL_COMPARE_AND_WAIT | (__notify_one ? 0 : ULF_WAKE_ALL),
+ const_cast<__cxx_atomic_contention_t*>(__ptr), 0);
+}
+
+#else // <- Add other operating systems here
+
+// Baseline is just a timed backoff
+
+static void __libcpp_platform_wait_on_address(__cxx_atomic_contention_t const volatile* __ptr,
+ __cxx_contention_t __val)
+{
+ __libcpp_thread_poll_with_backoff([=]() -> bool {
+ return !__cxx_nonatomic_compare_equal(__cxx_atomic_load(__ptr, memory_order_relaxed), __val);
+ }, __libcpp_timed_backoff_policy());
+}
+
+static void __libcpp_platform_wake_by_address(__cxx_atomic_contention_t const volatile*, bool) { }
+
+#endif // __linux__
+
+static constexpr size_t __libcpp_contention_table_size = (1 << 8); /* < there's no magic in this number */
+
+struct alignas(64) /* aim to avoid false sharing */ __libcpp_contention_table_entry
+{
+ __cxx_atomic_contention_t __contention_state;
+ __cxx_atomic_contention_t __platform_state;
+ inline constexpr __libcpp_contention_table_entry() :
+ __contention_state(0), __platform_state(0) { }
+};
+
+static __libcpp_contention_table_entry __libcpp_contention_table[ __libcpp_contention_table_size ];
+
+static hash<void const volatile*> __libcpp_contention_hasher;
+
+static __libcpp_contention_table_entry* __libcpp_contention_state(void const volatile * p)
+{
+ return &__libcpp_contention_table[__libcpp_contention_hasher(p) & (__libcpp_contention_table_size - 1)];
+}
+
+/* Given an atomic to track contention and an atomic to actually wait on, which may be
+ the same atomic, we try to detect contention to avoid spuriously calling the platform. */
+
+static void __libcpp_contention_notify(__cxx_atomic_contention_t volatile* __contention_state,
+ __cxx_atomic_contention_t const volatile* __platform_state,
+ bool __notify_one)
+{
+ if(0 != __cxx_atomic_load(__contention_state, memory_order_seq_cst))
+ // We only call 'wake' if we consumed a contention bit here.
+ __libcpp_platform_wake_by_address(__platform_state, __notify_one);
+}
+static __cxx_contention_t __libcpp_contention_monitor_for_wait(__cxx_atomic_contention_t volatile* __contention_state,
+ __cxx_atomic_contention_t const volatile* __platform_state)
+{
+ // We will monitor this value.
+ return __cxx_atomic_load(__platform_state, memory_order_acquire);
+}
+static void __libcpp_contention_wait(__cxx_atomic_contention_t volatile* __contention_state,
+ __cxx_atomic_contention_t const volatile* __platform_state,
+ __cxx_contention_t __old_value)
+{
+ __cxx_atomic_fetch_add(__contention_state, __cxx_contention_t(1), memory_order_seq_cst);
+ // We sleep as long as the monitored value hasn't changed.
+ __libcpp_platform_wait_on_address(__platform_state, __old_value);
+ __cxx_atomic_fetch_sub(__contention_state, __cxx_contention_t(1), memory_order_release);
+}
+
+/* When the incoming atomic is the wrong size for the platform wait size, need to
+ launder the value sequence through an atomic from our table. */
+
+static void __libcpp_atomic_notify(void const volatile* __location)
+{
+ auto const __entry = __libcpp_contention_state(__location);
+ // The value sequence laundering happens on the next line below.
+ __cxx_atomic_fetch_add(&__entry->__platform_state, __cxx_contention_t(1), memory_order_release);
+ __libcpp_contention_notify(&__entry->__contention_state,
+ &__entry->__platform_state,
+ false /* when laundering, we can't handle notify_one */);
+}
+_LIBCPP_EXPORTED_FROM_ABI
+void __cxx_atomic_notify_one(void const volatile* __location)
+ { __libcpp_atomic_notify(__location); }
+_LIBCPP_EXPORTED_FROM_ABI
+void __cxx_atomic_notify_all(void const volatile* __location)
+ { __libcpp_atomic_notify(__location); }
+_LIBCPP_EXPORTED_FROM_ABI
+__cxx_contention_t __libcpp_atomic_monitor(void const volatile* __location)
+{
+ auto const __entry = __libcpp_contention_state(__location);
+ return __libcpp_contention_monitor_for_wait(&__entry->__contention_state, &__entry->__platform_state);
+}
+_LIBCPP_EXPORTED_FROM_ABI
+void __libcpp_atomic_wait(void const volatile* __location, __cxx_contention_t __old_value)
+{
+ auto const __entry = __libcpp_contention_state(__location);
+ __libcpp_contention_wait(&__entry->__contention_state, &__entry->__platform_state, __old_value);
+}
+
+/* When the incoming atomic happens to be the platform wait size, we still need to use the
+ table for the contention detection, but we can use the atomic directly for the wait. */
+
+_LIBCPP_EXPORTED_FROM_ABI
+void __cxx_atomic_notify_one(__cxx_atomic_contention_t const volatile* __location)
+{
+ __libcpp_contention_notify(&__libcpp_contention_state(__location)->__contention_state, __location, true);
+}
+_LIBCPP_EXPORTED_FROM_ABI
+void __cxx_atomic_notify_all(__cxx_atomic_contention_t const volatile* __location)
+{
+ __libcpp_contention_notify(&__libcpp_contention_state(__location)->__contention_state, __location, false);
+}
+_LIBCPP_EXPORTED_FROM_ABI
+__cxx_contention_t __libcpp_atomic_monitor(__cxx_atomic_contention_t const volatile* __location)
+{
+ return __libcpp_contention_monitor_for_wait(&__libcpp_contention_state(__location)->__contention_state, __location);
+}
+_LIBCPP_EXPORTED_FROM_ABI
+void __libcpp_atomic_wait(__cxx_atomic_contention_t const volatile* __location, __cxx_contention_t __old_value)
+{
+ __libcpp_contention_wait(&__libcpp_contention_state(__location)->__contention_state, __location, __old_value);
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif //_LIBCPP_HAS_NO_THREADS
diff --git a/libcxx/src/barrier.cpp b/libcxx/src/barrier.cpp
new file mode 100644
index 000000000000..4edc2d090556
--- /dev/null
+++ b/libcxx/src/barrier.cpp
@@ -0,0 +1,95 @@
+//===------------------------- barrier.cpp ---------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <__config>
+
+#ifndef _LIBCPP_HAS_NO_THREADS
+
+#include <barrier>
+#include <thread>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if !defined(_LIBCPP_HAS_NO_TREE_BARRIER) && (_LIBCPP_STD_VER > 11)
+
+class __barrier_algorithm_base {
+public:
+ struct alignas(64) /* naturally-align the heap state */ __state_t
+ {
+ struct {
+ __atomic_base<__barrier_phase_t> __phase = ATOMIC_VAR_INIT(0);
+ } __tickets[64];
+ };
+
+ ptr
diff _t& __expected;
+ unique_ptr<__state_t[]> __state;
+
+ _LIBCPP_HIDDEN
+ __barrier_algorithm_base(ptr
diff _t& __expected)
+ : __expected(__expected), __state(new __barrier_algorithm_base::__state_t[(__expected + 1) >> 1])
+ {
+ }
+ _LIBCPP_HIDDEN
+ bool __arrive(__barrier_phase_t __old_phase)
+ {
+ __barrier_phase_t const __half_step = __old_phase + 1,
+ __full_step = __old_phase + 2;
+ size_t __current_expected = __expected,
+ __current = hash<thread::id>()(this_thread::get_id()) % ((__expected + 1) >> 1);
+ for(int __round = 0;; ++__round) {
+ if(__current_expected <= 1)
+ return true;
+ size_t const __end_node = ((__current_expected + 1) >> 1),
+ __last_node = __end_node - 1;
+ for(;;++__current) {
+ if(__current == __end_node)
+ __current = 0;
+ __barrier_phase_t expect = __old_phase;
+ if(__current == __last_node && (__current_expected & 1))
+ {
+ if(__state[__current].__tickets[__round].__phase.compare_exchange_strong(expect, __full_step, memory_order_acq_rel))
+ break; // I'm 1 in 1, go to next __round
+ }
+ else if(__state[__current].__tickets[__round].__phase.compare_exchange_strong(expect, __half_step, memory_order_acq_rel))
+ {
+ return false; // I'm 1 in 2, done with arrival
+ }
+ else if(expect == __half_step)
+ {
+ if(__state[__current].__tickets[__round].__phase.compare_exchange_strong(expect, __full_step, memory_order_acq_rel))
+ break; // I'm 2 in 2, go to next __round
+ }
+ }
+ __current_expected = __last_node + 1;
+ __current >>= 1;
+ }
+ }
+};
+
+_LIBCPP_EXPORTED_FROM_ABI
+__barrier_algorithm_base * __construct_barrier_algorithm_base(ptr
diff _t& __expected)
+{
+ return new __barrier_algorithm_base(__expected);
+}
+_LIBCPP_EXPORTED_FROM_ABI
+bool __arrive_barrier_algorithm_base(__barrier_algorithm_base* __barrier,
+ __barrier_phase_t __old_phase)
+{
+ return __barrier->__arrive(__old_phase);
+}
+_LIBCPP_EXPORTED_FROM_ABI
+void __destroy_barrier_algorithm_base(__barrier_algorithm_base* __barrier)
+{
+ delete __barrier;
+}
+
+#endif //!defined(_LIBCPP_HAS_NO_TREE_BARRIER) && (_LIBCPP_STD_VER >= 11)
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif //_LIBCPP_HAS_NO_THREADS
diff --git a/libcxx/src/include/apple_availability.h b/libcxx/src/include/apple_availability.h
index 0091138170fd..f0a5800e8f3a 100644
--- a/libcxx/src/include/apple_availability.h
+++ b/libcxx/src/include/apple_availability.h
@@ -46,6 +46,24 @@
#endif
#endif // __ENVIRONMENT_.*_VERSION_MIN_REQUIRED__
+#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__)
+#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101500
+#define _LIBCPP_USE_ULOCK
+#endif
+#elif defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__)
+#if __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ >= 130000
+#define _LIBCPP_USE_ULOCK
+#endif
+#elif defined(__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__)
+#if __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ >= 130000
+#define _LIBCPP_USE_ULOCK
+#endif
+#elif defined(__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__)
+#if __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ >= 60000
+#define _LIBCPP_USE_ULOCK
+#endif
+#endif // __ENVIRONMENT_.*_VERSION_MIN_REQUIRED__
+
#endif // __APPLE__
#endif // _LIBCPP_SRC_INCLUDE_APPLE_AVAILABILITY_H
diff --git a/libcxx/test/libcxx/double_include.sh.cpp b/libcxx/test/libcxx/double_include.sh.cpp
index 5a1ee32f93d7..42f6376142f2 100644
--- a/libcxx/test/libcxx/double_include.sh.cpp
+++ b/libcxx/test/libcxx/double_include.sh.cpp
@@ -25,6 +25,9 @@
#include <array>
#ifndef _LIBCPP_HAS_NO_THREADS
#include <atomic>
+#include <latch>
+#include <barrier>
+#include <semaphore>
#endif
#include <bit>
#include <bitset>
diff --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.wait/atomic_wait.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.wait/atomic_wait.pass.cpp
new file mode 100644
index 000000000000..8bd262a384af
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.wait/atomic_wait.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: libcpp-has-no-threads
+// XFAIL: c++98, c++03
+
+// <atomic>
+
+#include <atomic>
+#include <type_traits>
+#include <cassert>
+#include <thread>
+
+#include "test_macros.h"
+#include "../atomics.types.operations.req/atomic_helpers.h"
+
+template <class T>
+struct TestFn {
+ void operator()() const {
+ typedef std::atomic<T> A;
+
+ A t;
+ std::atomic_init(&t, T(1));
+ assert(std::atomic_load(&t) == T(1));
+ std::atomic_wait(&t, T(0));
+ std::thread t_([&](){
+ std::atomic_store(&t, T(3));
+ std::atomic_notify_one(&t);
+ });
+ std::atomic_wait(&t, T(1));
+ t_.join();
+
+ volatile A vt;
+ std::atomic_init(&vt, T(2));
+ assert(std::atomic_load(&vt) == T(2));
+ std::atomic_wait(&vt, T(1));
+ std::thread t2_([&](){
+ std::atomic_store(&vt, T(4));
+ std::atomic_notify_one(&vt);
+ });
+ std::atomic_wait(&vt, T(2));
+ t2_.join();
+ }
+};
+
+int main(int, char**)
+{
+ TestEachAtomicType<TestFn>()();
+
+ return 0;
+}
diff --git a/libcxx/test/std/atomics/types.pass.cpp b/libcxx/test/std/atomics/types.pass.cpp
new file mode 100644
index 000000000000..4c225a58d0ba
--- /dev/null
+++ b/libcxx/test/std/atomics/types.pass.cpp
@@ -0,0 +1,125 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// <atomic>
+
+// Test nested types
+
+// template <class T>
+// class atomic
+// {
+// public:
+// typedef T value_type;
+// };
+
+#include <atomic>
+#include <type_traits>
+
+#include <thread>
+#include <chrono>
+#if TEST_STD_VER >= 20
+# include <memory>
+#endif
+
+#include "test_macros.h"
+
+template <class A>
+void
+test_atomic()
+{
+ A a;
+#if TEST_STD_VER >= 17
+ static_assert((std::is_same<typename A::value_type, decltype(a.load())>::value), "");
+#endif
+}
+
+template <class T>
+void
+test()
+{
+ using A = std::atomic<T>;
+#if TEST_STD_VER >= 17
+ static_assert((std::is_same<typename A::value_type, T>::value), "");
+#endif
+ test_atomic<A>();
+}
+
+struct TriviallyCopyable {
+ int i_;
+};
+
+int main(int, char**)
+{
+ test<bool> ();
+ test<char> ();
+ test<signed char> ();
+ test<unsigned char> ();
+ test<short> ();
+ test<unsigned short> ();
+ test<int> ();
+ test<unsigned int> ();
+ test<long> ();
+ test<unsigned long> ();
+ test<long long> ();
+ test<unsigned long long> ();
+ test<char16_t> ();
+ test<char32_t> ();
+ test<wchar_t> ();
+
+ test<int_least8_t> ();
+ test<uint_least8_t> ();
+ test<int_least16_t> ();
+ test<uint_least16_t> ();
+ test<int_least32_t> ();
+ test<uint_least32_t> ();
+ test<int_least64_t> ();
+ test<uint_least64_t> ();
+
+ test<int_fast8_t> ();
+ test<uint_fast8_t> ();
+ test<int_fast16_t> ();
+ test<uint_fast16_t> ();
+ test<int_fast32_t> ();
+ test<uint_fast32_t> ();
+ test<int_fast64_t> ();
+ test<uint_fast64_t> ();
+
+ test< int8_t> ();
+ test<uint8_t> ();
+ test< int16_t> ();
+ test<uint16_t> ();
+ test< int32_t> ();
+ test<uint32_t> ();
+ test< int64_t> ();
+ test<uint64_t> ();
+
+ test<intptr_t> ();
+ test<uintptr_t> ();
+ test<size_t> ();
+ test<ptr
diff _t> ();
+ test<intmax_t> ();
+ test<uintmax_t> ();
+
+ test<uintmax_t> ();
+ test<uintmax_t> ();
+
+ test<TriviallyCopyable>();
+ test<std::thread::id>();
+ test<std::chrono::nanoseconds>();
+ test<float>();
+
+#if TEST_STD_VER >= 20
+ test_atomic<std::atomic_signed_lock_free>();
+ test_atomic<std::atomic_unsigned_lock_free>();
+/*
+ test<std::shared_ptr<int>>();
+*/
+#endif
+
+ return 0;
+}
diff --git a/libcxx/test/std/thread/thread.barrier/arrive.pass.cpp b/libcxx/test/std/thread/thread.barrier/arrive.pass.cpp
new file mode 100644
index 000000000000..943fc84306ae
--- /dev/null
+++ b/libcxx/test/std/thread/thread.barrier/arrive.pass.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: libcpp-has-no-threads
+// UNSUPPORTED: c++98, c++03, c++11
+
+// <barrier>
+
+#include <barrier>
+#include <thread>
+
+#include "test_macros.h"
+
+int main(int, char**)
+{
+ std::barrier<> b(2);
+
+ auto tok = b.arrive();
+ std::thread t([&](){
+ (void)b.arrive();
+ });
+ b.wait(std::move(tok));
+ t.join();
+
+ auto tok2 = b.arrive(2);
+ b.wait(std::move(tok2));
+ return 0;
+}
diff --git a/libcxx/test/std/thread/thread.barrier/arrive_and_drop.pass.cpp b/libcxx/test/std/thread/thread.barrier/arrive_and_drop.pass.cpp
new file mode 100644
index 000000000000..8724a1dbe207
--- /dev/null
+++ b/libcxx/test/std/thread/thread.barrier/arrive_and_drop.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: libcpp-has-no-threads
+// UNSUPPORTED: c++98, c++03, c++11
+
+// <barrier>
+
+#include <barrier>
+#include <thread>
+#include <cassert>
+
+#include "test_macros.h"
+
+int main(int, char**)
+{
+ std::barrier<> b(2);
+
+ std::thread t([&](){
+ b.arrive_and_drop();
+ });
+
+ b.arrive_and_wait();
+ b.arrive_and_wait();
+ t.join();
+ return 0;
+}
diff --git a/libcxx/test/std/thread/thread.barrier/arrive_and_wait.pass.cpp b/libcxx/test/std/thread/thread.barrier/arrive_and_wait.pass.cpp
new file mode 100644
index 000000000000..8596e8a1b6f2
--- /dev/null
+++ b/libcxx/test/std/thread/thread.barrier/arrive_and_wait.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: libcpp-has-no-threads
+// UNSUPPORTED: c++98, c++03, c++11
+
+// <barrier>
+
+#include <barrier>
+#include <thread>
+
+#include "test_macros.h"
+
+int main(int, char**)
+{
+ std::barrier<> b(2);
+
+ std::thread t([&](){
+ for(int i = 0; i < 10; ++i)
+ b.arrive_and_wait();
+ });
+ for(int i = 0; i < 10; ++i)
+ b.arrive_and_wait();
+ t.join();
+
+ return 0;
+}
diff --git a/libcxx/test/std/thread/thread.barrier/completion.pass.cpp b/libcxx/test/std/thread/thread.barrier/completion.pass.cpp
new file mode 100644
index 000000000000..67ae526f4881
--- /dev/null
+++ b/libcxx/test/std/thread/thread.barrier/completion.pass.cpp
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: libcpp-has-no-threads
+// UNSUPPORTED: c++98, c++03, c++11
+
+// <barrier>
+
+#include <barrier>
+#include <thread>
+#include <cassert>
+
+#include "test_macros.h"
+
+int main(int, char**)
+{
+ int x = 0;
+ auto comp = [&]() { x += 1; };
+ std::barrier<decltype(comp)> b(2, comp);
+
+ std::thread t([&](){
+ for(int i = 0; i < 10; ++i)
+ b.arrive_and_wait();
+ });
+
+ for(int i = 0; i < 10; ++i)
+ b.arrive_and_wait();
+
+ assert(x == 10);
+ t.join();
+ return 0;
+}
diff --git a/libcxx/test/std/thread/thread.barrier/max.pass.cpp b/libcxx/test/std/thread/thread.barrier/max.pass.cpp
new file mode 100644
index 000000000000..5e64b0154846
--- /dev/null
+++ b/libcxx/test/std/thread/thread.barrier/max.pass.cpp
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: libcpp-has-no-threads
+// UNSUPPORTED: c++98, c++03, c++11
+
+// <barrier>
+
+#include <barrier>
+#include <thread>
+#include <cassert>
+
+#include "test_macros.h"
+
+int main(int, char**)
+{
+ static_assert(std::barrier<>::max() > 0, "");
+ auto l = [](){};
+ static_assert(std::barrier<decltype(l)>::max() > 0, "");
+ return 0;
+}
diff --git a/libcxx/test/std/thread/thread.barrier/version.pass.cpp b/libcxx/test/std/thread/thread.barrier/version.pass.cpp
new file mode 100644
index 000000000000..544f2d1c88dd
--- /dev/null
+++ b/libcxx/test/std/thread/thread.barrier/version.pass.cpp
@@ -0,0 +1,25 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: libcpp-has-no-threads
+// UNSUPPORTED: c++98, c++03, c++11
+
+// <barrier>
+
+#include <barrier>
+
+#include "test_macros.h"
+
+#ifndef _LIBCPP_VERSION
+#error _LIBCPP_VERSION not defined
+#endif
+
+int main(int, char**)
+{
+ return 0;
+}
diff --git a/libcxx/test/std/thread/thread.latch/arrive_and_wait.pass.cpp b/libcxx/test/std/thread/thread.latch/arrive_and_wait.pass.cpp
new file mode 100644
index 000000000000..156e913e2562
--- /dev/null
+++ b/libcxx/test/std/thread/thread.latch/arrive_and_wait.pass.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: libcpp-has-no-threads
+// UNSUPPORTED: c++98, c++03, c++11
+
+// <latch>
+
+#include <latch>
+#include <thread>
+
+#include "test_macros.h"
+
+int main(int, char**)
+{
+ std::latch l(2);
+
+ std::thread t([&](){
+ l.arrive_and_wait();
+ });
+ l.arrive_and_wait();
+ t.join();
+
+ return 0;
+}
diff --git a/libcxx/test/std/thread/thread.latch/count_down.pass.cpp b/libcxx/test/std/thread/thread.latch/count_down.pass.cpp
new file mode 100644
index 000000000000..81f0605313f0
--- /dev/null
+++ b/libcxx/test/std/thread/thread.latch/count_down.pass.cpp
@@ -0,0 +1,31 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: libcpp-has-no-threads
+// UNSUPPORTED: c++98, c++03, c++11
+
+// <latch>
+
+#include <latch>
+#include <thread>
+
+#include "test_macros.h"
+
+int main(int, char**)
+{
+ std::latch l(2);
+
+ l.count_down();
+ std::thread t([&](){
+ l.count_down();
+ });
+ l.wait();
+ t.join();
+
+ return 0;
+}
diff --git a/libcxx/test/std/thread/thread.latch/max.pass.cpp b/libcxx/test/std/thread/thread.latch/max.pass.cpp
new file mode 100644
index 000000000000..676748f055ec
--- /dev/null
+++ b/libcxx/test/std/thread/thread.latch/max.pass.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: libcpp-has-no-threads
+// UNSUPPORTED: c++98, c++03, c++11
+
+// <latch>
+
+#include <latch>
+#include <cassert>
+
+#include "test_macros.h"
+
+int main(int, char**)
+{
+ static_assert(std::latch::max() > 0, "");
+ return 0;
+}
diff --git a/libcxx/test/std/thread/thread.latch/try_wait.pass.cpp b/libcxx/test/std/thread/thread.latch/try_wait.pass.cpp
new file mode 100644
index 000000000000..7284849b6ede
--- /dev/null
+++ b/libcxx/test/std/thread/thread.latch/try_wait.pass.cpp
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: libcpp-has-no-threads
+// UNSUPPORTED: c++98, c++03, c++11
+
+// <latch>
+
+#include <latch>
+#include <cassert>
+
+#include "test_macros.h"
+
+int main(int, char**)
+{
+ std::latch l(1);
+
+ l.count_down();
+ bool const b = l.try_wait();
+ assert(b);
+
+ return 0;
+}
diff --git a/libcxx/test/std/thread/thread.latch/version.pass.cpp b/libcxx/test/std/thread/thread.latch/version.pass.cpp
new file mode 100644
index 000000000000..8c17dab61af8
--- /dev/null
+++ b/libcxx/test/std/thread/thread.latch/version.pass.cpp
@@ -0,0 +1,25 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: libcpp-has-no-threads
+// UNSUPPORTED: c++98, c++03, c++11
+
+// <latch>
+
+#include <latch>
+
+#include "test_macros.h"
+
+#ifndef _LIBCPP_VERSION
+#error _LIBCPP_VERSION not defined
+#endif
+
+int main(int, char**)
+{
+ return 0;
+}
diff --git a/libcxx/test/std/thread/thread.semaphore/acquire.pass.cpp b/libcxx/test/std/thread/thread.semaphore/acquire.pass.cpp
new file mode 100644
index 000000000000..bb5f7fb31162
--- /dev/null
+++ b/libcxx/test/std/thread/thread.semaphore/acquire.pass.cpp
@@ -0,0 +1,31 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: libcpp-has-no-threads
+// UNSUPPORTED: c++98, c++03, c++11
+
+// <semaphore>
+
+#include <semaphore>
+#include <thread>
+
+#include "test_macros.h"
+
+int main(int, char**)
+{
+ std::counting_semaphore<> s(2);
+
+ std::thread t([&](){
+ s.acquire();
+ });
+ t.join();
+
+ s.acquire();
+
+ return 0;
+}
diff --git a/libcxx/test/std/thread/thread.semaphore/binary.pass.cpp b/libcxx/test/std/thread/thread.semaphore/binary.pass.cpp
new file mode 100644
index 000000000000..d46d6ae47719
--- /dev/null
+++ b/libcxx/test/std/thread/thread.semaphore/binary.pass.cpp
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: libcpp-has-no-threads
+// UNSUPPORTED: c++98, c++03, c++11
+
+// <semaphore>
+
+#include <semaphore>
+#include <chrono>
+#include <thread>
+
+#include "test_macros.h"
+
+int main(int, char**)
+{
+ std::binary_semaphore s(1);
+
+ auto l = [&](){
+ for(int i = 0; i < 1024; ++i) {
+ s.acquire();
+ std::this_thread::sleep_for(std::chrono::microseconds(1));
+ s.release();
+ }
+ };
+
+ std::thread t(l);
+ l();
+
+ t.join();
+
+ return 0;
+}
diff --git a/libcxx/test/std/thread/thread.semaphore/max.pass.cpp b/libcxx/test/std/thread/thread.semaphore/max.pass.cpp
new file mode 100644
index 000000000000..9ed70efcd0dc
--- /dev/null
+++ b/libcxx/test/std/thread/thread.semaphore/max.pass.cpp
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: libcpp-has-no-threads
+// UNSUPPORTED: c++98, c++03, c++11
+
+// <semaphore>
+
+#include <semaphore>
+#include <thread>
+
+#include "test_macros.h"
+
+int main(int, char**)
+{
+ static_assert(std::counting_semaphore<>::max() > 0, "");
+ static_assert(std::counting_semaphore<1>::max() >= 1, "");
+ static_assert(std::counting_semaphore<std::numeric_limits<int>::max()>::max() >= 1, "");
+ static_assert(std::counting_semaphore<std::numeric_limits<unsigned>::max()>::max() >= 1, "");
+ static_assert(std::counting_semaphore<std::numeric_limits<ptr
diff _t>::max()>::max() >= 1, "");
+ static_assert(std::counting_semaphore<1>::max() == std::binary_semaphore::max(), "");
+ return 0;
+}
diff --git a/libcxx/test/std/thread/thread.semaphore/release.pass.cpp b/libcxx/test/std/thread/thread.semaphore/release.pass.cpp
new file mode 100644
index 000000000000..0660feb34de7
--- /dev/null
+++ b/libcxx/test/std/thread/thread.semaphore/release.pass.cpp
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: libcpp-has-no-threads
+// UNSUPPORTED: c++98, c++03, c++11
+
+// <semaphore>
+
+#include <semaphore>
+#include <thread>
+
+#include "test_macros.h"
+
+int main(int, char**)
+{
+ std::counting_semaphore<> s(0);
+
+ s.release();
+ s.acquire();
+
+ std::thread t([&](){
+ s.acquire();
+ });
+ s.release(2);
+ t.join();
+ s.acquire();
+
+ return 0;
+}
diff --git a/libcxx/test/std/thread/thread.semaphore/timed.pass.cpp b/libcxx/test/std/thread/thread.semaphore/timed.pass.cpp
new file mode 100644
index 000000000000..d0bbc91351cc
--- /dev/null
+++ b/libcxx/test/std/thread/thread.semaphore/timed.pass.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: libcpp-has-no-threads
+// UNSUPPORTED: c++98, c++03, c++11
+
+// <semaphore>
+
+#include <semaphore>
+#include <thread>
+#include <chrono>
+
+#include "test_macros.h"
+
+int main(int, char**)
+{
+ auto const start = std::chrono::steady_clock::now();
+
+ std::counting_semaphore<> s(0);
+
+ assert(!s.try_acquire_until(start + std::chrono::milliseconds(250)));
+ assert(!s.try_acquire_for(std::chrono::milliseconds(250)));
+
+ std::thread t([&](){
+ std::this_thread::sleep_for(std::chrono::milliseconds(250));
+ s.release();
+ std::this_thread::sleep_for(std::chrono::milliseconds(250));
+ s.release();
+ });
+
+ assert(s.try_acquire_until(start + std::chrono::seconds(2)));
+ assert(s.try_acquire_for(std::chrono::seconds(2)));
+ t.join();
+
+ auto const end = std::chrono::steady_clock::now();
+ assert(end - start < std::chrono::seconds(10));
+
+ return 0;
+}
diff --git a/libcxx/test/std/thread/thread.semaphore/try_acquire.pass.cpp b/libcxx/test/std/thread/thread.semaphore/try_acquire.pass.cpp
new file mode 100644
index 000000000000..7f0656785927
--- /dev/null
+++ b/libcxx/test/std/thread/thread.semaphore/try_acquire.pass.cpp
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: libcpp-has-no-threads
+// UNSUPPORTED: c++98, c++03, c++11
+
+// <semaphore>
+
+#include <semaphore>
+#include <thread>
+
+#include "test_macros.h"
+
+int main(int, char**)
+{
+ std::counting_semaphore<> s(1);
+
+ assert(s.try_acquire());
+ s.release();
+ assert(s.try_acquire());
+ s.release(2);
+ std::thread t([&](){
+ assert(s.try_acquire());
+ });
+ t.join();
+ assert(s.try_acquire());
+
+ return 0;
+}
diff --git a/libcxx/test/std/thread/thread.semaphore/version.pass.cpp b/libcxx/test/std/thread/thread.semaphore/version.pass.cpp
new file mode 100644
index 000000000000..0a3affb876ac
--- /dev/null
+++ b/libcxx/test/std/thread/thread.semaphore/version.pass.cpp
@@ -0,0 +1,25 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: libcpp-has-no-threads
+// UNSUPPORTED: c++98, c++03, c++11
+
+// <semaphore>
+
+#include <semaphore>
+
+#include "test_macros.h"
+
+#ifndef _LIBCPP_VERSION
+#error _LIBCPP_VERSION not defined
+#endif
+
+int main(int, char**)
+{
+ return 0;
+}
diff --git a/libcxx/www/cxx2a_status.html b/libcxx/www/cxx2a_status.html
index 9422fb93de15..add53d46670b 100644
--- a/libcxx/www/cxx2a_status.html
+++ b/libcxx/www/cxx2a_status.html
@@ -169,7 +169,7 @@ <h3>Paper Status</h3>
<tr><td><a href="https://wg21.link/P1004">P1004</a></td><td>LWG</td><td>Making std::vector constexpr</td><td>Cologne</td><td></td><td></td></tr>
<tr><td><a href="https://wg21.link/P1035">P1035</a></td><td>LWG</td><td>Input Range Adaptors</td><td>Cologne</td><td></td><td></td></tr>
<tr><td><a href="https://wg21.link/P1065">P1065</a></td><td>LWG</td><td>Constexpr INVOKE</td><td>Cologne</td><td></td><td></td></tr>
- <tr><td><a href="https://wg21.link/P1135">P1135</a></td><td>LWG</td><td>The C++20 Synchronization Library</td><td>Cologne</td><td></td><td></td></tr>
+ <tr><td><a href="https://wg21.link/P1135">P1135</a></td><td>LWG</td><td>The C++20 Synchronization Library</td><td>Cologne</td><td>Complete</td><td></td></tr>
<tr><td><a href="https://wg21.link/P1207">P1207</a></td><td>LWG</td><td>Movability of Single-pass Iterators</td><td>Cologne</td><td></td><td></td></tr>
<tr><td><a href="https://wg21.link/P1208">P1208</a></td><td>LWG</td><td>Adopt source_location for C++20</td><td>Cologne</td><td></td><td></td></tr>
<tr><td><a href="https://wg21.link/P1355">P1355</a></td><td>LWG</td><td>Exposing a narrow contract for ceil2</td><td>Cologne</td><td>Complete</td><td>9.0</td></tr>
More information about the libcxx-commits
mailing list