Index: include/__threading_support =================================================================== --- include/__threading_support +++ include/__threading_support @@ -14,6 +14,13 @@ #include #include #include +#include + +#if defined(__linux__) +# include +# include +# include +#endif #ifndef _LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER #pragma GCC system_header @@ -26,6 +33,10 @@ #if defined(_LIBCPP_HAS_THREAD_API_PTHREAD) # include # include +# include +# if defined(__APPLE__) +# include +# endif #endif #if defined(_LIBCPP_HAS_THREAD_LIBRARY_EXTERNAL) || \ @@ -63,6 +74,15 @@ typedef pthread_cond_t __libcpp_condvar_t; #define _LIBCPP_CONDVAR_INITIALIZER PTHREAD_COND_INITIALIZER +// Semaphore +#if defined(__APPLE__) +typedef dispatch_semaphore_t __libcpp_semaphore_t; +# define _LIBCPP_SEMAPHORE_MAX numeric_limits::max() +#else +typedef sem_t __libcpp_semaphore_t; +# define _LIBCPP_SEMAPHORE_MAX SEM_VALUE_MAX +#endif + // Execute once typedef pthread_once_t __libcpp_exec_once_flag; #define _LIBCPP_EXEC_ONCE_INITIALIZER PTHREAD_ONCE_INIT @@ -115,6 +135,10 @@ #endif // !defined(_LIBCPP_HAS_THREAD_API_PTHREAD) && !defined(_LIBCPP_HAS_THREAD_API_EXTERNAL) #if !defined(_LIBCPP_HAS_THREAD_API_EXTERNAL) + +_LIBCPP_THREAD_ABI_VISIBILITY +__libcpp_timespec_t __libcpp_to_timespec(const chrono::nanoseconds& __ns); + // Mutex _LIBCPP_THREAD_ABI_VISIBILITY int __libcpp_recursive_mutex_init(__libcpp_recursive_mutex_t *__m); @@ -160,6 +184,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, @@ -195,9 +235,16 @@ _LIBCPP_THREAD_ABI_VISIBILITY void __libcpp_thread_yield(); +_LIBCPP_THREAD_ABI_VISIBILITY +void __libcpp_thread_yield_processor(); + _LIBCPP_THREAD_ABI_VISIBILITY void __libcpp_thread_sleep_for(const chrono::nanoseconds& __ns); +template +_LIBCPP_THREAD_ABI_VISIBILITY +bool __libcpp_thread_poll_with_backoff(_Fn && __f, chrono::nanoseconds const& __max = chrono::nanoseconds::zero()); + // Thread local storage _LIBCPP_THREAD_ABI_VISIBILITY int __libcpp_tls_create(__libcpp_tls_key* __key, @@ -215,6 +262,27 @@ defined(_LIBCPP_BUILDING_THREAD_LIBRARY_EXTERNAL)) && \ defined(_LIBCPP_HAS_THREAD_API_PTHREAD) +__libcpp_timespec_t __libcpp_to_timespec(const chrono::nanoseconds& __ns) { + + using namespace chrono; + seconds __s = duration_cast(__ns); + __libcpp_timespec_t __ts; + typedef decltype(__ts.tv_sec) ts_sec; + _LIBCPP_CONSTEXPR ts_sec __ts_sec_max = numeric_limits::max(); + + if (__s.count() < __ts_sec_max) + { + __ts.tv_sec = static_cast(__s.count()); + __ts.tv_nsec = static_cast((__ns - __s).count()); + } + else + { + __ts.tv_sec = __ts_sec_max; + __ts.tv_nsec = 999999999; // (10^9 - 1) + } + return __ts; +} + int __libcpp_recursive_mutex_init(__libcpp_recursive_mutex_t *__m) { pthread_mutexattr_t attr; @@ -306,6 +374,66 @@ return pthread_cond_destroy(__cv); } +// Semaphore +#if defined(__APPLE__) + +bool __libcpp_semaphore_init(__libcpp_semaphore_t* __sem, int __init) +{ + return (*__sem = dispatch_semaphore_create(__init)) != NULL; +} + +bool __libcpp_semaphore_destroy(__libcpp_semaphore_t* __sem) +{ + dispatch_release(*__sem); + return true; +} + +bool __libcpp_semaphore_post(__libcpp_semaphore_t* __sem) +{ + dispatch_semaphore_signal(*__sem); + return true; +} + +bool __libcpp_semaphore_wait(__libcpp_semaphore_t* __sem) +{ + return dispatch_semaphore_wait(*__sem, DISPATCH_TIME_FOREVER) == 0; +} + +bool __libcpp_semaphore_wait_timed(__libcpp_semaphore_t* __sem, chrono::nanoseconds const& __ns) +{ + return dispatch_semaphore_wait(*__sem, dispatch_time(DISPATCH_TIME_NOW, __ns.count())) == 0; +} + +#else + +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 __ts = __libcpp_to_timespec(__ns); + return sem_timedwait(__sem, &__ts) == 0; +} + +#endif //__APPLE__ + // Execute once int __libcpp_execute_once(__libcpp_exec_once_flag *flag, void (*init_routine)()) { @@ -361,28 +489,52 @@ sched_yield(); } -void __libcpp_thread_sleep_for(const chrono::nanoseconds& __ns) +void __libcpp_thread_yield_processor() { - using namespace chrono; - seconds __s = duration_cast(__ns); - __libcpp_timespec_t __ts; - typedef decltype(__ts.tv_sec) ts_sec; - _LIBCPP_CONSTEXPR ts_sec __ts_sec_max = numeric_limits::max(); - - if (__s.count() < __ts_sec_max) - { - __ts.tv_sec = static_cast(__s.count()); - __ts.tv_nsec = static_cast((__ns - __s).count()); - } - else - { - __ts.tv_sec = __ts_sec_max; - __ts.tv_nsec = 999999999; // (10^9 - 1) - } +#if defined(__aarch64__) + asm volatile ("yield" :::); +#elif defined(__x86_64__) + asm volatile ("pause" :::); +#elif defined (__powerpc__) + asm volatile ("or 27,27,27":::); +#else + ; +#endif +} +void __libcpp_thread_sleep_for(const chrono::nanoseconds& __ns) +{ + auto __ts = __libcpp_to_timespec(__ns); while (nanosleep(&__ts, &__ts) == -1 && errno == EINTR); } +template +bool __libcpp_thread_poll_with_backoff(_Fn && __f, chrono::nanoseconds const& __max) +{ + auto const __start = chrono::high_resolution_clock::now(); + for(int __count = 0;;) { + if(__builtin_expect(__f(),1)) + return true; + if(__count < 16) { + if(__count > 8) + __libcpp_thread_yield_processor(); + __count += 1; + continue; + } + auto const __elapsed = chrono::high_resolution_clock::now() - __start; + if(__max != chrono::nanoseconds::zero() && + __max < __elapsed) + return false; + auto const __step = __elapsed / 4; + if(__step >= chrono::milliseconds(1)) + __libcpp_thread_sleep_for(chrono::milliseconds(1)); + else if(__step >= chrono::microseconds(10)) + __libcpp_thread_sleep_for(__step); + else + __libcpp_thread_yield(); + } +} + // Thread local storage int __libcpp_tls_create(__libcpp_tls_key *__key, void (*__at_exit)(void *)) { @@ -401,6 +553,54 @@ #endif // !_LIBCPP_HAS_THREAD_LIBRARY_EXTERNAL || _LIBCPP_BUILDING_THREAD_LIBRARY_EXTERNAL +#if defined(__linux__) && !defined(_LIBCPP_HAS_NO_PLATFORM_WAIT) + +#define _LIBCPP_HAS_PLATFORM_WAIT + +using __libcpp_platform_wait_t = int; + +template +struct __libcpp_platform_wait_uses_type { + enum { __value = is_same::type, __libcpp_platform_wait_t>::value }; +}; + +template ::__value, int>::type = 1> +void __libcpp_platform_wait(_Tp const* ptr, _Tp val, void const* timeout) { + syscall(SYS_futex, ptr, FUTEX_WAIT_PRIVATE, val, timeout, 0, 0); +} + +template ::__value, int>::type = 1> +void __libcpp_platform_wake(_Tp const* ptr, bool all) { + syscall(SYS_futex, ptr, FUTEX_WAKE_PRIVATE, all ? INT_MAX : 1, 0, 0, 0); +} + +#endif // __linux__ + +#ifndef _LIBCPP_HAS_NO_THREAD_CONTENTION_TABLE + +struct alignas(64) __libcpp_contention_t { +#if defined(_LIBCPP_HAS_PLATFORM_WAIT) + ptrdiff_t __waiters = 0; + __libcpp_platform_wait_t __version = 0; +#else + ptrdiff_t __credit = 0; + __libcpp_mutex_t __mutex = _LIBCPP_MUTEX_INITIALIZER; + __libcpp_condvar_t __condvar = _LIBCPP_CONDVAR_INITIALIZER; +#endif +}; + +_LIBCPP_FUNC_VIS +__libcpp_contention_t * __libcpp_contention_state(void const volatile * p) _NOEXCEPT; + +#endif // _LIBCPP_HAS_NO_THREAD_CONTENTION_TABLE + +#if !defined(_LIBCPP_HAS_NO_TREE_BARRIER) && !defined(_LIBCPP_HAS_NO_THREAD_FAVORITE_BARRIER_INDEX) + +_LIBCPP_EXPORTED_FROM_ABI +extern thread_local ptrdiff_t __libcpp_thread_favorite_barrier_index; + +#endif + class _LIBCPP_TYPE_VIS thread; class _LIBCPP_TYPE_VIS __thread_id; Index: include/atomic =================================================================== --- include/atomic +++ include/atomic @@ -547,6 +547,7 @@ */ #include <__config> +#include <__threading_support> #include #include #include @@ -629,11 +630,18 @@ #endif // _LIBCPP_STD_VER > 17 +extern "C" int memcmp( const void * ptr1, const void * ptr2, size_t num ); + +template _LIBCPP_INLINE_VISIBILITY +bool __cxx_atomic_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"); #if defined(_LIBCPP_HAS_GCC_ATOMIC_IMP) || \ - defined(_LIBCPP_ATOMIC_ONLY_USE_BUILTINS) + defined(_LIBCPP_ATOMIC_ONLY_USE_BUILTINS) // [atomics.types.generic]p1 guarantees _Tp is trivially copyable. Because // the default operator= in an object is not volatile, a byte-by-byte copy @@ -1079,7 +1087,7 @@ return __c11_atomic_fetch_xor(&__a->__a_value, __pattern, static_cast<__memory_order_underlying_t>(__order)); } -#endif // _LIBCPP_HAS_GCC_ATOMIC_IMP, _LIBCPP_HAS_C_ATOMIC_IMP +#endif // _LIBCPP_HAS_GCC_ATOMIC_IMP, _LIBCPP_HAS_C_ATOMIC_IMP template _LIBCPP_INLINE_VISIBILITY @@ -1307,7 +1315,7 @@ template _LIBCPP_INLINE_VISIBILITY _Tp* __cxx_atomic_fetch_add(__cxx_atomic_lock_impl<_Tp*>* __a, - ptrdiff_t __delta, memory_order) { + ptrdiff_t __delta, memory_order) { __a->__lock(); _Tp* __old = __a->__a_value; __a->__a_value += __delta; @@ -1452,6 +1460,153 @@ : _Base(value) {} }; +template +__cxx_atomic_impl<_Tp>* __cxx_atomic_rebind(_Tp* __inst) { + static_assert(sizeof(__cxx_atomic_impl<_Tp>) == sizeof(_Tp),""); + static_assert(alignof(__cxx_atomic_impl<_Tp>) == alignof(_Tp),""); + return (__cxx_atomic_impl<_Tp>*)__inst; +} + +#ifdef _LIBCPP_HAS_NO_THREAD_CONTENTION_TABLE + +template +_LIBCPP_INLINE_VISIBILITY void __cxx_atomic_try_wait_slow_fallback(__cxx_atomic_impl<_Tp> const volatile* __a, _Tp __val, memory_order __order) { + __libcpp_thread_poll_with_backoff([=] _LIBCPP_INLINE_VISIBILITY () -> bool { + return __cxx_atomic_load(__a, __order) != __val; + }); +} + +#endif + +#ifdef _LIBCPP_HAS_PLATFORM_WAIT + +template ::__value, int>::type = 1> +_LIBCPP_INLINE_VISIBILITY void __cxx_atomic_notify_all(__cxx_atomic_impl<_Tp> const volatile* __a) { +#ifndef _LIBCPP_HAS_NO_THREAD_CONTENTION_TABLE + auto * const __c = __libcpp_contention_state(__a); + __cxx_atomic_fetch_add(__cxx_atomic_rebind(&__c->__version), (__libcpp_platform_wait_t)1, memory_order_relaxed); + __cxx_atomic_thread_fence(memory_order_seq_cst); + if (0 != __cxx_atomic_exchange(__cxx_atomic_rebind(&__c->__waiters), (ptrdiff_t)0, memory_order_relaxed)) + __libcpp_platform_wake(&__c->__version, true); +#endif +} +template ::__value, int>::type = 1> +_LIBCPP_INLINE_VISIBILITY void __cxx_atomic_notify_one(__cxx_atomic_impl<_Tp> const volatile* __a) { + __cxx_atomic_notify_all(__a); +} +template ::__value, int>::type = 1> +_LIBCPP_INLINE_VISIBILITY void __cxx_atomic_try_wait_slow(__cxx_atomic_impl<_Tp> const volatile* __a, _Tp const __val, memory_order __order) { +#ifndef _LIBCPP_HAS_NO_THREAD_CONTENTION_TABLE + auto * const __c = __libcpp_contention_state(__a); + __cxx_atomic_store(__cxx_atomic_rebind(&__c->__waiters), (ptrdiff_t)1, memory_order_relaxed); + __cxx_atomic_thread_fence(memory_order_seq_cst); + auto const __version = __cxx_atomic_load(__cxx_atomic_rebind(&__c->__version), memory_order_relaxed); + if (__builtin_expect(!__cxx_atomic_compare_equal(__cxx_atomic_load(__a, __order), __val), 1)) + return; + if(sizeof(__libcpp_platform_wait_t) < 8) { + constexpr timespec __timeout = { 2, 0 }; // Hedge on rare 'int version' aliasing. + __libcpp_platform_wait(&__c->__version, __version, &__timeout); + } + else + __libcpp_platform_wait(&__c->__version, __version, nullptr); +#else + __cxx_atomic_try_wait_slow_fallback(__a, __val, __order); +#endif // _LIBCPP_HAS_NO_THREAD_CONTENTION_TABLE +} + +template ::__value, int>::type = 1> +_LIBCPP_INLINE_VISIBILITY void __cxx_atomic_try_wait_slow(__cxx_atomic_impl<_Tp> const volatile* __a, _Tp __val, memory_order __order) { +#ifndef _LIBCPP_HAS_NO_THREAD_CONTENTION_TABLE + auto * const __c = __libcpp_contention_state(__a); + __cxx_atomic_fetch_add(__cxx_atomic_rebind(&__c->__waiters), (ptrdiff_t)1, memory_order_relaxed); + __cxx_atomic_thread_fence(memory_order_seq_cst); +#endif + __libcpp_platform_wait((_Tp*)__a, __val, nullptr); +#ifndef _LIBCPP_HAS_NO_THREAD_CONTENTION_TABLE + __cxx_atomic_fetch_sub(__cxx_atomic_rebind(&__c->__waiters), (ptrdiff_t)1, memory_order_relaxed); +#endif +} +template ::__value, int>::type = 1> +_LIBCPP_INLINE_VISIBILITY void __cxx_atomic_notify_all(__cxx_atomic_impl<_Tp> const volatile* __a) { +#ifndef _LIBCPP_HAS_NO_THREAD_CONTENTION_TABLE + auto * const __c = __libcpp_contention_state(__a); + __cxx_atomic_thread_fence(memory_order_seq_cst); + if (0 != __cxx_atomic_load(__cxx_atomic_rebind(&__c->__waiters), memory_order_relaxed)) +#endif + __libcpp_platform_wake((_Tp*)__a, true); +} +template ::__value, int>::type = 1> +_LIBCPP_INLINE_VISIBILITY void __cxx_atomic_notify_one(__cxx_atomic_impl<_Tp> const volatile* __a) { +#ifndef _LIBCPP_HAS_NO_THREAD_CONTENTION_TABLE + auto * const __c = __libcpp_contention_state(__a); + __cxx_atomic_thread_fence(memory_order_seq_cst); + if (0 != __cxx_atomic_load(__cxx_atomic_rebind(&__c->__waiters), memory_order_relaxed)) +#endif + __libcpp_platform_wake((_Tp*)__a, false); +} + +#elif !defined(_LIBCPP_HAS_NO_THREAD_CONTENTION_TABLE) + +template +_LIBCPP_INLINE_VISIBILITY void __cxx_atomic_notify_all(__cxx_atomic_impl<_Tp> const volatile* __a) { + auto * const __c = __libcpp_contention_state(__a); + __cxx_atomic_thread_fence(memory_order_seq_cst); + if(__builtin_expect(0 == __cxx_atomic_load(__cxx_atomic_rebind(&__c->__credit), memory_order_relaxed), 1)) + return; + if(0 != __cxx_atomic_exchange(__cxx_atomic_rebind(&__c->__credit), (ptrdiff_t)0, memory_order_relaxed)) { + __libcpp_mutex_lock(&__c->__mutex); + __libcpp_mutex_unlock(&__c->__mutex); + __libcpp_condvar_broadcast(&__c->__condvar); + } +} +template +_LIBCPP_INLINE_VISIBILITY void __cxx_atomic_notify_one(__cxx_atomic_impl<_Tp> const volatile* __a) { + __cxx_atomic_notify_all(__a); +} +template +_LIBCPP_INLINE_VISIBILITY void __cxx_atomic_try_wait_slow(__cxx_atomic_impl<_Tp> const volatile* __a, _Tp const __val, memory_order __order) { + auto * const __c = __libcpp_contention_state(__a); + __libcpp_mutex_lock(&__c->__mutex); + __cxx_atomic_store(__cxx_atomic_rebind(&__c->__credit), (ptrdiff_t)1, memory_order_relaxed); + __cxx_atomic_thread_fence(memory_order_seq_cst); + if (__cxx_atomic_compare_equal(__cxx_atomic_load(__a, __order), __val)) + __libcpp_condvar_wait(&__c->__condvar, &__c->__mutex); + __libcpp_mutex_unlock(&__c->__mutex); +} + +#else + +template +_LIBCPP_INLINE_VISIBILITY void __cxx_atomic_try_wait_slow(__cxx_atomic_impl<_Tp> const volatile* __a, _Tp __val, memory_order __order) { + __cxx_atomic_try_wait_slow_fallback(__a, __val, __order); +} + +template +_LIBCPP_INLINE_VISIBILITY void __cxx_atomic_notify_one(__cxx_atomic_impl<_Tp> const volatile*) { +} + +template +_LIBCPP_INLINE_VISIBILITY void __cxx_atomic_notify_all(__cxx_atomic_impl<_Tp> const volatile*) { +} + +#endif // _LIBCPP_HAS_PLATFORM_WAIT || !defined(_LIBCPP_HAS_NO_THREAD_CONTENTION_TABLE) + +template +_LIBCPP_INLINE_VISIBILITY void __cxx_atomic_wait(__cxx_atomic_impl<_Tp> const volatile* __a, _Tp const __val, memory_order __order) { +#ifndef _LIBCPP_NO_SPINNING + for(int __i = 0; __i < 16; ++__i) { + if(!__cxx_atomic_compare_equal(__cxx_atomic_load(__a, __order), __val)) + return; + if(__i < 12) + __libcpp_thread_yield_processor(); + else + __libcpp_thread_yield(); + } +#endif + while(__cxx_atomic_compare_equal(__cxx_atomic_load(__a, __order), __val)) + __cxx_atomic_try_wait_slow(__a, __val, __order); +} + // general atomic template ::value && !is_same<_Tp, bool>::value> @@ -1532,6 +1687,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 @@ -1643,6 +1811,7 @@ : public __atomic_base<_Tp> { typedef __atomic_base<_Tp> __base; + using value_type = _Tp; _LIBCPP_INLINE_VISIBILITY atomic() _NOEXCEPT _LIBCPP_DEFAULT _LIBCPP_INLINE_VISIBILITY @@ -1663,6 +1832,7 @@ : public __atomic_base<_Tp*> { typedef __atomic_base<_Tp*> __base; + using value_type = _Tp*; _LIBCPP_INLINE_VISIBILITY atomic() _NOEXCEPT _LIBCPP_DEFAULT _LIBCPP_INLINE_VISIBILITY @@ -1947,6 +2117,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 @@ -2279,6 +2519,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);} @@ -2292,6 +2539,25 @@ 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 @@ -2310,6 +2576,35 @@ #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 +2661,65 @@ __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 +2788,11 @@ typedef atomic atomic_intmax_t; typedef atomic atomic_uintmax_t; +static_assert(ATOMIC_INT_LOCK_FREE, "This library assumes atomic is lock-free."); + +typedef atomic atomic_signed_lock_free; +typedef atomic atomic_unsigned_lock_free; + #define ATOMIC_FLAG_INIT {false} #define ATOMIC_VAR_INIT(__v) {__v} Index: include/barrier =================================================================== --- /dev/null +++ include/barrier @@ -0,0 +1,323 @@ +// -*- 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 <__threading_support> +#include +#include +# ifndef _LIBCPP_HAS_NO_TREE_BARRIER +# include +# 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 + +_LIBCPP_BEGIN_NAMESPACE_STD + +struct __empty_completion +{ + inline _LIBCPP_INLINE_VISIBILITY + void operator()() noexcept { } +}; + +#ifndef _LIBCPP_HAS_NO_TREE_BARRIER + +template +class alignas(64) __barrier_base { + + ptrdiff_t __expected; + atomic __expected_adjustment; + _CompletionF __completion; + + using __phase_t = uint8_t; + atomic<__phase_t> __phase; + + struct alignas(64) __state_t + { + struct { + atomic<__phase_t> __phase = ATOMIC_VAR_INIT(0); + } __tickets[64]; + }; + vector<__state_t> __state; + + inline _LIBCPP_INLINE_VISIBILITY + bool __arrive(__phase_t const __old_phase) + { + __phase_t const __half_step = __old_phase + 1, __full_step = __old_phase + 2; +#ifndef _LIBCPP_HAS_NO_THREAD_FAVORITE_BARRIER_INDEX + ptrdiff_t __current = __libcpp_thread_favorite_barrier_index, +#else + ptrdiff_t __current = 0, +#endif + __current_expected = __expected, + __last_node = (__current_expected >> 1); + for(size_t __round = 0;; ++__round) { + assert(__round <= 63); + if(__current_expected == 1) + return true; + for(;;++__current) { +#ifndef _LIBCPP_HAS_NO_THREAD_FAVORITE_BARRIER_INDEX + if(0 == __round) { + if(__current >= __current_expected) + __current = 0; + __libcpp_thread_favorite_barrier_index = __current; + } +#endif + assert(__current <= __last_node); + __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 + assert(expect == __full_step); + } + 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 + assert(expect == __full_step); + } + assert(__round == 0 && expect == __full_step); + } + __current_expected = (__current_expected >> 1) + (__current_expected & 1); + __current &= ~( 1 << __round ); + __last_node &= ~( 1 << __round ); + } + } + +public: + using arrival_token = __phase_t; + + inline _LIBCPP_INLINE_VISIBILITY + __barrier_base(ptrdiff_t __expected, _CompletionF __completion = _CompletionF()) + : __expected(__expected), __expected_adjustment(0), __completion(__completion), + __phase(0), __state((__expected+1) >> 1) + { + assert(__expected >= 0); + } + + inline _LIBCPP_INLINE_VISIBILITY + ~__barrier_base() = default; + + __barrier_base(__barrier_base const&) = delete; + __barrier_base& operator=(__barrier_base const&) = delete; + + [[nodiscard]] inline _LIBCPP_INLINE_VISIBILITY + arrival_token arrive(ptrdiff_t update = 1) + { + assert(update > 0); + auto __old_phase = __phase.load(memory_order_relaxed); + for(; update; --update) + if(__arrive(__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); + } + return __old_phase; + } + inline _LIBCPP_INLINE_VISIBILITY + void wait(arrival_token&& __old_phase) const + { + __libcpp_thread_poll_with_backoff([&]() -> bool { + return __phase.load(memory_order_acquire) != __old_phase; + }); + } + inline _LIBCPP_INLINE_VISIBILITY + void arrive_and_wait() + { + wait(arrive()); + } + inline _LIBCPP_INLINE_VISIBILITY + void arrive_and_drop() + { + __expected_adjustment.fetch_sub(1, memory_order_relaxed); + (void)arrive(); + } +}; + +#else + +template +class __barrier_base { + + alignas(64) atomic __expected, arrived; + alignas(64) _CompletionF __completion; + alignas(64) atomic __phase; +public: + using arrival_token = bool; + + _LIBCPP_INLINE_VISIBILITY + __barrier_base(ptrdiff_t __expected, _CompletionF __completion = _CompletionF()) + : __phase(false), __expected(__expected), arrived(__expected), __completion(__completion) + { + } + + ~__barrier_base() = default; + + __barrier_base(__barrier_base const&) = delete; + __barrier_base& operator=(__barrier_base const&) = delete; + + [[nodiscard]] _LIBCPP_INLINE_VISIBILITY + arrival_token arrive(ptrdiff_t update = 1) + { + auto const __old_phase = __phase.load(memory_order_relaxed); + auto const __result = arrived.fetch_sub(update, memory_order_acq_rel) - update; + assert(__result >= 0); + 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); + atomic_notify_all(&__phase); + } + 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_wait() + { + wait(arrive()); + } + _LIBCPP_INLINE_VISIBILITY + void arrive_and_drop() + { + __expected.fetch_sub(1, memory_order_relaxed); + (void)arrive(); + } +}; + +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; + + alignas(64) atomic __phase_arrived_expected; + + static inline _LIBCPP_INLINE_VISIBILITY + constexpr uint64_t __init(ptrdiff_t __count) _NOEXCEPT + { + return (((1u << 31) - __count) << 32) + | ((1u << 31) - __count); + } + +public: + using arrival_token = uint64_t; + + _LIBCPP_INLINE_VISIBILITY + __barrier_base(ptrdiff_t __count, __empty_completion = __empty_completion()) + : __phase_arrived_expected(__init(__count)) { + } + + ~__barrier_base() = default; + + __barrier_base(__barrier_base const&) = delete; + __barrier_base& operator=(__barrier_base const&) = delete; + + [[nodiscard]] inline _LIBCPP_INLINE_VISIBILITY + arrival_token arrive(ptrdiff_t update = 1) + { + 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 + { + __libcpp_thread_poll_with_backoff([&]() -> bool + { + uint64_t const __current = __phase_arrived_expected.load(memory_order_acquire); + return ((__current & __phase_bit) != __phase); + }); + } + inline _LIBCPP_INLINE_VISIBILITY + void arrive_and_wait() + { + wait(arrive()); + } + inline _LIBCPP_INLINE_VISIBILITY + void arrive_and_drop() + { + __phase_arrived_expected.fetch_add(__expected_unit, memory_order_relaxed); + (void)arrive(); + } +}; + +#endif //_LIBCPP_HAS_NO_TREE_BARRIER + +template +class barrier : public __barrier_base<_CompletionF> { +public: + _LIBCPP_INLINE_VISIBILITY + barrier(ptrdiff_t __count, _CompletionF __completion = _CompletionF()) + : __barrier_base<_CompletionF>(__count, __completion) { + } +}; + +_LIBCPP_END_NAMESPACE_STD + +#endif //_LIBCPP_BARRIER Index: include/chrono =================================================================== --- include/chrono +++ include/chrono @@ -1568,6 +1568,8 @@ /////////////////////// clocks /////////////////////////// ////////////////////////////////////////////////////////// +#ifndef _LIBCPP_HAS_CLOCK_API_EXTERNAL + class _LIBCPP_TYPE_VIS system_clock { public: @@ -1600,7 +1602,12 @@ typedef system_clock high_resolution_clock; #endif +#endif // _LIBCPP_HAS_CLOCK_API_EXTERNAL + #if _LIBCPP_STD_VER > 17 + +#ifndef _LIBCPP_HAS_CLOCK_API_EXTERNAL + // [time.clock.file], type file_clock using file_clock = _VSTD_FS::_FilesystemClock; @@ -1619,6 +1626,7 @@ using local_seconds = local_time; using local_days = local_time; +#endif // _LIBCPP_HAS_CLOCK_API_EXTERNAL struct last_spec { explicit last_spec() = default; }; Index: include/cstddef =================================================================== --- include/cstddef +++ include/cstddef @@ -60,8 +60,12 @@ _LIBCPP_END_NAMESPACE_STD #if _LIBCPP_STD_VER > 14 +#ifdef _LIBCPP_BEGIN_NAMESPACE_STD_NOVERSION +_LIBCPP_BEGIN_NAMESPACE_STD_NOVERSION +#else namespace std // purposefully not versioned { +#endif //_LIBCPP_BEGIN_NAMESPACE_STD_NOVERSION enum class byte : unsigned char {}; constexpr byte operator| (byte __lhs, byte __rhs) noexcept @@ -105,7 +109,11 @@ )); } +#ifdef _LIBCPP_END_NAMESPACE_STD_NOVERSION +_LIBCPP_END_NAMESPACE_STD_NOVERSION +#else } +#endif //_LIBCPP_END_NAMESPACE_STD_NOVERSION #include // rest of byte #endif Index: include/latch =================================================================== --- /dev/null +++ include/latch @@ -0,0 +1,105 @@ +// -*- 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 <__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 + +_LIBCPP_BEGIN_NAMESPACE_STD + +class __latch_base +{ + alignas(64) atomic __counter; +public: + inline _LIBCPP_INLINE_VISIBILITY + constexpr explicit __latch_base(ptrdiff_t __expected) + : __counter(__expected) { } + + ~__latch_base() = default; + __latch_base(const __latch_base&) = delete; + __latch_base& operator=(const __latch_base&) = delete; + + inline _LIBCPP_INLINE_VISIBILITY + void count_down(ptrdiff_t __update = 1) + { + assert(__update > 0); + auto const __old = __counter.fetch_sub(__update, memory_order_release); + assert(__old >= __update); + if(__old == __update) + __counter.notify_all(); + } + inline _LIBCPP_INLINE_VISIBILITY + bool try_wait() const noexcept + { + return __counter.load(memory_order_acquire) == 0; + } + inline _LIBCPP_INLINE_VISIBILITY + void wait() const + { + while(1) { + auto const __current = __counter.load(memory_order_acquire); + if(__current == 0) + return; + __counter.wait(__current, memory_order_relaxed) + ; + } + } + inline _LIBCPP_INLINE_VISIBILITY + void arrive_and_wait(ptrdiff_t __update = 1) + { + count_down(__update); + wait(); + } +}; + +using latch = __latch_base; + +_LIBCPP_END_NAMESPACE_STD + +#endif //_LIBCPP_LATCH Index: include/semaphore =================================================================== --- /dev/null +++ include/semaphore @@ -0,0 +1,433 @@ +// -*- 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 + +_LIBCPP_BEGIN_NAMESPACE_STD + +template +class __atomic_semaphore_base +{ + _LIBCPP_INLINE_VISIBILITY + bool __fetch_sub_if_slow(ptrdiff_t __old) + { + while (__old != 0) { + if (__count.compare_exchange_weak(__old, __old - 1, memory_order_acquire, memory_order_relaxed)) + return true; + } + return false; + } + + _LIBCPP_INLINE_VISIBILITY + bool __fetch_sub_if() + { + ptrdiff_t __old = __count.load(memory_order_acquire); + if (__old == 0) + return false; + if(__count.compare_exchange_weak(__old, __old - 1, memory_order_acquire, memory_order_relaxed)) + return true; + return __fetch_sub_if_slow(__old); // fail only if not __available + } + + _LIBCPP_INLINE_VISIBILITY + void __wait_slow() + { + while (1) { + ptrdiff_t const __old = __count.load(memory_order_acquire); + if(__old != 0) + break; + __count.wait(__old, memory_order_relaxed); + } + } + + _LIBCPP_INLINE_VISIBILITY + bool __acquire_slow_timed(chrono::nanoseconds const& __rel_time) + { + return __libcpp_thread_poll_with_backoff([=]() { + ptrdiff_t const __old = __count.load(memory_order_acquire); + return __old != 0 && __fetch_sub_if_slow(__old); + }, __rel_time); + } + atomic __count; + +public: + static constexpr ptrdiff_t max() noexcept + { + return numeric_limits::max(); + } + + _LIBCPP_INLINE_VISIBILITY + __atomic_semaphore_base(ptrdiff_t __count) : __count(__count) { } + + ~__atomic_semaphore_base() = default; + + __atomic_semaphore_base(__atomic_semaphore_base const&) = delete; + __atomic_semaphore_base& operator=(__atomic_semaphore_base const&) = delete; + + _LIBCPP_INLINE_VISIBILITY + void release(ptrdiff_t __update = 1) + { + __count.fetch_add(__update, memory_order_release); + if(__update > 1) + __count.notify_all(); + else + __count.notify_one(); + } + + _LIBCPP_INLINE_VISIBILITY + void acquire() + { + while (!try_acquire()) + __wait_slow(); + } + + _LIBCPP_INLINE_VISIBILITY + bool try_acquire() noexcept + { + return __fetch_sub_if(); + } + + template + _LIBCPP_INLINE_VISIBILITY + bool try_acquire_until(chrono::time_point const& __abs_time) + { + if (try_acquire()) + return true; + else + return __acquire_slow_timed(__abs_time - Clock::now()); + } + + template + _LIBCPP_INLINE_VISIBILITY + bool try_acquire_for(chrono::duration const& __rel_time) + { + + if (try_acquire()) + return true; + else + return __acquire_slow_timed(__rel_time); + } +}; + +#ifndef _LIBCPP_USE_NATIVE_SEMAPHORES + +template<> +class __atomic_semaphore_base<1> { + + _LIBCPP_INLINE_VISIBILITY + bool __acquire_slow_timed(chrono::nanoseconds const& __rel_time) + { + return __libcpp_thread_poll_with_backoff([=]() { + return try_acquire(); + }, __rel_time); + } + atomic __available; + +public: + static constexpr ptrdiff_t max() noexcept { return 1; } + + _LIBCPP_INLINE_VISIBILITY + __atomic_semaphore_base(ptrdiff_t __available) : __available(__available) { } + + ~__atomic_semaphore_base() = default; + + __atomic_semaphore_base(__atomic_semaphore_base const&) = delete; + __atomic_semaphore_base& operator=(__atomic_semaphore_base const&) = delete; + + _LIBCPP_INLINE_VISIBILITY + void release(ptrdiff_t __update = 1) + { + assert(__update == 1); + __available.store(1, memory_order_release); + __available.notify_one(); + } + + _LIBCPP_INLINE_VISIBILITY + void acquire() + { + while (!__builtin_expect(try_acquire(), 1)) + __available.wait(0, memory_order_relaxed); + } + + _LIBCPP_INLINE_VISIBILITY + bool try_acquire() noexcept + { + return 1 == __available.exchange(0, memory_order_acquire); + } + + template + _LIBCPP_INLINE_VISIBILITY + bool try_acquire_until(chrono::time_point const& __abs_time) + { + if (__builtin_expect(try_acquire(), 1)) + return true; + else + return __acquire_slow_timed(__abs_time - Clock::now()); + } + + template + _LIBCPP_INLINE_VISIBILITY + bool try_acquire_for(chrono::duration const& __rel_time) + { + if (__builtin_expect(try_acquire(), 1)) + return true; + else + return __acquire_slow_timed(__rel_time); + } +}; + +#else + +class __sem_semaphore_base { + + _LIBCPP_INLINE_VISIBILITY + bool __backfill(bool __success) + { +#ifndef _LIBCPP_HAS_NO_SEMAPHORE_BACK_BUFFER + if(__success) { + auto const __back_amount = __backbuffer.fetch_sub(2, memory_order_acquire); + bool const __post_one = __back_amount > 0; + bool const __post_two = __back_amount > 1; + auto const __success = (!__post_one || __libcpp_semaphore_post(&__semaphore)) && + (!__post_two || __libcpp_semaphore_post(&__semaphore)); + assert(__success); + if(!__post_one || !__post_two) + __backbuffer.fetch_add(!__post_one ? 2 : 1, memory_order_relaxed); + } +#endif + return __success; + } + + _LIBCPP_INLINE_VISIBILITY + bool __try_acquire_fast() + { +#ifndef _LIBCPP_HAS_NO_SEMAPHORE_FRONT_BUFFER + + ptrdiff_t __old; + __libcpp_thread_poll_with_backoff([&]() { + __old = __frontbuffer.load(memory_order_relaxed); + return 0 != (__old >> 32); + }, chrono::microseconds(5)); + + // always steal if you can + while(__old >> 32) + if(__frontbuffer.compare_exchange_weak(__old, __old - (1ll << 32), memory_order_acquire)) + return true; + // record we're waiting + __old = __frontbuffer.fetch_add(1ll, memory_order_release); + // ALWAYS steal if you can! + while(__old >> 32) + if(__frontbuffer.compare_exchange_weak(__old, __old - (1ll << 32), memory_order_acquire)) + break; + // not going to wait after all + if(__old >> 32) + return __try_done(true); +#endif + // the wait has begun... + return false; + } + + _LIBCPP_INLINE_VISIBILITY + bool __try_done(bool __success) + { +#ifndef _LIBCPP_HAS_NO_SEMAPHORE_FRONT_BUFFER + // record we're NOT waiting + __frontbuffer.fetch_sub(1ll, memory_order_release); +#endif + return __backfill(__success); + } + + _LIBCPP_INLINE_VISIBILITY + void __release_slow(ptrdiff_t __post_amount) + { + #ifndef _LIBCPP_HAS_NO_SEMAPHORE_BACK_BUFFER + bool const __post_one = __post_amount > 0; + bool const __post_two = __post_amount > 1; + if(__post_amount > 2) + __backbuffer.fetch_add(__post_amount - 2, memory_order_acq_rel); + auto const __success = (!__post_one || __libcpp_semaphore_post(&__semaphore)) && + (!__post_two || __libcpp_semaphore_post(&__semaphore)); + assert(__success); + #else + for(; __post_amount; --__post_amount) { + auto const __success = __libcpp_semaphore_post(&__semaphore); + assert(__success); + } + #endif + } + + __libcpp_semaphore_t __semaphore; +#ifndef _LIBCPP_HAS_NO_SEMAPHORE_FRONT_BUFFER + atomic __frontbuffer; +#endif +#ifndef _LIBCPP_HAS_NO_SEMAPHORE_BACK_BUFFER + atomic __backbuffer; +#endif + +public: + static constexpr ptrdiff_t max() noexcept { + return _LIBCPP_SEMAPHORE_MAX; + } + + _LIBCPP_INLINE_VISIBILITY + __sem_semaphore_base(ptrdiff_t __count = 0) : __semaphore() +#ifndef _LIBCPP_HAS_NO_SEMAPHORE_FRONT_BUFFER + , __frontbuffer(__count << 32) +#endif +#ifndef _LIBCPP_HAS_NO_SEMAPHORE_BACK_BUFFER + , __backbuffer(0) +#endif + { + assert(__count <= max()); + auto const __success = +#ifndef _LIBCPP_HAS_NO_SEMAPHORE_FRONT_BUFFER + __libcpp_semaphore_init(&__semaphore, 0); +#else + __libcpp_semaphore_init(&__semaphore, __count); +#endif + assert(__success); + } + + _LIBCPP_INLINE_VISIBILITY + ~__sem_semaphore_base() { +#ifndef _LIBCPP_HAS_NO_SEMAPHORE_FRONT_BUFFER + assert(0 == (__frontbuffer.load(memory_order_relaxed) & ~0u)); +#endif + auto const __success = __libcpp_semaphore_destroy(&__semaphore); + assert(__success); + } + + __sem_semaphore_base(const __sem_semaphore_base&) = delete; + __sem_semaphore_base& operator=(const __sem_semaphore_base&) = delete; + + _LIBCPP_INLINE_VISIBILITY + void release(ptrdiff_t __update = 1) + { +#ifndef _LIBCPP_HAS_NO_SEMAPHORE_FRONT_BUFFER + // boldly assume the semaphore is taken but uncontended + ptrdiff_t __old = 0; + // try to fast-release as long as it's uncontended + while(0 == (__old & ~0ul)) + if(__frontbuffer.compare_exchange_weak(__old, __old + (__update << 32), memory_order_acq_rel)) + return; +#endif + // slow-release it is + __release_slow(__update); + } + + _LIBCPP_INLINE_VISIBILITY + void acquire() + { + if(!__try_acquire_fast()) + __try_done(__libcpp_semaphore_wait(&__semaphore)); + } + + _LIBCPP_INLINE_VISIBILITY + bool try_acquire() noexcept + { + return try_acquire_for(chrono::nanoseconds(0)); + } + + template + _LIBCPP_INLINE_VISIBILITY + bool try_acquire_until(chrono::time_point const& __abs_time) + { + auto const current = max(Clock::now(), __abs_time); + return try_acquire_for(chrono::duration_cast(__abs_time - current)); + } + + template + _LIBCPP_INLINE_VISIBILITY + bool try_acquire_for(chrono::duration const& __rel_time) + { + return __try_acquire_fast() || + __try_done(__libcpp_semaphore_wait_timed(&__semaphore, __rel_time)); + } +}; + +#endif //_LIBCPP_HAS_NO_SEMAPHORES + +template +using __semaphore_base = +#ifdef _LIBCPP_USE_NATIVE_SEMAPHORES + typename conditional>::type +#else + __atomic_semaphore_base +#endif + ; + +template +class counting_semaphore : public __semaphore_base +{ + static_assert(least_max_value <= __semaphore_base::max(), ""); +public: + _LIBCPP_INLINE_VISIBILITY + counting_semaphore(ptrdiff_t __count = 0) : __semaphore_base(__count) { } + ~counting_semaphore() = default; + + counting_semaphore(const counting_semaphore&) = delete; + counting_semaphore& operator=(const counting_semaphore&) = delete; +}; + +using binary_semaphore = counting_semaphore<1>; + +_LIBCPP_END_NAMESPACE_STD + +#endif //_LIBCPP_SEMAPHORE Index: include/stdexcept =================================================================== --- include/stdexcept +++ include/stdexcept @@ -72,8 +72,12 @@ _LIBCPP_END_NAMESPACE_STD -namespace std // purposefully not using versioning namespace +#ifdef _LIBCPP_BEGIN_NAMESPACE_STD_NOVERSION +_LIBCPP_BEGIN_NAMESPACE_STD_NOVERSION +#else +namespace std // purposefully not versioned { +#endif //_LIBCPP_BEGIN_NAMESPACE_STD_NOVERSION class _LIBCPP_EXCEPTION_ABI logic_error : public exception @@ -204,7 +208,11 @@ #endif }; -} // std +#ifdef _LIBCPP_END_NAMESPACE_STD_NOVERSION +_LIBCPP_END_NAMESPACE_STD_NOVERSION +#else +} +#endif //_LIBCPP_END_NAMESPACE_STD_NOVERSION _LIBCPP_BEGIN_NAMESPACE_STD Index: include/type_traits =================================================================== --- include/type_traits +++ include/type_traits @@ -4024,8 +4024,12 @@ #if _LIBCPP_STD_VER > 14 // std::byte +#ifdef _LIBCPP_BEGIN_NAMESPACE_STD_NOVERSION +_LIBCPP_BEGIN_NAMESPACE_STD_NOVERSION +#else namespace std // purposefully not versioned { +#endif //_LIBCPP_BEGIN_NAMESPACE_STD_NOVERSION template constexpr typename enable_if, byte>::type & operator<<=(byte& __lhs, _Integer __shift) noexcept @@ -4050,7 +4054,11 @@ constexpr typename enable_if, _Integer>::type to_integer(byte __b) noexcept { return static_cast<_Integer>(__b); } +#ifdef _LIBCPP_END_NAMESPACE_STD_NOVERSION +_LIBCPP_END_NAMESPACE_STD_NOVERSION +#else } +#endif //_LIBCPP_END_NAMESPACE_STD_NOVERSION #endif #endif // _LIBCPP_TYPE_TRAITS Index: src/CMakeLists.txt =================================================================== --- src/CMakeLists.txt +++ src/CMakeLists.txt @@ -4,6 +4,8 @@ set(LIBCXX_SOURCES algorithm.cpp any.cpp + atomic.cpp + barrier.cpp bind.cpp charconv.cpp chrono.cpp Index: src/atomic.cpp =================================================================== --- /dev/null +++ src/atomic.cpp @@ -0,0 +1,27 @@ +//===------------------------- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP_HAS_NO_THREADS +#include "atomic" + +_LIBCPP_BEGIN_NAMESPACE_STD + +#ifndef _LIBCPP_HAS_NO_THREAD_CONTENTION_TABLE + +__libcpp_contention_t __libcpp_contention_state_[ 256 /* < there's no magic in this number */ ]; + +_LIBCPP_FUNC_VIS +__libcpp_contention_t * __libcpp_contention_state(void const volatile * p) _NOEXCEPT { + return __libcpp_contention_state_ + ((std::uintptr_t)p & 255); +} + +#endif //_LIBCPP_HAS_NO_THREAD_CONTENTION_TABLE + +_LIBCPP_END_NAMESPACE_STD + +#endif //_LIBCPP_HAS_NO_THREADS Index: src/barrier.cpp =================================================================== --- /dev/null +++ src/barrier.cpp @@ -0,0 +1,23 @@ +//===------------------------- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP_HAS_NO_THREADS +#include "barrier" + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if !defined(_LIBCPP_HAS_NO_TREE_BARRIER) && !defined(_LIBCPP_HAS_NO_THREAD_FAVORITE_BARRIER_INDEX) + +_LIBCPP_EXPORTED_FROM_ABI +thread_local ptrdiff_t __libcpp_thread_favorite_barrier_index = 0; + +#endif + +_LIBCPP_END_NAMESPACE_STD + +#endif //_LIBCPP_HAS_NO_THREADS Index: test/std/atomics/atomics.types.operations/atomics.types.operations.wait/atomic_wait.pass.cpp =================================================================== --- /dev/null +++ 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; +} Index: test/std/thread/thread.barrier/arrive.pass.cpp =================================================================== --- /dev/null +++ test/std/thread/thread.barrier/arrive.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 + +// + +#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; +} Index: test/std/thread/thread.barrier/arrive_and_drop.pass.cpp =================================================================== --- /dev/null +++ test/std/thread/thread.barrier/arrive_and_drop.pass.cpp @@ -0,0 +1,29 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +#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; +} Index: test/std/thread/thread.barrier/arrive_and_wait.pass.cpp =================================================================== --- /dev/null +++ test/std/thread/thread.barrier/arrive_and_wait.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 + +// + +#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; +} Index: test/std/thread/thread.barrier/completion.pass.cpp =================================================================== --- /dev/null +++ test/std/thread/thread.barrier/completion.pass.cpp @@ -0,0 +1,35 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +#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; +} Index: test/std/thread/thread.barrier/version.pass.cpp =================================================================== --- /dev/null +++ test/std/thread/thread.barrier/version.pass.cpp @@ -0,0 +1,24 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +#include + +#include "test_macros.h" + +#ifndef _LIBCPP_VERSION +#error _LIBCPP_VERSION not defined +#endif + +int main(int, char**) +{ + return 0; +} Index: test/std/thread/thread.latch/arrive_and_wait.pass.cpp =================================================================== --- /dev/null +++ test/std/thread/thread.latch/arrive_and_wait.pass.cpp @@ -0,0 +1,29 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +#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; +} Index: test/std/thread/thread.latch/count_down.pass.cpp =================================================================== --- /dev/null +++ test/std/thread/thread.latch/count_down.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 + +// + +#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; +} Index: test/std/thread/thread.latch/try_wait.pass.cpp =================================================================== --- /dev/null +++ test/std/thread/thread.latch/try_wait.pass.cpp @@ -0,0 +1,27 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +#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; +} Index: test/std/thread/thread.latch/version.pass.cpp =================================================================== --- /dev/null +++ test/std/thread/thread.latch/version.pass.cpp @@ -0,0 +1,24 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +#include + +#include "test_macros.h" + +#ifndef _LIBCPP_VERSION +#error _LIBCPP_VERSION not defined +#endif + +int main(int, char**) +{ + return 0; +} Index: test/std/thread/thread.semaphore/acquire.pass.cpp =================================================================== --- /dev/null +++ test/std/thread/thread.semaphore/acquire.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 + +// + +#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; +} Index: test/std/thread/thread.semaphore/max.pass.cpp =================================================================== --- /dev/null +++ test/std/thread/thread.semaphore/max.pass.cpp @@ -0,0 +1,27 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +#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; +} Index: test/std/thread/thread.semaphore/release.pass.cpp =================================================================== --- /dev/null +++ test/std/thread/thread.semaphore/release.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 + +// + +#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; +} Index: test/std/thread/thread.semaphore/timed.pass.cpp =================================================================== --- /dev/null +++ test/std/thread/thread.semaphore/timed.pass.cpp @@ -0,0 +1,43 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +#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; +} Index: test/std/thread/thread.semaphore/try_acquire.pass.cpp =================================================================== --- /dev/null +++ test/std/thread/thread.semaphore/try_acquire.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 + +// + +#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; +} Index: test/std/thread/thread.semaphore/version.pass.cpp =================================================================== --- /dev/null +++ test/std/thread/thread.semaphore/version.pass.cpp @@ -0,0 +1,24 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +#include + +#include "test_macros.h" + +#ifndef _LIBCPP_VERSION +#error _LIBCPP_VERSION not defined +#endif + +int main(int, char**) +{ + return 0; +}