diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -25,6 +25,7 @@ any array atomic + barrier bit bitset cassert @@ -103,6 +104,7 @@ iostream istream iterator + latch limits limits.h list @@ -122,6 +124,7 @@ ratio regex scoped_allocator + semaphore set setjmp.h shared_mutex diff --git a/libcxx/include/__threading_support b/libcxx/include/__threading_support --- a/libcxx/include/__threading_support +++ b/libcxx/include/__threading_support @@ -26,6 +26,10 @@ #if defined(_LIBCPP_HAS_THREAD_API_PTHREAD) # include # include +# include +# ifdef __APPLE__ +# define _LIBCPP_NO_NATIVE_SEMAPHORES +# endif #elif defined(_LIBCPP_HAS_THREAD_API_C11) # include #endif @@ -65,6 +69,10 @@ 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_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 @@ _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 @@ _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 +_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 @@ 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 @@ 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 +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 --- a/libcxx/include/atomic +++ b/libcxx/include/atomic @@ -547,8 +547,10 @@ */ #include <__config> +#include <__threading_support> #include #include +#include #include #include @@ -629,6 +631,11 @@ #endif // _LIBCPP_STD_VER > 17 +template _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::type, __memory_order_underlying_t>::value), "unexpected underlying type for std::memory_order"); @@ -1218,9 +1225,9 @@ 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 @@ 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 @@ : _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 +_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 +_LIBCPP_INLINE_VISIBILITY void __cxx_atomic_notify_all(__cxx_atomic_impl<_Tp> const volatile*) { } +template +_LIBCPP_INLINE_VISIBILITY void __cxx_atomic_notify_one(__cxx_atomic_impl<_Tp> const volatile*) { } +template +_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 +_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 template ::value && !is_same<_Tp, bool>::value> @@ -1532,6 +1606,19 @@ 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 @@ __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 @@ : 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 @@ : 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 @@ return __o->compare_exchange_strong(*__e, __d, __s, __f); } +// atomic_wait + +template +_LIBCPP_INLINE_VISIBILITY +void atomic_wait(const volatile atomic<_Tp>* __o, + typename atomic<_Tp>::value_type __v) _NOEXCEPT +{ + return __o->wait(__v); +} + +template +_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 +_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 +_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 +_LIBCPP_INLINE_VISIBILITY +void atomic_notify_one(volatile atomic<_Tp>* __o) _NOEXCEPT +{ + __o->notify_one(); +} +template +_LIBCPP_INLINE_VISIBILITY +void atomic_notify_one(atomic<_Tp>* __o) _NOEXCEPT +{ + __o->notify_one(); +} + +// atomic_notify_one + +template +_LIBCPP_INLINE_VISIBILITY +void atomic_notify_all(volatile atomic<_Tp>* __o) _NOEXCEPT +{ + __o->notify_all(); +} +template +_LIBCPP_INLINE_VISIBILITY +void atomic_notify_all(atomic<_Tp>* __o) _NOEXCEPT +{ + __o->notify_all(); +} + // atomic_fetch_add template @@ -2280,6 +2442,13 @@ __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);} _LIBCPP_INLINE_VISIBILITY @@ -2293,6 +2462,25 @@ {__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 _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR @@ -2304,12 +2492,44 @@ 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 @@ __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 atomic_intmax_t; typedef atomic 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 --- /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 barrier + { + public: + using arrival_token = see below; + + constexpr explicit barrier(ptrdiff_t phase_count, + CompletionFunction f = CompletionFunction()); + ~barrier(); + + barrier(const barrier&) = delete; + barrier& operator=(const barrier&) = delete; + + [[nodiscard]] arrival_token arrive(ptrdiff_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 +#ifndef _LIBCPP_HAS_NO_TREE_BARRIER +# include +#endif + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +#ifdef _LIBCPP_HAS_NO_THREADS +# error is not supported on this single threaded system +#endif + +#if _LIBCPP_STD_VER < 14 +# error 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 different 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(ptrdiff_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 __barrier_base { + + ptrdiff_t __expected; + unique_ptr<__barrier_algorithm_base, + decltype(&__destroy_barrier_algorithm_base)> __base; + __atomic_base __expected_adjustment; + _CompletionF __completion; + __atomic_base<__barrier_phase_t> __phase; + +public: + using arrival_token = __barrier_phase_t; + + static constexpr ptrdiff_t max() noexcept { + return numeric_limits::max(); + } + + _LIBCPP_INLINE_VISIBILITY + __barrier_base(ptrdiff_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(ptrdiff_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 __barrier_base { + + __atomic_base __expected; + __atomic_base __arrived; + _CompletionF __completion; + __atomic_base __phase; +public: + using arrival_token = bool; + + static constexpr ptrdiff_t max() noexcept { + return numeric_limits::max(); + } + + _LIBCPP_INLINE_VISIBILITY + __barrier_base(ptrdiff_t __expected, _CompletionF __completion = _CompletionF()) + : __expected(__expected), __arrived(__expected), __completion(move(__completion)), __phase(false) + { + } + [[nodiscard]] _LIBCPP_INLINE_VISIBILITY + arrival_token arrive(ptrdiff_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 __phase_arrived_expected; + + static _LIBCPP_INLINE_VISIBILITY + constexpr uint64_t __init(ptrdiff_t __count) _NOEXCEPT + { + return ((uint64_t(1u << 31) - __count) << 32) + | (uint64_t(1u << 31) - __count); + } + +public: + using arrival_token = uint64_t; + + static constexpr ptrdiff_t max() noexcept { + return ptrdiff_t(1u << 31) - 1; + } + + _LIBCPP_INLINE_VISIBILITY + explicit inline __barrier_base(ptrdiff_t __count, __empty_completion = __empty_completion()) + : __phase_arrived_expected(__init(__count)) + { + } + [[nodiscard]] inline _LIBCPP_INLINE_VISIBILITY + arrival_token arrive(ptrdiff_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 barrier { + + __barrier_base<_CompletionF> __b; +public: + using arrival_token = typename __barrier_base<_CompletionF>::arrival_token; + + static constexpr ptrdiff_t max() noexcept { + return __barrier_base<_CompletionF>::max(); + } + + _LIBCPP_INLINE_VISIBILITY + barrier(ptrdiff_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(ptrdiff_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 --- /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(ptrdiff_t __expected); + ~latch(); + + latch(const latch&) = delete; + latch& operator=(const latch&) = delete; + + void count_down(ptrdiff_t __update = 1); + bool try_wait() const noexcept; + void wait() const; + void arrive_and_wait(ptrdiff_t __update = 1); + + private: + ptrdiff_t __counter; // exposition only + }; + +} + +*/ + +#include <__config> +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +#ifdef _LIBCPP_HAS_NO_THREADS +# error is not supported on this single threaded system +#endif + +#if _LIBCPP_STD_VER < 14 +# error requires C++14 or later +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +class latch +{ + __atomic_base __a; + +public: + static constexpr ptrdiff_t max() noexcept { + return numeric_limits::max(); + } + + inline _LIBCPP_INLINE_VISIBILITY + constexpr explicit latch(ptrdiff_t __expected) : __a(__expected) { } + + ~latch() = default; + latch(const latch&) = delete; + latch& operator=(const latch&) = delete; + + inline _LIBCPP_INLINE_VISIBILITY + void count_down(ptrdiff_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(ptrdiff_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 --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -231,6 +231,10 @@ header "atomic" export * } + module barrier { + header "barrier" + export * + } module bit { header "bit" export * @@ -334,6 +338,10 @@ header "iterator" export * } + module latch { + header "latch" + export * + } module limits { header "limits" export * @@ -400,6 +408,10 @@ 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 --- /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 +class counting_semaphore +{ +public: +static constexpr ptrdiff_t max() noexcept; + +constexpr explicit counting_semaphore(ptrdiff_t desired); +~counting_semaphore(); + +counting_semaphore(const counting_semaphore&) = delete; +counting_semaphore& operator=(const counting_semaphore&) = delete; + +void release(ptrdiff_t update = 1); +void acquire(); +bool try_acquire() noexcept; +template + bool try_acquire_for(const chrono::duration& rel_time); +template + bool try_acquire_until(const chrono::time_point& abs_time); + +private: +ptrdiff_t counter; // exposition only +}; + +using binary_semaphore = counting_semaphore<1>; + +} + +*/ + +#include <__config> +#include <__threading_support> +#include +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +#ifdef _LIBCPP_HAS_NO_THREADS +# error is not supported on this single threaded system +#endif + +#if _LIBCPP_STD_VER < 14 +# error 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 __a; + +public: + _LIBCPP_INLINE_VISIBILITY + __atomic_semaphore_base(ptrdiff_t __count) : __a(__count) + { + } + _LIBCPP_INLINE_VISIBILITY + void release(ptrdiff_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 + _LIBCPP_INLINE_VISIBILITY + bool try_acquire_for(chrono::duration 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(ptrdiff_t __count) : + __semaphore() + { + __libcpp_semaphore_init(&__semaphore, __count); + } + _LIBCPP_INLINE_VISIBILITY + ~__platform_semaphore_base() { + __libcpp_semaphore_destroy(&__semaphore); + } + _LIBCPP_INLINE_VISIBILITY + void release(ptrdiff_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 +using __semaphore_base = + typename conditional<(__least_max_value > 1 && __least_max_value <= _LIBCPP_SEMAPHORE_MAX), + __platform_semaphore_base, + __atomic_semaphore_base>::type; + +#else + +template +using __semaphore_base = + __atomic_semaphore_base; + +#endif //_LIBCPP_NO_NATIVE_SEMAPHORES + +template +class counting_semaphore +{ + __semaphore_base<__least_max_value> __semaphore; + +public: + static constexpr ptrdiff_t max() noexcept { + return __least_max_value; + } + + _LIBCPP_INLINE_VISIBILITY + counting_semaphore(ptrdiff_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(ptrdiff_t __update = 1) + { + __semaphore.release(__update); + } + _LIBCPP_INLINE_VISIBILITY + void acquire() + { + __semaphore.acquire(); + } + template + _LIBCPP_INLINE_VISIBILITY + bool try_acquire_for(chrono::duration const& __rel_time) + { + return __semaphore.try_acquire_for(chrono::duration_cast(__rel_time)); + } + _LIBCPP_INLINE_VISIBILITY + bool try_acquire() + { + return try_acquire_for(chrono::nanoseconds::zero()); + } + template + _LIBCPP_INLINE_VISIBILITY + bool try_acquire_until(chrono::time_point 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 --- a/libcxx/src/CMakeLists.txt +++ b/libcxx/src/CMakeLists.txt @@ -4,6 +4,8 @@ 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 --- /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 +#include +#include + +#include + +#ifdef __linux__ + +#include +#include +#include + +#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 __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 --- /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 +#include + +_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]; + }; + + ptrdiff_t& __expected; + unique_ptr<__state_t[]> __state; + + _LIBCPP_HIDDEN + __barrier_algorithm_base(ptrdiff_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()(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(ptrdiff_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 --- 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 --- a/libcxx/test/libcxx/double_include.sh.cpp +++ b/libcxx/test/libcxx/double_include.sh.cpp @@ -25,6 +25,9 @@ #include #ifndef _LIBCPP_HAS_NO_THREADS #include +#include +#include +#include #endif #include #include 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 --- /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 + +// + +#include +#include +#include +#include + +#include "test_macros.h" +#include "../atomics.types.operations.req/atomic_helpers.h" + +template +struct TestFn { + void operator()() const { + typedef std::atomic 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()(); + + return 0; +} diff --git a/libcxx/test/std/atomics/types.pass.cpp b/libcxx/test/std/atomics/types.pass.cpp new file mode 100644 --- /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 +// +//===----------------------------------------------------------------------===// + +// + +// Test nested types + +// template +// class atomic +// { +// public: +// typedef T value_type; +// }; + +#include +#include + +#include +#include +#if TEST_STD_VER >= 20 +# include +#endif + +#include "test_macros.h" + +template +void +test_atomic() +{ + A a; +#if TEST_STD_VER >= 17 + static_assert((std::is_same::value), ""); +#endif +} + +template +void +test() +{ + using A = std::atomic; +#if TEST_STD_VER >= 17 + static_assert((std::is_same::value), ""); +#endif + test_atomic(); +} + +struct TriviallyCopyable { + int i_; +}; + +int main(int, char**) +{ + test (); + test (); + test (); + test (); + test (); + test (); + test (); + test (); + test (); + test (); + test (); + test (); + test (); + test (); + test (); + + test (); + test (); + test (); + test (); + test (); + test (); + test (); + test (); + + test (); + test (); + test (); + test (); + test (); + test (); + test (); + test (); + + test< int8_t> (); + test (); + test< int16_t> (); + test (); + test< int32_t> (); + test (); + test< int64_t> (); + test (); + + test (); + test (); + test (); + test (); + test (); + test (); + + test (); + test (); + + test(); + test(); + test(); + test(); + +#if TEST_STD_VER >= 20 + test_atomic(); + test_atomic(); +/* + test>(); +*/ +#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 --- /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 + +// + +#include +#include + +#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 --- /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 + +// + +#include +#include +#include + +#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 --- /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 + +// + +#include +#include + +#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 --- /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 + +// + +#include +#include +#include + +#include "test_macros.h" + +int main(int, char**) +{ + int x = 0; + auto comp = [&]() { x += 1; }; + std::barrier 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 --- /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 + +// + +#include +#include +#include + +#include "test_macros.h" + +int main(int, char**) +{ + static_assert(std::barrier<>::max() > 0, ""); + auto l = [](){}; + static_assert(std::barrier::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 --- /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 + +// + +#include + +#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 --- /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 + +// + +#include +#include + +#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 --- /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 + +// + +#include +#include + +#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 --- /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 + +// + +#include +#include + +#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 --- /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 + +// + +#include +#include + +#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 --- /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 + +// + +#include + +#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 --- /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 + +// + +#include +#include + +#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 --- /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 + +// + +#include +#include +#include + +#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 --- /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 + +// + +#include +#include + +#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::max()>::max() >= 1, ""); + static_assert(std::counting_semaphore::max()>::max() >= 1, ""); + static_assert(std::counting_semaphore::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 --- /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 + +// + +#include +#include + +#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 --- /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 + +// + +#include +#include +#include + +#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 --- /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 + +// + +#include +#include + +#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 --- /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 + +// + +#include + +#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 --- a/libcxx/www/cxx2a_status.html +++ b/libcxx/www/cxx2a_status.html @@ -169,7 +169,7 @@ P1004LWGMaking std::vector constexprCologne P1035LWGInput Range AdaptorsCologne P1065LWGConstexpr INVOKECologne - P1135LWGThe C++20 Synchronization LibraryCologne + P1135LWGThe C++20 Synchronization LibraryCologneComplete P1207LWGMovability of Single-pass IteratorsCologne P1208LWGAdopt source_location for C++20Cologne P1355LWGExposing a narrow contract for ceil2CologneComplete9.0