[libcxx-commits] [libcxx] 54fa9ec - [libc++] Implementation of C++20's P1135R6 for libcxx

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Mon Feb 24 07:59:50 PST 2020


Author: Olivier Giroux
Date: 2020-02-24T10:59:35-05:00
New Revision: 54fa9ecd3088508b05b0c5b5cb52da8a3c188655

URL: https://github.com/llvm/llvm-project/commit/54fa9ecd3088508b05b0c5b5cb52da8a3c188655
DIFF: https://github.com/llvm/llvm-project/commit/54fa9ecd3088508b05b0c5b5cb52da8a3c188655.diff

LOG: [libc++] Implementation of C++20's P1135R6 for libcxx

Differential Revision: https://reviews.llvm.org/D68480

Added: 
    libcxx/include/barrier
    libcxx/include/latch
    libcxx/include/semaphore
    libcxx/src/atomic.cpp
    libcxx/src/barrier.cpp
    libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.wait/atomic_wait.pass.cpp
    libcxx/test/std/atomics/types.pass.cpp
    libcxx/test/std/thread/thread.barrier/arrive.pass.cpp
    libcxx/test/std/thread/thread.barrier/arrive_and_drop.pass.cpp
    libcxx/test/std/thread/thread.barrier/arrive_and_wait.pass.cpp
    libcxx/test/std/thread/thread.barrier/completion.pass.cpp
    libcxx/test/std/thread/thread.barrier/max.pass.cpp
    libcxx/test/std/thread/thread.barrier/version.pass.cpp
    libcxx/test/std/thread/thread.latch/arrive_and_wait.pass.cpp
    libcxx/test/std/thread/thread.latch/count_down.pass.cpp
    libcxx/test/std/thread/thread.latch/max.pass.cpp
    libcxx/test/std/thread/thread.latch/try_wait.pass.cpp
    libcxx/test/std/thread/thread.latch/version.pass.cpp
    libcxx/test/std/thread/thread.semaphore/acquire.pass.cpp
    libcxx/test/std/thread/thread.semaphore/binary.pass.cpp
    libcxx/test/std/thread/thread.semaphore/max.pass.cpp
    libcxx/test/std/thread/thread.semaphore/release.pass.cpp
    libcxx/test/std/thread/thread.semaphore/timed.pass.cpp
    libcxx/test/std/thread/thread.semaphore/try_acquire.pass.cpp
    libcxx/test/std/thread/thread.semaphore/version.pass.cpp

Modified: 
    libcxx/include/CMakeLists.txt
    libcxx/include/__threading_support
    libcxx/include/atomic
    libcxx/include/module.modulemap
    libcxx/src/CMakeLists.txt
    libcxx/src/include/apple_availability.h
    libcxx/test/libcxx/double_include.sh.cpp
    libcxx/www/cxx2a_status.html

Removed: 
    


################################################################################
diff  --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 302da8a131b4..ca6c7c5c5675 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -25,6 +25,7 @@ set(files
   any
   array
   atomic
+  barrier
   bit
   bitset
   cassert
@@ -103,6 +104,7 @@ set(files
   iostream
   istream
   iterator
+  latch
   limits
   limits.h
   list
@@ -122,6 +124,7 @@ set(files
   ratio
   regex
   scoped_allocator
+  semaphore
   set
   setjmp.h
   shared_mutex

diff  --git a/libcxx/include/__threading_support b/libcxx/include/__threading_support
index dbf313a1bf22..fef172c70ea7 100644
--- a/libcxx/include/__threading_support
+++ b/libcxx/include/__threading_support
@@ -26,6 +26,10 @@
 #if defined(_LIBCPP_HAS_THREAD_API_PTHREAD)
 # include <pthread.h>
 # include <sched.h>
+# include <semaphore.h>
+# ifdef __APPLE__
+#  define _LIBCPP_NO_NATIVE_SEMAPHORES
+# endif
 #elif defined(_LIBCPP_HAS_THREAD_API_C11)
 # include <threads.h>
 #endif
@@ -65,6 +69,10 @@ typedef pthread_mutex_t __libcpp_recursive_mutex_t;
 typedef pthread_cond_t __libcpp_condvar_t;
 #define _LIBCPP_CONDVAR_INITIALIZER PTHREAD_COND_INITIALIZER
 
+// Semaphore
+typedef sem_t __libcpp_semaphore_t;
+#define _LIBCPP_SEMAPHORE_MAX SEM_VALUE_MAX
+
 // Execute once
 typedef pthread_once_t __libcpp_exec_once_flag;
 #define _LIBCPP_EXEC_ONCE_INITIALIZER PTHREAD_ONCE_INIT
@@ -127,6 +135,9 @@ typedef void* __libcpp_recursive_mutex_t[5];
 typedef void* __libcpp_condvar_t;
 #define _LIBCPP_CONDVAR_INITIALIZER 0
 
+// Semaphore
+typedef void* __libcpp_semaphore_t;
+
 // Execute Once
 typedef void* __libcpp_exec_once_flag;
 #define _LIBCPP_EXEC_ONCE_INITIALIZER 0
@@ -191,6 +202,22 @@ int __libcpp_condvar_timedwait(__libcpp_condvar_t *__cv, __libcpp_mutex_t *__m,
 _LIBCPP_THREAD_ABI_VISIBILITY
 int __libcpp_condvar_destroy(__libcpp_condvar_t* __cv);
 
+// Semaphore
+_LIBCPP_THREAD_ABI_VISIBILITY
+bool __libcpp_semaphore_init(__libcpp_semaphore_t* __sem, int __init);
+
+_LIBCPP_THREAD_ABI_VISIBILITY
+bool __libcpp_semaphore_destroy(__libcpp_semaphore_t* __sem);
+
+_LIBCPP_THREAD_ABI_VISIBILITY
+bool __libcpp_semaphore_post(__libcpp_semaphore_t* __sem);
+
+_LIBCPP_THREAD_ABI_VISIBILITY
+bool __libcpp_semaphore_wait(__libcpp_semaphore_t* __sem);
+
+_LIBCPP_THREAD_ABI_VISIBILITY
+bool __libcpp_semaphore_wait_timed(__libcpp_semaphore_t* __sem, chrono::nanoseconds const& __ns);
+
 // Execute once
 _LIBCPP_THREAD_ABI_VISIBILITY
 int __libcpp_execute_once(__libcpp_exec_once_flag *flag,
@@ -229,6 +256,16 @@ void __libcpp_thread_yield();
 _LIBCPP_THREAD_ABI_VISIBILITY
 void __libcpp_thread_sleep_for(const chrono::nanoseconds& __ns);
 
+struct __libcpp_timed_backoff_policy {
+  _LIBCPP_THREAD_ABI_VISIBILITY
+  bool operator()(chrono::nanoseconds __elapsed) const;
+};
+
+template<class _Fn, class _BFn>
+_LIBCPP_INLINE_VISIBILITY
+bool __libcpp_thread_poll_with_backoff(
+  _Fn && __f, _BFn && __bf, chrono::nanoseconds __max_elapsed = chrono::nanoseconds::zero());
+
 // Thread local storage
 _LIBCPP_THREAD_ABI_VISIBILITY
 int __libcpp_tls_create(__libcpp_tls_key* __key,
@@ -364,6 +401,38 @@ int __libcpp_condvar_destroy(__libcpp_condvar_t *__cv)
   return pthread_cond_destroy(__cv);
 }
 
+#ifndef _LIBCPP_NO_NATIVE_SEMAPHORES
+
+// Semaphore
+bool __libcpp_semaphore_init(__libcpp_semaphore_t* __sem, int __init)
+{
+    return sem_init(__sem, 0, __init) == 0;
+}
+
+bool __libcpp_semaphore_destroy(__libcpp_semaphore_t* __sem)
+{
+    return sem_destroy(__sem) == 0;
+}
+
+bool __libcpp_semaphore_post(__libcpp_semaphore_t* __sem)
+{
+    return sem_post(__sem) == 0;
+}
+
+bool __libcpp_semaphore_wait(__libcpp_semaphore_t* __sem)
+{
+    return sem_wait(__sem) == 0;
+}
+
+bool __libcpp_semaphore_wait_timed(__libcpp_semaphore_t* __sem, chrono::nanoseconds const& __ns)
+{
+    auto const __abs_time = chrono::system_clock::now().time_since_epoch() + __ns;
+    __libcpp_timespec_t __ts = __thread_detail::__convert_to_timespec(__abs_time);
+    return sem_timedwait(__sem, &__ts) == 0;
+}
+
+#endif //_LIBCPP_NO_NATIVE_SEMAPHORES
+
 // Execute once
 int __libcpp_execute_once(__libcpp_exec_once_flag *flag,
                           void (*init_routine)()) {
@@ -425,6 +494,40 @@ void __libcpp_thread_sleep_for(const chrono::nanoseconds& __ns)
    while (nanosleep(&__ts, &__ts) == -1 && errno == EINTR);
 }
 
+bool __libcpp_timed_backoff_policy::operator()(chrono::nanoseconds __elapsed) const
+{
+    if(__elapsed > chrono::milliseconds(128))
+        __libcpp_thread_sleep_for(chrono::milliseconds(8));
+    else if(__elapsed > chrono::microseconds(64))
+        __libcpp_thread_sleep_for(__elapsed / 2);
+    else if(__elapsed > chrono::microseconds(4))
+      __libcpp_thread_yield();
+    else
+      ; // poll
+    return false;
+}
+
+static constexpr int __libcpp_polling_count = 64;
+
+template<class _Fn, class _BFn>
+bool __libcpp_thread_poll_with_backoff(_Fn && __f, _BFn && __bf, chrono::nanoseconds __max_elapsed)
+{
+    auto const __start = chrono::high_resolution_clock::now();
+    for(int __count = 0;;) {
+      if(__f())
+        return true; // _Fn completion means success
+      if(__count < __libcpp_polling_count) {
+        __count += 1;
+        continue;
+      }
+      chrono::nanoseconds const __elapsed = chrono::high_resolution_clock::now() - __start;
+      if(__max_elapsed != chrono::nanoseconds::zero() && __max_elapsed < __elapsed)
+          return false; // timeout failure
+      if(__bf(__elapsed))
+        return false; // _BFn completion means failure
+    }
+}
+
 // Thread local storage
 int __libcpp_tls_create(__libcpp_tls_key *__key, void (*__at_exit)(void *))
 {

diff  --git a/libcxx/include/atomic b/libcxx/include/atomic
index 6904dd400032..02784df7d54c 100644
--- a/libcxx/include/atomic
+++ b/libcxx/include/atomic
@@ -547,8 +547,10 @@ void atomic_signal_fence(memory_order m) noexcept;
 */
 
 #include <__config>
+#include <__threading_support>
 #include <cstddef>
 #include <cstdint>
+#include <cstring>
 #include <type_traits>
 #include <version>
 
@@ -629,6 +631,11 @@ typedef enum memory_order {
 
 #endif // _LIBCPP_STD_VER > 17
 
+template <typename _Tp> _LIBCPP_INLINE_VISIBILITY
+bool __cxx_nonatomic_compare_equal(_Tp const& __lhs, _Tp const& __rhs) {
+    return memcmp(&__lhs, &__rhs, sizeof(_Tp)) == 0;
+}
+
 static_assert((is_same<underlying_type<memory_order>::type, __memory_order_underlying_t>::value),
   "unexpected underlying type for std::memory_order");
 
@@ -1218,9 +1225,9 @@ _LIBCPP_INLINE_VISIBILITY
 bool __cxx_atomic_compare_exchange_strong(volatile __cxx_atomic_lock_impl<_Tp>* __a,
                                           _Tp* __expected, _Tp __value, memory_order, memory_order) {
   __a->__lock();
-  _Tp temp;
-  __cxx_atomic_assign_volatile(temp, __a->__a_value);
-  bool __ret = temp == *__expected;
+  _Tp __temp;
+  __cxx_atomic_assign_volatile(__temp, __a->__a_value);
+  bool __ret = __temp == *__expected;
   if(__ret)
     __cxx_atomic_assign_volatile(__a->__a_value, __value);
   else
@@ -1247,9 +1254,9 @@ _LIBCPP_INLINE_VISIBILITY
 bool __cxx_atomic_compare_exchange_weak(volatile __cxx_atomic_lock_impl<_Tp>* __a,
                                         _Tp* __expected, _Tp __value, memory_order, memory_order) {
   __a->__lock();
-  _Tp temp;
-  __cxx_atomic_assign_volatile(temp, __a->__a_value);
-  bool __ret = temp == *__expected;
+  _Tp __temp;
+  __cxx_atomic_assign_volatile(__temp, __a->__a_value);
+  bool __ret = __temp == *__expected;
   if(__ret)
     __cxx_atomic_assign_volatile(__a->__a_value, __value);
   else
@@ -1452,6 +1459,73 @@ struct __cxx_atomic_impl : public _Base {
     : _Base(value) {}
 };
 
+#ifdef __linux__
+    using __cxx_contention_t = int32_t;
+#else
+    using __cxx_contention_t = int64_t;
+#endif //__linux__
+
+#if _LIBCPP_STD_VER >= 11
+
+using __cxx_atomic_contention_t = __cxx_atomic_impl<__cxx_contention_t>;
+
+#ifndef _LIBCPP_HAS_NO_PLATFORM_WAIT
+
+_LIBCPP_EXPORTED_FROM_ABI void __cxx_atomic_notify_one(void const volatile*);
+_LIBCPP_EXPORTED_FROM_ABI void __cxx_atomic_notify_all(void const volatile*);
+_LIBCPP_EXPORTED_FROM_ABI __cxx_contention_t __libcpp_atomic_monitor(void const volatile*);
+_LIBCPP_EXPORTED_FROM_ABI void __libcpp_atomic_wait(void const volatile*, __cxx_contention_t);
+
+_LIBCPP_EXPORTED_FROM_ABI void __cxx_atomic_notify_one(__cxx_atomic_contention_t const volatile*);
+_LIBCPP_EXPORTED_FROM_ABI void __cxx_atomic_notify_all(__cxx_atomic_contention_t const volatile*);
+_LIBCPP_EXPORTED_FROM_ABI __cxx_contention_t __libcpp_atomic_monitor(__cxx_atomic_contention_t const volatile*);
+_LIBCPP_EXPORTED_FROM_ABI void __libcpp_atomic_wait(__cxx_atomic_contention_t const volatile*, __cxx_contention_t);
+
+template <class _Atp, class _Fn>
+_LIBCPP_INLINE_VISIBILITY bool __cxx_atomic_wait(_Atp* __a, _Fn && __test_fn)
+{
+    auto const __libcpp_atomic_wait_backoff = [=](chrono::nanoseconds __elapsed) -> bool {
+        if(__elapsed > chrono::microseconds(64))
+        {
+            auto const __monitor = __libcpp_atomic_monitor(__a);
+            if(__test_fn())
+                return true;
+            __libcpp_atomic_wait(__a, __monitor);
+        }
+        else if(__elapsed > chrono::microseconds(4))
+            __libcpp_thread_yield();
+        else
+            ; // poll
+        return false;
+    };
+    return __libcpp_thread_poll_with_backoff(__test_fn, __libcpp_atomic_wait_backoff);
+}
+
+#else // _LIBCPP_HAS_NO_PLATFORM_WAIT
+
+template <class _Tp>
+_LIBCPP_INLINE_VISIBILITY void __cxx_atomic_notify_all(__cxx_atomic_impl<_Tp> const volatile*) { }
+template <class _Tp>
+_LIBCPP_INLINE_VISIBILITY void __cxx_atomic_notify_one(__cxx_atomic_impl<_Tp> const volatile*) { }
+template <class _Atp, class _Fn>
+_LIBCPP_INLINE_VISIBILITY bool __cxx_atomic_wait(_Atp*, _Fn && __test_fn)
+{
+    return __libcpp_thread_poll_with_backoff(__test_fn, __libcpp_timed_backoff_policy());
+}
+
+#endif // _LIBCPP_HAS_NO_PLATFORM_WAIT
+
+template <class _Atp, class _Tp>
+_LIBCPP_INLINE_VISIBILITY bool __cxx_atomic_wait(_Atp* __a, _Tp const __val, memory_order __order)
+{
+    auto const __test_fn = [=]() -> bool {
+        return !__cxx_nonatomic_compare_equal(__cxx_atomic_load(__a, __order), __val);
+    };
+    return __cxx_atomic_wait(__a, __test_fn);
+}
+
+#endif //_LIBCPP_STD_VER >= 11
+
 // general atomic<T>
 
 template <class _Tp, bool = is_integral<_Tp>::value && !is_same<_Tp, bool>::value>
@@ -1532,6 +1606,19 @@ struct __atomic_base  // false
                                  memory_order __m = memory_order_seq_cst) _NOEXCEPT
         {return __cxx_atomic_compare_exchange_strong(&__a_, &__e, __d, __m, __m);}
 
+    _LIBCPP_INLINE_VISIBILITY void wait(_Tp __v, memory_order __m = memory_order_seq_cst) const volatile _NOEXCEPT
+        {__cxx_atomic_wait(&__a_, __v, __m);}
+    _LIBCPP_INLINE_VISIBILITY void wait(_Tp __v, memory_order __m = memory_order_seq_cst) const _NOEXCEPT
+        {__cxx_atomic_wait(&__a_, __v, __m);}
+    _LIBCPP_INLINE_VISIBILITY void notify_one() volatile _NOEXCEPT
+        {__cxx_atomic_notify_one(&__a_);}
+    _LIBCPP_INLINE_VISIBILITY void notify_one() _NOEXCEPT
+        {__cxx_atomic_notify_one(&__a_);}
+    _LIBCPP_INLINE_VISIBILITY void notify_all() volatile _NOEXCEPT
+        {__cxx_atomic_notify_all(&__a_);}
+    _LIBCPP_INLINE_VISIBILITY void notify_all() _NOEXCEPT
+        {__cxx_atomic_notify_all(&__a_);}
+
     _LIBCPP_INLINE_VISIBILITY
     __atomic_base() _NOEXCEPT _LIBCPP_DEFAULT
 
@@ -1544,8 +1631,11 @@ struct __atomic_base  // false
     __atomic_base& operator=(const __atomic_base&) volatile = delete;
 #else
 private:
+    _LIBCPP_INLINE_VISIBILITY
     __atomic_base(const __atomic_base&);
+    _LIBCPP_INLINE_VISIBILITY
     __atomic_base& operator=(const __atomic_base&);
+    _LIBCPP_INLINE_VISIBILITY
     __atomic_base& operator=(const __atomic_base&) volatile;
 #endif
 };
@@ -1643,6 +1733,7 @@ struct atomic
     : public __atomic_base<_Tp>
 {
     typedef __atomic_base<_Tp> __base;
+    typedef _Tp value_type;
     _LIBCPP_INLINE_VISIBILITY
     atomic() _NOEXCEPT _LIBCPP_DEFAULT
     _LIBCPP_INLINE_VISIBILITY
@@ -1663,6 +1754,7 @@ struct atomic<_Tp*>
     : public __atomic_base<_Tp*>
 {
     typedef __atomic_base<_Tp*> __base;
+    typedef _Tp* value_type;
     _LIBCPP_INLINE_VISIBILITY
     atomic() _NOEXCEPT _LIBCPP_DEFAULT
     _LIBCPP_INLINE_VISIBILITY
@@ -1947,6 +2039,76 @@ atomic_compare_exchange_strong_explicit(atomic<_Tp>* __o, _Tp* __e,
     return __o->compare_exchange_strong(*__e, __d, __s, __f);
 }
 
+// atomic_wait
+
+template <class _Tp>
+_LIBCPP_INLINE_VISIBILITY
+void atomic_wait(const volatile atomic<_Tp>* __o,
+                 typename atomic<_Tp>::value_type __v) _NOEXCEPT
+{
+    return __o->wait(__v);
+}
+
+template <class _Tp>
+_LIBCPP_INLINE_VISIBILITY
+void atomic_wait(const atomic<_Tp>* __o,
+                 typename atomic<_Tp>::value_type __v) _NOEXCEPT
+{
+    return __o->wait(__v);
+}
+
+// atomic_wait_explicit
+
+template <class _Tp>
+_LIBCPP_INLINE_VISIBILITY
+void atomic_wait_explicit(const volatile atomic<_Tp>* __o,
+                          typename atomic<_Tp>::value_type __v,
+                          memory_order __m) _NOEXCEPT
+  _LIBCPP_CHECK_LOAD_MEMORY_ORDER(__m)
+{
+    return __o->wait(__v, __m);
+}
+
+template <class _Tp>
+_LIBCPP_INLINE_VISIBILITY
+void atomic_wait_explicit(const atomic<_Tp>* __o,
+                          typename atomic<_Tp>::value_type __v,
+                          memory_order __m) _NOEXCEPT
+  _LIBCPP_CHECK_LOAD_MEMORY_ORDER(__m)
+{
+    return __o->wait(__v, __m);
+}
+
+// atomic_notify_one
+
+template <class _Tp>
+_LIBCPP_INLINE_VISIBILITY
+void atomic_notify_one(volatile atomic<_Tp>* __o) _NOEXCEPT
+{
+    __o->notify_one();
+}
+template <class _Tp>
+_LIBCPP_INLINE_VISIBILITY
+void atomic_notify_one(atomic<_Tp>* __o) _NOEXCEPT
+{
+    __o->notify_one();
+}
+
+// atomic_notify_one
+
+template <class _Tp>
+_LIBCPP_INLINE_VISIBILITY
+void atomic_notify_all(volatile atomic<_Tp>* __o) _NOEXCEPT
+{
+    __o->notify_all();
+}
+template <class _Tp>
+_LIBCPP_INLINE_VISIBILITY
+void atomic_notify_all(atomic<_Tp>* __o) _NOEXCEPT
+{
+    __o->notify_all();
+}
+
 // atomic_fetch_add
 
 template <class _Tp>
@@ -2279,6 +2441,13 @@ typedef struct atomic_flag
 {
     __cxx_atomic_impl<_LIBCPP_ATOMIC_FLAG_TYPE> __a_;
 
+    _LIBCPP_INLINE_VISIBILITY
+    bool test(memory_order __m = memory_order_seq_cst) const volatile _NOEXCEPT
+        {return _LIBCPP_ATOMIC_FLAG_TYPE(true) == __cxx_atomic_load(&__a_, __m);}
+    _LIBCPP_INLINE_VISIBILITY
+    bool test(memory_order __m = memory_order_seq_cst) const _NOEXCEPT
+        {return _LIBCPP_ATOMIC_FLAG_TYPE(true) == __cxx_atomic_load(&__a_, __m);}
+
     _LIBCPP_INLINE_VISIBILITY
     bool test_and_set(memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT
         {return __cxx_atomic_exchange(&__a_, _LIBCPP_ATOMIC_FLAG_TYPE(true), __m);}
@@ -2292,6 +2461,25 @@ typedef struct atomic_flag
     void clear(memory_order __m = memory_order_seq_cst) _NOEXCEPT
         {__cxx_atomic_store(&__a_, _LIBCPP_ATOMIC_FLAG_TYPE(false), __m);}
 
+    _LIBCPP_INLINE_VISIBILITY
+    void wait(bool __v, memory_order __m = memory_order_seq_cst) const volatile _NOEXCEPT
+        {__cxx_atomic_wait(&__a_, _LIBCPP_ATOMIC_FLAG_TYPE(__v), __m);}
+    _LIBCPP_INLINE_VISIBILITY
+    void wait(bool __v, memory_order __m = memory_order_seq_cst) const _NOEXCEPT
+        {__cxx_atomic_wait(&__a_, _LIBCPP_ATOMIC_FLAG_TYPE(__v), __m);}
+    _LIBCPP_INLINE_VISIBILITY
+    void notify_one() volatile _NOEXCEPT
+        {__cxx_atomic_notify_one(&__a_);}
+    _LIBCPP_INLINE_VISIBILITY
+    void notify_one() _NOEXCEPT
+        {__cxx_atomic_notify_one(&__a_);}
+    _LIBCPP_INLINE_VISIBILITY
+    void notify_all() volatile _NOEXCEPT
+        {__cxx_atomic_notify_all(&__a_);}
+    _LIBCPP_INLINE_VISIBILITY
+    void notify_all() _NOEXCEPT
+        {__cxx_atomic_notify_all(&__a_);}
+
     _LIBCPP_INLINE_VISIBILITY
     atomic_flag() _NOEXCEPT _LIBCPP_DEFAULT
 
@@ -2304,12 +2492,44 @@ typedef struct atomic_flag
     atomic_flag& operator=(const atomic_flag&) volatile = delete;
 #else
 private:
+    _LIBCPP_INLINE_VISIBILITY
     atomic_flag(const atomic_flag&);
+    _LIBCPP_INLINE_VISIBILITY
     atomic_flag& operator=(const atomic_flag&);
+    _LIBCPP_INLINE_VISIBILITY
     atomic_flag& operator=(const atomic_flag&) volatile;
 #endif
 } atomic_flag;
 
+
+inline _LIBCPP_INLINE_VISIBILITY
+bool
+atomic_flag_test(const volatile atomic_flag* __o) _NOEXCEPT
+{
+    return __o->test();
+}
+
+inline _LIBCPP_INLINE_VISIBILITY
+bool
+atomic_flag_test(const atomic_flag* __o) _NOEXCEPT
+{
+    return __o->test();
+}
+
+inline _LIBCPP_INLINE_VISIBILITY
+bool
+atomic_flag_test_explicit(const volatile atomic_flag* __o, memory_order __m) _NOEXCEPT
+{
+    return __o->test(__m);
+}
+
+inline _LIBCPP_INLINE_VISIBILITY
+bool
+atomic_flag_test_explicit(const atomic_flag* __o, memory_order __m) _NOEXCEPT
+{
+    return __o->test(__m);
+}
+
 inline _LIBCPP_INLINE_VISIBILITY
 bool
 atomic_flag_test_and_set(volatile atomic_flag* __o) _NOEXCEPT
@@ -2366,6 +2586,64 @@ atomic_flag_clear_explicit(atomic_flag* __o, memory_order __m) _NOEXCEPT
     __o->clear(__m);
 }
 
+inline _LIBCPP_INLINE_VISIBILITY
+void
+atomic_flag_wait(const volatile atomic_flag* __o, bool __v) _NOEXCEPT
+{
+    __o->wait(__v);
+}
+
+inline _LIBCPP_INLINE_VISIBILITY
+void
+atomic_flag_wait(const atomic_flag* __o, bool __v) _NOEXCEPT
+{
+    __o->wait(__v);
+}
+
+inline _LIBCPP_INLINE_VISIBILITY
+void
+atomic_flag_wait_explicit(const volatile atomic_flag* __o,
+                          bool __v, memory_order __m) _NOEXCEPT
+{
+    __o->wait(__v, __m);
+}
+
+inline _LIBCPP_INLINE_VISIBILITY
+void
+atomic_flag_wait_explicit(const atomic_flag* __o,
+                          bool __v, memory_order __m) _NOEXCEPT
+{
+    __o->wait(__v, __m);
+}
+
+inline _LIBCPP_INLINE_VISIBILITY
+void
+atomic_flag_notify_one(volatile atomic_flag* __o) _NOEXCEPT
+{
+    __o->notify_one();
+}
+
+inline _LIBCPP_INLINE_VISIBILITY
+void
+atomic_flag_notify_one(atomic_flag* __o) _NOEXCEPT
+{
+    __o->notify_one();
+}
+
+inline _LIBCPP_INLINE_VISIBILITY
+void
+atomic_flag_notify_all(volatile atomic_flag* __o) _NOEXCEPT
+{
+    __o->notify_all();
+}
+
+inline _LIBCPP_INLINE_VISIBILITY
+void
+atomic_flag_notify_all(atomic_flag* __o) _NOEXCEPT
+{
+    __o->notify_all();
+}
+
 // fences
 
 inline _LIBCPP_INLINE_VISIBILITY
@@ -2434,6 +2712,33 @@ typedef atomic<ptr
diff _t> atomic_ptr
diff _t;
 typedef atomic<intmax_t>  atomic_intmax_t;
 typedef atomic<uintmax_t> atomic_uintmax_t;
 
+// atomic_*_lock_free : prefer the contention type most highly, then the largest lock-free type
+
+#ifdef __cpp_lib_atomic_is_always_lock_free
+# define _LIBCPP_CONTENTION_LOCK_FREE __atomic_always_lock_free(sizeof(__cxx_contention_t), 0)
+#else
+# define _LIBCPP_CONTENTION_LOCK_FREE false
+#endif
+
+#if ATOMIC_LLONG_LOCK_FREE == 2
+typedef conditional<_LIBCPP_CONTENTION_LOCK_FREE, __cxx_contention_t, long long>::type          __libcpp_signed_lock_free;
+typedef conditional<_LIBCPP_CONTENTION_LOCK_FREE, __cxx_contention_t, unsigned long long>::type __libcpp_unsigned_lock_free;
+#elif ATOMIC_INT_LOCK_FREE == 2
+typedef conditional<_LIBCPP_CONTENTION_LOCK_FREE, __cxx_contention_t, int>::type                __libcpp_signed_lock_free;
+typedef conditional<_LIBCPP_CONTENTION_LOCK_FREE, __cxx_contention_t, unsigned int>::type       __libcpp_unsigned_lock_free;
+#elif ATOMIC_SHORT_LOCK_FREE == 2
+typedef conditional<_LIBCPP_CONTENTION_LOCK_FREE, __cxx_contention_t, short>::type              __libcpp_signed_lock_free;
+typedef conditional<_LIBCPP_CONTENTION_LOCK_FREE, __cxx_contention_t, unsigned short>::type     __libcpp_unsigned_lock_free;
+#elif ATOMIC_CHAR_LOCK_FREE == 2
+typedef conditional<_LIBCPP_CONTENTION_LOCK_FREE, __cxx_contention_t, char>::type               __libcpp_signed_lock_free;
+typedef conditional<_LIBCPP_CONTENTION_LOCK_FREE, __cxx_contention_t, unsigned char>::type      __libcpp_unsigned_lock_free;
+#else
+    // No signed/unsigned lock-free types
+#endif
+
+typedef atomic<__libcpp_signed_lock_free> atomic_signed_lock_free;
+typedef atomic<__libcpp_unsigned_lock_free> atomic_unsigned_lock_free;
+
 #define ATOMIC_FLAG_INIT {false}
 #define ATOMIC_VAR_INIT(__v) {__v}
 

diff  --git a/libcxx/include/barrier b/libcxx/include/barrier
new file mode 100644
index 000000000000..bf2b9a88297a
--- /dev/null
+++ b/libcxx/include/barrier
@@ -0,0 +1,322 @@
+// -*- C++ -*-
+//===--------------------------- barrier ----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_BARRIER
+#define _LIBCPP_BARRIER
+
+/*
+    barrier synopsis
+
+namespace std
+{
+
+  template<class CompletionFunction = see below>
+  class barrier
+  {
+  public:
+    using arrival_token = see below;
+
+    constexpr explicit barrier(ptr
diff _t phase_count,
+                               CompletionFunction f = CompletionFunction());
+    ~barrier();
+
+    barrier(const barrier&) = delete;
+    barrier& operator=(const barrier&) = delete;
+
+    [[nodiscard]] arrival_token arrive(ptr
diff _t update = 1);
+    void wait(arrival_token&& arrival) const;
+
+    void arrive_and_wait();
+    void arrive_and_drop();
+
+  private:
+    CompletionFunction completion; // exposition only
+  };
+
+}
+
+*/
+
+#include <__config>
+#include <atomic>
+#ifndef _LIBCPP_HAS_NO_TREE_BARRIER
+# include <memory>
+#endif
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#pragma GCC system_header
+#endif
+
+#ifdef _LIBCPP_HAS_NO_THREADS
+# error <barrier> is not supported on this single threaded system
+#endif
+
+#if _LIBCPP_STD_VER < 14
+# error <barrier> requires C++14 or later
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+struct __empty_completion
+{
+    inline _LIBCPP_INLINE_VISIBILITY
+    void operator()() noexcept
+    {
+    }
+};
+
+#ifndef _LIBCPP_HAS_NO_TREE_BARRIER
+
+/*
+
+The default implementation of __barrier_base is a classic tree barrier.
+
+It looks 
diff erent from literature pseudocode for two main reasons:
+ 1. Threads that call into std::barrier functions do not provide indices,
+    so a numbering step is added before the actual barrier algorithm,
+    appearing as an N+1 round to the N rounds of the tree barrier.
+ 2. A great deal of attention has been paid to avoid cache line thrashing
+    by flattening the tree structure into cache-line sized arrays, that
+    are indexed in an efficient way.
+
+*/
+
+using __barrier_phase_t = uint8_t;
+
+class __barrier_algorithm_base;
+
+_LIBCPP_EXPORTED_FROM_ABI
+__barrier_algorithm_base* __construct_barrier_algorithm_base(ptr
diff _t& __expected);
+
+_LIBCPP_EXPORTED_FROM_ABI
+bool __arrive_barrier_algorithm_base(__barrier_algorithm_base* __barrier,
+                                     __barrier_phase_t __old_phase);
+
+_LIBCPP_EXPORTED_FROM_ABI
+void __destroy_barrier_algorithm_base(__barrier_algorithm_base* __barrier);
+
+template<class _CompletionF>
+class __barrier_base {
+
+    ptr
diff _t                                               __expected;
+    unique_ptr<__barrier_algorithm_base,
+               decltype(&__destroy_barrier_algorithm_base)> __base;
+    __atomic_base<ptr
diff _t>                                __expected_adjustment;
+    _CompletionF                                            __completion;
+    __atomic_base<__barrier_phase_t>                        __phase;
+
+public:
+    using arrival_token = __barrier_phase_t;
+
+    static constexpr ptr
diff _t max() noexcept {
+        return numeric_limits<ptr
diff _t>::max();
+    }
+
+    _LIBCPP_INLINE_VISIBILITY
+    __barrier_base(ptr
diff _t __expected, _CompletionF __completion = _CompletionF())
+            : __expected(__expected), __base(__construct_barrier_algorithm_base(this->__expected),
+                                             &__destroy_barrier_algorithm_base),
+              __expected_adjustment(0), __completion(move(__completion)), __phase(0)
+    {
+    }
+    [[nodiscard]] _LIBCPP_INLINE_VISIBILITY
+    arrival_token arrive(ptr
diff _t update)
+    {
+        auto const __old_phase = __phase.load(memory_order_relaxed);
+        for(; update; --update)
+            if(__arrive_barrier_algorithm_base(__base.get(), __old_phase)) {
+                __completion();
+                __expected += __expected_adjustment.load(memory_order_relaxed);
+                __expected_adjustment.store(0, memory_order_relaxed);
+                __phase.store(__old_phase + 2, memory_order_release);
+                __phase.notify_all();
+            }
+        return __old_phase;
+    }
+    _LIBCPP_INLINE_VISIBILITY
+    void wait(arrival_token&& __old_phase) const
+    {
+        auto const __test_fn = [=]() -> bool {
+            return __phase.load(memory_order_acquire) != __old_phase;
+        };
+        __libcpp_thread_poll_with_backoff(__test_fn, __libcpp_timed_backoff_policy());
+    }
+    _LIBCPP_INLINE_VISIBILITY
+    void arrive_and_drop()
+    {
+        __expected_adjustment.fetch_sub(1, memory_order_relaxed);
+        (void)arrive(1);
+    }
+};
+
+#else
+
+/*
+
+The alternative implementation of __barrier_base is a central barrier.
+
+Two versions of this algorithm are provided:
+ 1. A fairly straightforward implementation of the litterature for the
+    general case where the completion function is not empty.
+ 2. An optimized implementation that exploits 2's complement arithmetic
+    and well-defined overflow in atomic arithmetic, to handle the phase
+    roll-over for free.
+
+*/
+
+template<class _CompletionF>
+class __barrier_base {
+
+    __atomic_base<ptr
diff _t> __expected;
+    __atomic_base<ptr
diff _t> __arrived;
+    _CompletionF             __completion;
+    __atomic_base<bool>      __phase;
+public:
+    using arrival_token = bool;
+
+    static constexpr ptr
diff _t max() noexcept {
+        return numeric_limits<ptr
diff _t>::max();
+    }
+
+    _LIBCPP_INLINE_VISIBILITY
+    __barrier_base(ptr
diff _t __expected, _CompletionF __completion = _CompletionF())
+        : __expected(__expected), __arrived(__expected), __completion(move(__completion)), __phase(false)
+    {
+    }
+    [[nodiscard]] _LIBCPP_INLINE_VISIBILITY
+    arrival_token arrive(ptr
diff _t update)
+    {
+        auto const __old_phase = __phase.load(memory_order_relaxed);
+        auto const __result = __arrived.fetch_sub(update, memory_order_acq_rel) - update;
+        auto const new_expected = __expected.load(memory_order_relaxed);
+        if(0 == __result) {
+            __completion();
+            __arrived.store(new_expected, memory_order_relaxed);
+            __phase.store(!__old_phase, memory_order_release);
+            __phase.notify_all();
+        }
+        return __old_phase;
+    }
+    _LIBCPP_INLINE_VISIBILITY
+    void wait(arrival_token&& __old_phase) const
+    {
+        __phase.wait(__old_phase, memory_order_acquire);
+    }
+    _LIBCPP_INLINE_VISIBILITY
+    void arrive_and_drop()
+    {
+        __expected.fetch_sub(1, memory_order_relaxed);
+        (void)arrive(1);
+    }
+};
+
+template<>
+class __barrier_base<__empty_completion> {
+
+    static constexpr uint64_t __expected_unit = 1ull;
+    static constexpr uint64_t __arrived_unit = 1ull << 32;
+    static constexpr uint64_t __expected_mask = __arrived_unit - 1;
+    static constexpr uint64_t __phase_bit = 1ull << 63;
+    static constexpr uint64_t __arrived_mask = (__phase_bit - 1) & ~__expected_mask;
+
+    __atomic_base<uint64_t>   __phase_arrived_expected;
+
+    static _LIBCPP_INLINE_VISIBILITY
+    constexpr uint64_t __init(ptr
diff _t __count) _NOEXCEPT
+    {
+        return ((uint64_t(1u << 31) - __count) << 32)
+              | (uint64_t(1u << 31) - __count);
+    }
+
+public:
+    using arrival_token = uint64_t;
+
+    static constexpr ptr
diff _t max() noexcept {
+        return ptr
diff _t(1u << 31) - 1;
+    }
+
+    _LIBCPP_INLINE_VISIBILITY
+    explicit inline __barrier_base(ptr
diff _t __count, __empty_completion = __empty_completion())
+        : __phase_arrived_expected(__init(__count))
+    {
+    }
+    [[nodiscard]] inline _LIBCPP_INLINE_VISIBILITY
+    arrival_token arrive(ptr
diff _t update)
+    {
+        auto const __inc = __arrived_unit * update;
+        auto const __old = __phase_arrived_expected.fetch_add(__inc, memory_order_acq_rel);
+        if((__old ^ (__old + __inc)) & __phase_bit) {
+            __phase_arrived_expected.fetch_add((__old & __expected_mask) << 32, memory_order_relaxed);
+            __phase_arrived_expected.notify_all();
+        }
+        return __old & __phase_bit;
+    }
+    inline _LIBCPP_INLINE_VISIBILITY
+    void wait(arrival_token&& __phase) const
+    {
+        auto const __test_fn = [=]() -> bool {
+            uint64_t const __current = __phase_arrived_expected.load(memory_order_acquire);
+            return ((__current & __phase_bit) != __phase);
+        };
+        __libcpp_thread_poll_with_backoff(__test_fn, __libcpp_timed_backoff_policy());
+    }
+    inline _LIBCPP_INLINE_VISIBILITY
+    void arrive_and_drop()
+    {
+        __phase_arrived_expected.fetch_add(__expected_unit, memory_order_relaxed);
+        (void)arrive(1);
+    }
+};
+
+#endif //_LIBCPP_HAS_NO_TREE_BARRIER
+
+template<class _CompletionF = __empty_completion>
+class barrier {
+
+    __barrier_base<_CompletionF> __b;
+public:
+    using arrival_token = typename __barrier_base<_CompletionF>::arrival_token;
+
+    static constexpr ptr
diff _t max() noexcept {
+        return __barrier_base<_CompletionF>::max();
+    }
+
+    _LIBCPP_INLINE_VISIBILITY
+    barrier(ptr
diff _t __count, _CompletionF __completion = _CompletionF())
+        : __b(__count, std::move(__completion)) {
+    }
+
+    barrier(barrier const&) = delete;
+    barrier& operator=(barrier const&) = delete;
+
+    [[nodiscard]] _LIBCPP_INLINE_VISIBILITY
+    arrival_token arrive(ptr
diff _t update = 1)
+    {
+        return __b.arrive(update);
+    }
+    _LIBCPP_INLINE_VISIBILITY
+    void wait(arrival_token&& __phase) const
+    {
+        __b.wait(std::move(__phase));
+    }
+	_LIBCPP_INLINE_VISIBILITY
+    void arrive_and_wait()
+    {
+        wait(arrive());
+	}
+    _LIBCPP_INLINE_VISIBILITY
+    void arrive_and_drop()
+    {
+        __b.arrive_and_drop();
+    }
+};
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif //_LIBCPP_BARRIER

diff  --git a/libcxx/include/latch b/libcxx/include/latch
new file mode 100644
index 000000000000..7c8b2f52205b
--- /dev/null
+++ b/libcxx/include/latch
@@ -0,0 +1,104 @@
+// -*- C++ -*-
+//===--------------------------- latch -----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_LATCH
+#define _LIBCPP_LATCH
+
+/*
+    latch synopsis
+
+namespace std
+{
+
+  class latch
+  {
+  public:
+    constexpr explicit latch(ptr
diff _t __expected);
+    ~latch();
+
+    latch(const latch&) = delete;
+    latch& operator=(const latch&) = delete;
+
+    void count_down(ptr
diff _t __update = 1);
+    bool try_wait() const noexcept;
+    void wait() const;
+    void arrive_and_wait(ptr
diff _t __update = 1);
+
+  private:
+    ptr
diff _t __counter; // exposition only
+  };
+
+}
+
+*/
+
+#include <__config>
+#include <atomic>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#pragma GCC system_header
+#endif
+
+#ifdef _LIBCPP_HAS_NO_THREADS
+# error <latch> is not supported on this single threaded system
+#endif
+
+#if _LIBCPP_STD_VER < 14
+# error <latch> requires C++14 or later
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+class latch
+{
+    __atomic_base<ptr
diff _t> __a;
+
+public:
+    static constexpr ptr
diff _t max() noexcept {
+        return numeric_limits<ptr
diff _t>::max();
+    }
+
+    inline _LIBCPP_INLINE_VISIBILITY
+    constexpr explicit latch(ptr
diff _t __expected) : __a(__expected) { }
+
+    ~latch() = default;
+    latch(const latch&) = delete;
+    latch& operator=(const latch&) = delete;
+
+    inline _LIBCPP_INLINE_VISIBILITY
+    void count_down(ptr
diff _t __update = 1)
+    {
+        auto const __old = __a.fetch_sub(__update, memory_order_release);
+        if(__old == __update)
+            __a.notify_all();
+    }
+    inline _LIBCPP_INLINE_VISIBILITY
+    bool try_wait() const noexcept
+    {
+        return 0 == __a.load(memory_order_acquire);
+    }
+    inline _LIBCPP_INLINE_VISIBILITY
+    void wait() const
+    {
+        auto const __test_fn = [=]() -> bool {
+            return try_wait();
+        };
+        __cxx_atomic_wait(&__a.__a_, __test_fn);
+    }
+    inline _LIBCPP_INLINE_VISIBILITY
+    void arrive_and_wait(ptr
diff _t __update = 1)
+    {
+        count_down(__update);
+        wait();
+    }
+};
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif //_LIBCPP_LATCH

diff  --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index 31d39ddf7c8a..698cc193de53 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -231,6 +231,10 @@ module std [system] {
     header "atomic"
     export *
   }
+  module barrier {
+    header "barrier"
+    export *
+  }
   module bit {
     header "bit"
     export *
@@ -334,6 +338,10 @@ module std [system] {
     header "iterator"
     export *
   }
+  module latch {
+    header "latch"
+    export *
+  }
   module limits {
     header "limits"
     export *
@@ -400,6 +408,10 @@ module std [system] {
     header "scoped_allocator"
     export *
   }
+  module semaphore {
+    header "semaphore"
+    export *
+  }
   module set {
     header "set"
     export initializer_list

diff  --git a/libcxx/include/semaphore b/libcxx/include/semaphore
new file mode 100644
index 000000000000..77e3797f7ac6
--- /dev/null
+++ b/libcxx/include/semaphore
@@ -0,0 +1,233 @@
+// -*- C++ -*-
+//===--------------------------- semaphore --------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_SEMAPHORE
+#define _LIBCPP_SEMAPHORE
+
+/*
+    semaphore synopsis
+
+namespace std {
+
+template<ptr
diff _t least_max_value = implementation-defined>
+class counting_semaphore
+{
+public:
+static constexpr ptr
diff _t max() noexcept;
+
+constexpr explicit counting_semaphore(ptr
diff _t desired);
+~counting_semaphore();
+
+counting_semaphore(const counting_semaphore&) = delete;
+counting_semaphore& operator=(const counting_semaphore&) = delete;
+
+void release(ptr
diff _t update = 1);
+void acquire();
+bool try_acquire() noexcept;
+template<class Rep, class Period>
+    bool try_acquire_for(const chrono::duration<Rep, Period>& rel_time);
+template<class Clock, class Duration>
+    bool try_acquire_until(const chrono::time_point<Clock, Duration>& abs_time);
+
+private:
+ptr
diff _t counter; // exposition only
+};
+
+using binary_semaphore = counting_semaphore<1>;
+
+}
+
+*/
+
+#include <__config>
+#include <__threading_support>
+#include <atomic>
+#include <cassert>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#pragma GCC system_header
+#endif
+
+#ifdef _LIBCPP_HAS_NO_THREADS
+# error <semaphore> is not supported on this single threaded system
+#endif
+
+#if _LIBCPP_STD_VER < 14
+# error <semaphore> is requires C++14 or later
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+/*
+
+__atomic_semaphore_base is the general-case implementation, to be used for
+user-requested least-max values that exceed the OS implementation support
+(incl. when the OS has no support of its own) and for binary semaphores.
+
+It is a typical Dijsktra semaphore algorithm over atomics, wait and notify
+functions. It avoids contention against users' own use of those facilities.
+
+*/
+
+class __atomic_semaphore_base
+{
+    __atomic_base<ptr
diff _t> __a;
+
+public:
+    _LIBCPP_INLINE_VISIBILITY
+    __atomic_semaphore_base(ptr
diff _t __count) : __a(__count)
+    {
+    }
+    _LIBCPP_INLINE_VISIBILITY
+    void release(ptr
diff _t __update = 1)
+    {
+        if(0 < __a.fetch_add(__update, memory_order_release))
+            ;
+        else if(__update > 1)
+            __a.notify_all();
+        else
+            __a.notify_one();
+    }
+    _LIBCPP_INLINE_VISIBILITY
+    void acquire()
+    {
+        auto const __test_fn = [=]() -> bool {
+            auto __old = __a.load(memory_order_relaxed);
+            return (__old != 0) && __a.compare_exchange_strong(__old, __old - 1, memory_order_acquire, memory_order_relaxed);
+        };
+        __cxx_atomic_wait(&__a.__a_, __test_fn);
+    }
+    template <class Rep, class Period>
+    _LIBCPP_INLINE_VISIBILITY
+    bool try_acquire_for(chrono::duration<Rep, Period> const& __rel_time)
+    {
+        auto const __test_fn = [=]() -> bool {
+            auto __old = __a.load(memory_order_acquire);
+            while(1) {
+                if (__old == 0)
+                    return false;
+                if(__a.compare_exchange_strong(__old, __old - 1, memory_order_acquire, memory_order_relaxed))
+                    return true;
+            }
+        };
+        return __libcpp_thread_poll_with_backoff(__test_fn, __libcpp_timed_backoff_policy(), __rel_time);
+    }
+};
+
+#ifndef _LIBCPP_NO_NATIVE_SEMAPHORES
+
+/*
+
+__platform_semaphore_base a simple wrapper for the OS semaphore type. That
+is, every call is routed to the OS in the most direct manner possible.
+
+*/
+
+class __platform_semaphore_base
+{
+    __libcpp_semaphore_t __semaphore;
+
+public:
+    _LIBCPP_INLINE_VISIBILITY
+    __platform_semaphore_base(ptr
diff _t __count) :
+        __semaphore()
+    {
+        __libcpp_semaphore_init(&__semaphore, __count);
+    }
+    _LIBCPP_INLINE_VISIBILITY
+    ~__platform_semaphore_base() {
+        __libcpp_semaphore_destroy(&__semaphore);
+    }
+    _LIBCPP_INLINE_VISIBILITY
+    void release(ptr
diff _t __update)
+    {
+        for(; __update; --__update)
+            __libcpp_semaphore_post(&__semaphore);
+    }
+    _LIBCPP_INLINE_VISIBILITY
+    void acquire()
+    {
+        __libcpp_semaphore_wait(&__semaphore);
+    }
+    _LIBCPP_INLINE_VISIBILITY
+    bool try_acquire_for(chrono::nanoseconds __rel_time)
+    {
+        return __libcpp_semaphore_wait_timed(&__semaphore, __rel_time);
+    }
+};
+
+template<ptr
diff _t __least_max_value>
+using __semaphore_base =
+    typename conditional<(__least_max_value > 1 && __least_max_value <= _LIBCPP_SEMAPHORE_MAX),
+                         __platform_semaphore_base,
+                         __atomic_semaphore_base>::type;
+
+#else
+
+template<ptr
diff _t __least_max_value>
+using __semaphore_base =
+    __atomic_semaphore_base;
+
+#endif //_LIBCPP_NO_NATIVE_SEMAPHORES
+
+template<ptr
diff _t __least_max_value = _LIBCPP_SEMAPHORE_MAX>
+class counting_semaphore
+{
+    __semaphore_base<__least_max_value> __semaphore;
+
+public:
+    static constexpr ptr
diff _t max() noexcept {
+        return __least_max_value;
+    }
+
+    _LIBCPP_INLINE_VISIBILITY
+    counting_semaphore(ptr
diff _t __count = 0) : __semaphore(__count) { }
+    ~counting_semaphore() = default;
+
+    counting_semaphore(const counting_semaphore&) = delete;
+    counting_semaphore& operator=(const counting_semaphore&) = delete;
+
+    _LIBCPP_INLINE_VISIBILITY
+    void release(ptr
diff _t __update = 1)
+    {
+        __semaphore.release(__update);
+    }
+    _LIBCPP_INLINE_VISIBILITY
+    void acquire()
+    {
+        __semaphore.acquire();
+    }
+    template<class Rep, class Period>
+    _LIBCPP_INLINE_VISIBILITY
+    bool try_acquire_for(chrono::duration<Rep, Period> const& __rel_time)
+    {
+        return __semaphore.try_acquire_for(chrono::duration_cast<chrono::nanoseconds>(__rel_time));
+    }
+    _LIBCPP_INLINE_VISIBILITY
+    bool try_acquire()
+    {
+        return try_acquire_for(chrono::nanoseconds::zero());
+    }
+    template <class Clock, class Duration>
+    _LIBCPP_INLINE_VISIBILITY
+    bool try_acquire_until(chrono::time_point<Clock, Duration> const& __abs_time)
+    {
+        auto const current = Clock::now();
+        if(current >= __abs_time)
+            return try_acquire();
+        else
+            return try_acquire_for(__abs_time - current);
+    }
+};
+
+using binary_semaphore = counting_semaphore<1>;
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif //_LIBCPP_SEMAPHORE

diff  --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index 120505fe18da..28af7b92430f 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -4,6 +4,8 @@ set(LIBCXX_LIB_CMAKEFILES_DIR "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTOR
 set(LIBCXX_SOURCES
   algorithm.cpp
   any.cpp
+  atomic.cpp
+  barrier.cpp
   bind.cpp
   charconv.cpp
   chrono.cpp

diff  --git a/libcxx/src/atomic.cpp b/libcxx/src/atomic.cpp
new file mode 100644
index 000000000000..65d4837bb4a7
--- /dev/null
+++ b/libcxx/src/atomic.cpp
@@ -0,0 +1,189 @@
+//===------------------------- atomic.cpp ---------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <__config>
+#ifndef _LIBCPP_HAS_NO_THREADS
+
+#include <climits>
+#include <atomic>
+#include <functional>
+
+#include <iostream>
+
+#ifdef __linux__
+
+#include <unistd.h>
+#include <linux/futex.h>
+#include <sys/syscall.h>
+
+#else // <- Add other operating systems here
+
+// Baseline needs no new headers
+
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#ifdef __linux__
+
+static void __libcpp_platform_wait_on_address(__cxx_atomic_contention_t const volatile* __ptr,
+                                              __cxx_contention_t __val)
+{
+    static constexpr timespec __timeout = { 2, 0 };
+    syscall(SYS_futex, __ptr, FUTEX_WAIT_PRIVATE, __val, &__timeout, 0, 0);
+}
+
+static void __libcpp_platform_wake_by_address(__cxx_atomic_contention_t const volatile* __ptr,
+                                              bool __notify_one)
+{
+    syscall(SYS_futex, __ptr, FUTEX_WAKE_PRIVATE, __notify_one ? 1 : INT_MAX, 0, 0, 0);
+}
+
+#elif defined(__APPLE__) && defined(_LIBCPP_USE_ULOCK)
+
+extern "C" int __ulock_wait(uint32_t operation, void *addr, uint64_t value,
+		uint32_t timeout); /* timeout is specified in microseconds */
+extern "C" int __ulock_wake(uint32_t operation, void *addr, uint64_t wake_value);
+
+#define UL_COMPARE_AND_WAIT				1
+#define ULF_WAKE_ALL					0x00000100
+
+static void __libcpp_platform_wait_on_address(__cxx_atomic_contention_t const volatile* __ptr,
+                                              __cxx_contention_t __val)
+{
+    __ulock_wait(UL_COMPARE_AND_WAIT,
+                 const_cast<__cxx_atomic_contention_t*>(__ptr), __val, 0);
+}
+
+static void __libcpp_platform_wake_by_address(__cxx_atomic_contention_t const volatile* __ptr,
+                                              bool __notify_one)
+{
+    __ulock_wake(UL_COMPARE_AND_WAIT | (__notify_one ? 0 : ULF_WAKE_ALL),
+                 const_cast<__cxx_atomic_contention_t*>(__ptr), 0);
+}
+
+#else // <- Add other operating systems here
+
+// Baseline is just a timed backoff
+
+static void __libcpp_platform_wait_on_address(__cxx_atomic_contention_t const volatile* __ptr,
+                                              __cxx_contention_t __val)
+{
+    __libcpp_thread_poll_with_backoff([=]() -> bool {
+        return !__cxx_nonatomic_compare_equal(__cxx_atomic_load(__ptr, memory_order_relaxed), __val);
+    }, __libcpp_timed_backoff_policy());
+}
+
+static void __libcpp_platform_wake_by_address(__cxx_atomic_contention_t const volatile*, bool) { }
+
+#endif // __linux__
+
+static constexpr size_t __libcpp_contention_table_size = (1 << 8);  /* < there's no magic in this number */
+
+struct alignas(64) /*  aim to avoid false sharing */ __libcpp_contention_table_entry
+{
+    __cxx_atomic_contention_t __contention_state;
+    __cxx_atomic_contention_t __platform_state;
+    inline constexpr __libcpp_contention_table_entry() :
+        __contention_state(0), __platform_state(0) { }
+};
+
+static __libcpp_contention_table_entry __libcpp_contention_table[ __libcpp_contention_table_size ];
+
+static hash<void const volatile*> __libcpp_contention_hasher;
+
+static __libcpp_contention_table_entry* __libcpp_contention_state(void const volatile * p)
+{
+    return &__libcpp_contention_table[__libcpp_contention_hasher(p) & (__libcpp_contention_table_size - 1)];
+}
+
+/* Given an atomic to track contention and an atomic to actually wait on, which may be
+   the same atomic, we try to detect contention to avoid spuriously calling the platform. */
+
+static void __libcpp_contention_notify(__cxx_atomic_contention_t volatile* __contention_state,
+                                       __cxx_atomic_contention_t const volatile* __platform_state,
+                                       bool __notify_one)
+{
+    if(0 != __cxx_atomic_load(__contention_state, memory_order_seq_cst))
+        // We only call 'wake' if we consumed a contention bit here.
+        __libcpp_platform_wake_by_address(__platform_state, __notify_one);
+}
+static __cxx_contention_t __libcpp_contention_monitor_for_wait(__cxx_atomic_contention_t volatile* __contention_state,
+                                                               __cxx_atomic_contention_t const volatile* __platform_state)
+{
+    // We will monitor this value.
+    return __cxx_atomic_load(__platform_state, memory_order_acquire);
+}
+static void __libcpp_contention_wait(__cxx_atomic_contention_t volatile* __contention_state,
+                                     __cxx_atomic_contention_t const volatile* __platform_state,
+                                     __cxx_contention_t __old_value)
+{
+    __cxx_atomic_fetch_add(__contention_state, __cxx_contention_t(1), memory_order_seq_cst);
+    // We sleep as long as the monitored value hasn't changed.
+    __libcpp_platform_wait_on_address(__platform_state, __old_value);
+    __cxx_atomic_fetch_sub(__contention_state, __cxx_contention_t(1), memory_order_release);
+}
+
+/* When the incoming atomic is the wrong size for the platform wait size, need to
+   launder the value sequence through an atomic from our table. */
+
+static void __libcpp_atomic_notify(void const volatile* __location)
+{
+    auto const __entry = __libcpp_contention_state(__location);
+    // The value sequence laundering happens on the next line below.
+    __cxx_atomic_fetch_add(&__entry->__platform_state, __cxx_contention_t(1), memory_order_release);
+    __libcpp_contention_notify(&__entry->__contention_state,
+                               &__entry->__platform_state,
+                               false /* when laundering, we can't handle notify_one */);
+}
+_LIBCPP_EXPORTED_FROM_ABI
+void __cxx_atomic_notify_one(void const volatile* __location)
+    { __libcpp_atomic_notify(__location); }
+_LIBCPP_EXPORTED_FROM_ABI
+void __cxx_atomic_notify_all(void const volatile* __location)
+    { __libcpp_atomic_notify(__location); }
+_LIBCPP_EXPORTED_FROM_ABI
+__cxx_contention_t __libcpp_atomic_monitor(void const volatile* __location)
+{
+    auto const __entry = __libcpp_contention_state(__location);
+    return __libcpp_contention_monitor_for_wait(&__entry->__contention_state, &__entry->__platform_state);
+}
+_LIBCPP_EXPORTED_FROM_ABI
+void __libcpp_atomic_wait(void const volatile* __location, __cxx_contention_t __old_value)
+{
+    auto const __entry = __libcpp_contention_state(__location);
+    __libcpp_contention_wait(&__entry->__contention_state, &__entry->__platform_state, __old_value);
+}
+
+/* When the incoming atomic happens to be the platform wait size, we still need to use the
+   table for the contention detection, but we can use the atomic directly for the wait. */
+
+_LIBCPP_EXPORTED_FROM_ABI
+void __cxx_atomic_notify_one(__cxx_atomic_contention_t const volatile* __location)
+{
+    __libcpp_contention_notify(&__libcpp_contention_state(__location)->__contention_state, __location, true);
+}
+_LIBCPP_EXPORTED_FROM_ABI
+void __cxx_atomic_notify_all(__cxx_atomic_contention_t const volatile* __location)
+{
+    __libcpp_contention_notify(&__libcpp_contention_state(__location)->__contention_state, __location, false);
+}
+_LIBCPP_EXPORTED_FROM_ABI
+__cxx_contention_t __libcpp_atomic_monitor(__cxx_atomic_contention_t const volatile* __location)
+{
+    return __libcpp_contention_monitor_for_wait(&__libcpp_contention_state(__location)->__contention_state, __location);
+}
+_LIBCPP_EXPORTED_FROM_ABI
+void __libcpp_atomic_wait(__cxx_atomic_contention_t const volatile* __location, __cxx_contention_t __old_value)
+{
+    __libcpp_contention_wait(&__libcpp_contention_state(__location)->__contention_state, __location, __old_value);
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif //_LIBCPP_HAS_NO_THREADS

diff  --git a/libcxx/src/barrier.cpp b/libcxx/src/barrier.cpp
new file mode 100644
index 000000000000..4edc2d090556
--- /dev/null
+++ b/libcxx/src/barrier.cpp
@@ -0,0 +1,95 @@
+//===------------------------- barrier.cpp ---------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <__config>
+
+#ifndef _LIBCPP_HAS_NO_THREADS
+
+#include <barrier>
+#include <thread>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if !defined(_LIBCPP_HAS_NO_TREE_BARRIER) && (_LIBCPP_STD_VER > 11)
+
+class __barrier_algorithm_base {
+public:
+    struct alignas(64) /* naturally-align the heap state */ __state_t
+    {
+        struct {
+            __atomic_base<__barrier_phase_t> __phase = ATOMIC_VAR_INIT(0);
+        } __tickets[64];
+    };
+
+    ptr
diff _t&              __expected;
+    unique_ptr<__state_t[]> __state;
+
+    _LIBCPP_HIDDEN
+    __barrier_algorithm_base(ptr
diff _t& __expected)
+        : __expected(__expected), __state(new __barrier_algorithm_base::__state_t[(__expected + 1) >> 1])
+    {
+    }
+    _LIBCPP_HIDDEN
+    bool __arrive(__barrier_phase_t __old_phase)
+    {
+        __barrier_phase_t const __half_step = __old_phase + 1,
+                                __full_step = __old_phase + 2;
+        size_t __current_expected = __expected,
+            __current = hash<thread::id>()(this_thread::get_id()) % ((__expected + 1) >> 1);
+        for(int __round = 0;; ++__round) {
+            if(__current_expected <= 1)
+                return true;
+            size_t const __end_node = ((__current_expected + 1) >> 1),
+                         __last_node = __end_node - 1;
+            for(;;++__current) {
+                if(__current == __end_node)
+                __current = 0;
+                __barrier_phase_t expect = __old_phase;
+                if(__current == __last_node && (__current_expected & 1))
+                {
+                    if(__state[__current].__tickets[__round].__phase.compare_exchange_strong(expect, __full_step, memory_order_acq_rel))
+                        break;    // I'm 1 in 1, go to next __round
+                }
+                else if(__state[__current].__tickets[__round].__phase.compare_exchange_strong(expect, __half_step, memory_order_acq_rel))
+                {
+                    return false; // I'm 1 in 2, done with arrival
+                }
+                else if(expect == __half_step)
+                {
+                    if(__state[__current].__tickets[__round].__phase.compare_exchange_strong(expect, __full_step, memory_order_acq_rel))
+                        break;    // I'm 2 in 2, go to next __round
+                }
+            }
+            __current_expected = __last_node + 1;
+            __current >>= 1;
+        }
+    }
+};
+
+_LIBCPP_EXPORTED_FROM_ABI
+__barrier_algorithm_base * __construct_barrier_algorithm_base(ptr
diff _t& __expected)
+{
+    return new __barrier_algorithm_base(__expected);
+}
+_LIBCPP_EXPORTED_FROM_ABI
+bool __arrive_barrier_algorithm_base(__barrier_algorithm_base* __barrier,
+                                     __barrier_phase_t __old_phase)
+{
+    return __barrier->__arrive(__old_phase);
+}
+_LIBCPP_EXPORTED_FROM_ABI
+void __destroy_barrier_algorithm_base(__barrier_algorithm_base* __barrier)
+{
+    delete __barrier;
+}
+
+#endif //!defined(_LIBCPP_HAS_NO_TREE_BARRIER) && (_LIBCPP_STD_VER >= 11)
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif //_LIBCPP_HAS_NO_THREADS

diff  --git a/libcxx/src/include/apple_availability.h b/libcxx/src/include/apple_availability.h
index 0091138170fd..f0a5800e8f3a 100644
--- a/libcxx/src/include/apple_availability.h
+++ b/libcxx/src/include/apple_availability.h
@@ -46,6 +46,24 @@
 #endif
 #endif // __ENVIRONMENT_.*_VERSION_MIN_REQUIRED__
 
+#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__)
+#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101500
+#define _LIBCPP_USE_ULOCK
+#endif
+#elif defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__)
+#if __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ >= 130000
+#define _LIBCPP_USE_ULOCK
+#endif
+#elif defined(__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__)
+#if __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ >= 130000
+#define _LIBCPP_USE_ULOCK
+#endif
+#elif defined(__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__)
+#if __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ >= 60000
+#define _LIBCPP_USE_ULOCK
+#endif
+#endif // __ENVIRONMENT_.*_VERSION_MIN_REQUIRED__
+
 #endif // __APPLE__
 
 #endif // _LIBCPP_SRC_INCLUDE_APPLE_AVAILABILITY_H

diff  --git a/libcxx/test/libcxx/double_include.sh.cpp b/libcxx/test/libcxx/double_include.sh.cpp
index 5a1ee32f93d7..42f6376142f2 100644
--- a/libcxx/test/libcxx/double_include.sh.cpp
+++ b/libcxx/test/libcxx/double_include.sh.cpp
@@ -25,6 +25,9 @@
 #include <array>
 #ifndef _LIBCPP_HAS_NO_THREADS
 #include <atomic>
+#include <latch>
+#include <barrier>
+#include <semaphore>
 #endif
 #include <bit>
 #include <bitset>

diff  --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.wait/atomic_wait.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.wait/atomic_wait.pass.cpp
new file mode 100644
index 000000000000..8bd262a384af
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.wait/atomic_wait.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: libcpp-has-no-threads
+// XFAIL: c++98, c++03
+
+// <atomic>
+
+#include <atomic>
+#include <type_traits>
+#include <cassert>
+#include <thread>
+
+#include "test_macros.h"
+#include "../atomics.types.operations.req/atomic_helpers.h"
+
+template <class T>
+struct TestFn {
+  void operator()() const {
+    typedef std::atomic<T> A;
+
+    A t;
+    std::atomic_init(&t, T(1));
+    assert(std::atomic_load(&t) == T(1));
+    std::atomic_wait(&t, T(0));
+    std::thread t_([&](){
+      std::atomic_store(&t, T(3));
+      std::atomic_notify_one(&t);
+    });
+    std::atomic_wait(&t, T(1));
+    t_.join();
+
+    volatile A vt;
+    std::atomic_init(&vt, T(2));
+    assert(std::atomic_load(&vt) == T(2));
+    std::atomic_wait(&vt, T(1));
+    std::thread t2_([&](){
+      std::atomic_store(&vt, T(4));
+      std::atomic_notify_one(&vt);
+    });
+    std::atomic_wait(&vt, T(2));
+    t2_.join();
+  }
+};
+
+int main(int, char**)
+{
+    TestEachAtomicType<TestFn>()();
+
+  return 0;
+}

diff  --git a/libcxx/test/std/atomics/types.pass.cpp b/libcxx/test/std/atomics/types.pass.cpp
new file mode 100644
index 000000000000..4c225a58d0ba
--- /dev/null
+++ b/libcxx/test/std/atomics/types.pass.cpp
@@ -0,0 +1,125 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// <atomic>
+
+// Test nested types
+
+// template <class T>
+// class atomic
+// {
+// public:
+//     typedef T                                        value_type;
+// };
+
+#include <atomic>
+#include <type_traits>
+
+#include <thread>
+#include <chrono>
+#if TEST_STD_VER >= 20
+# include <memory>
+#endif
+
+#include "test_macros.h"
+
+template <class A>
+void
+test_atomic()
+{
+    A a;
+#if TEST_STD_VER >= 17
+    static_assert((std::is_same<typename A::value_type, decltype(a.load())>::value), "");
+#endif
+}
+
+template <class T>
+void
+test()
+{
+    using A = std::atomic<T>;
+#if TEST_STD_VER >= 17
+    static_assert((std::is_same<typename A::value_type, T>::value), "");
+#endif
+    test_atomic<A>();
+}
+
+struct TriviallyCopyable {
+    int i_;
+};
+
+int main(int, char**)
+{
+    test<bool>               ();
+    test<char>               ();
+    test<signed char>        ();
+    test<unsigned char>      ();
+    test<short>              ();
+    test<unsigned short>     ();
+    test<int>                ();
+    test<unsigned int>       ();
+    test<long>               ();
+    test<unsigned long>      ();
+    test<long long>          ();
+    test<unsigned long long> ();
+    test<char16_t>           ();
+    test<char32_t>           ();
+    test<wchar_t>            ();
+
+    test<int_least8_t>   ();
+    test<uint_least8_t>  ();
+    test<int_least16_t>  ();
+    test<uint_least16_t> ();
+    test<int_least32_t>  ();
+    test<uint_least32_t> ();
+    test<int_least64_t>  ();
+    test<uint_least64_t> ();
+
+    test<int_fast8_t>   ();
+    test<uint_fast8_t>  ();
+    test<int_fast16_t>  ();
+    test<uint_fast16_t> ();
+    test<int_fast32_t>  ();
+    test<uint_fast32_t> ();
+    test<int_fast64_t>  ();
+    test<uint_fast64_t> ();
+
+    test< int8_t>  ();
+    test<uint8_t>  ();
+    test< int16_t> ();
+    test<uint16_t> ();
+    test< int32_t> ();
+    test<uint32_t> ();
+    test< int64_t> ();
+    test<uint64_t> ();
+
+    test<intptr_t>  ();
+    test<uintptr_t> ();
+    test<size_t>    ();
+    test<ptr
diff _t> ();
+    test<intmax_t>  ();
+    test<uintmax_t> ();
+
+    test<uintmax_t> ();
+    test<uintmax_t> ();
+
+    test<TriviallyCopyable>();
+    test<std::thread::id>();
+    test<std::chrono::nanoseconds>();
+    test<float>();
+
+#if TEST_STD_VER >= 20
+    test_atomic<std::atomic_signed_lock_free>();
+    test_atomic<std::atomic_unsigned_lock_free>();
+/*
+    test<std::shared_ptr<int>>();
+*/
+#endif
+
+    return 0;
+}

diff  --git a/libcxx/test/std/thread/thread.barrier/arrive.pass.cpp b/libcxx/test/std/thread/thread.barrier/arrive.pass.cpp
new file mode 100644
index 000000000000..943fc84306ae
--- /dev/null
+++ b/libcxx/test/std/thread/thread.barrier/arrive.pass.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: libcpp-has-no-threads
+// UNSUPPORTED: c++98, c++03, c++11
+
+// <barrier>
+
+#include <barrier>
+#include <thread>
+
+#include "test_macros.h"
+
+int main(int, char**)
+{
+  std::barrier<> b(2);
+
+  auto tok = b.arrive();
+  std::thread t([&](){
+    (void)b.arrive();
+  });
+  b.wait(std::move(tok));
+  t.join();
+
+  auto tok2 = b.arrive(2);
+  b.wait(std::move(tok2));
+  return 0;
+}

diff  --git a/libcxx/test/std/thread/thread.barrier/arrive_and_drop.pass.cpp b/libcxx/test/std/thread/thread.barrier/arrive_and_drop.pass.cpp
new file mode 100644
index 000000000000..8724a1dbe207
--- /dev/null
+++ b/libcxx/test/std/thread/thread.barrier/arrive_and_drop.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: libcpp-has-no-threads
+// UNSUPPORTED: c++98, c++03, c++11
+
+// <barrier>
+
+#include <barrier>
+#include <thread>
+#include <cassert>
+
+#include "test_macros.h"
+
+int main(int, char**)
+{
+  std::barrier<> b(2);
+
+  std::thread t([&](){
+    b.arrive_and_drop();
+  });
+
+  b.arrive_and_wait();
+  b.arrive_and_wait();
+  t.join();
+  return 0;
+}

diff  --git a/libcxx/test/std/thread/thread.barrier/arrive_and_wait.pass.cpp b/libcxx/test/std/thread/thread.barrier/arrive_and_wait.pass.cpp
new file mode 100644
index 000000000000..8596e8a1b6f2
--- /dev/null
+++ b/libcxx/test/std/thread/thread.barrier/arrive_and_wait.pass.cpp
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: libcpp-has-no-threads
+// UNSUPPORTED: c++98, c++03, c++11
+
+// <barrier>
+
+#include <barrier>
+#include <thread>
+
+#include "test_macros.h"
+
+int main(int, char**)
+{
+  std::barrier<> b(2);
+
+  std::thread t([&](){
+    for(int i = 0; i < 10; ++i)
+      b.arrive_and_wait();
+  });
+  for(int i = 0; i < 10; ++i)
+    b.arrive_and_wait();
+  t.join();
+
+  return 0;
+}

diff  --git a/libcxx/test/std/thread/thread.barrier/completion.pass.cpp b/libcxx/test/std/thread/thread.barrier/completion.pass.cpp
new file mode 100644
index 000000000000..67ae526f4881
--- /dev/null
+++ b/libcxx/test/std/thread/thread.barrier/completion.pass.cpp
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: libcpp-has-no-threads
+// UNSUPPORTED: c++98, c++03, c++11
+
+// <barrier>
+
+#include <barrier>
+#include <thread>
+#include <cassert>
+
+#include "test_macros.h"
+
+int main(int, char**)
+{
+  int x = 0;
+  auto comp = [&]() { x += 1; };
+  std::barrier<decltype(comp)> b(2, comp);
+
+  std::thread t([&](){
+      for(int i = 0; i < 10; ++i)
+        b.arrive_and_wait();
+  });
+
+  for(int i = 0; i < 10; ++i)
+    b.arrive_and_wait();
+
+  assert(x == 10);
+  t.join();
+  return 0;
+}

diff  --git a/libcxx/test/std/thread/thread.barrier/max.pass.cpp b/libcxx/test/std/thread/thread.barrier/max.pass.cpp
new file mode 100644
index 000000000000..5e64b0154846
--- /dev/null
+++ b/libcxx/test/std/thread/thread.barrier/max.pass.cpp
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: libcpp-has-no-threads
+// UNSUPPORTED: c++98, c++03, c++11
+
+// <barrier>
+
+#include <barrier>
+#include <thread>
+#include <cassert>
+
+#include "test_macros.h"
+
+int main(int, char**)
+{
+  static_assert(std::barrier<>::max() > 0, "");
+  auto l = [](){};
+  static_assert(std::barrier<decltype(l)>::max() > 0, "");
+  return 0;
+}

diff  --git a/libcxx/test/std/thread/thread.barrier/version.pass.cpp b/libcxx/test/std/thread/thread.barrier/version.pass.cpp
new file mode 100644
index 000000000000..544f2d1c88dd
--- /dev/null
+++ b/libcxx/test/std/thread/thread.barrier/version.pass.cpp
@@ -0,0 +1,25 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: libcpp-has-no-threads
+// UNSUPPORTED: c++98, c++03, c++11
+
+// <barrier>
+
+#include <barrier>
+
+#include "test_macros.h"
+
+#ifndef _LIBCPP_VERSION
+#error _LIBCPP_VERSION not defined
+#endif
+
+int main(int, char**)
+{
+  return 0;
+}

diff  --git a/libcxx/test/std/thread/thread.latch/arrive_and_wait.pass.cpp b/libcxx/test/std/thread/thread.latch/arrive_and_wait.pass.cpp
new file mode 100644
index 000000000000..156e913e2562
--- /dev/null
+++ b/libcxx/test/std/thread/thread.latch/arrive_and_wait.pass.cpp
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: libcpp-has-no-threads
+// UNSUPPORTED: c++98, c++03, c++11
+
+// <latch>
+
+#include <latch>
+#include <thread>
+
+#include "test_macros.h"
+
+int main(int, char**)
+{
+  std::latch l(2);
+
+  std::thread t([&](){
+      l.arrive_and_wait();
+  });
+  l.arrive_and_wait();
+  t.join();
+
+  return 0;
+}

diff  --git a/libcxx/test/std/thread/thread.latch/count_down.pass.cpp b/libcxx/test/std/thread/thread.latch/count_down.pass.cpp
new file mode 100644
index 000000000000..81f0605313f0
--- /dev/null
+++ b/libcxx/test/std/thread/thread.latch/count_down.pass.cpp
@@ -0,0 +1,31 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: libcpp-has-no-threads
+// UNSUPPORTED: c++98, c++03, c++11
+
+// <latch>
+
+#include <latch>
+#include <thread>
+
+#include "test_macros.h"
+
+int main(int, char**)
+{
+  std::latch l(2);
+
+  l.count_down();
+  std::thread t([&](){
+    l.count_down();
+  });
+  l.wait();
+  t.join();
+
+  return 0;
+}

diff  --git a/libcxx/test/std/thread/thread.latch/max.pass.cpp b/libcxx/test/std/thread/thread.latch/max.pass.cpp
new file mode 100644
index 000000000000..676748f055ec
--- /dev/null
+++ b/libcxx/test/std/thread/thread.latch/max.pass.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: libcpp-has-no-threads
+// UNSUPPORTED: c++98, c++03, c++11
+
+// <latch>
+
+#include <latch>
+#include <cassert>
+
+#include "test_macros.h"
+
+int main(int, char**)
+{
+  static_assert(std::latch::max() > 0, "");
+  return 0;
+}

diff  --git a/libcxx/test/std/thread/thread.latch/try_wait.pass.cpp b/libcxx/test/std/thread/thread.latch/try_wait.pass.cpp
new file mode 100644
index 000000000000..7284849b6ede
--- /dev/null
+++ b/libcxx/test/std/thread/thread.latch/try_wait.pass.cpp
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: libcpp-has-no-threads
+// UNSUPPORTED: c++98, c++03, c++11
+
+// <latch>
+
+#include <latch>
+#include <cassert>
+
+#include "test_macros.h"
+
+int main(int, char**)
+{
+  std::latch l(1);
+
+  l.count_down();
+  bool const b = l.try_wait();
+  assert(b);
+
+  return 0;
+}

diff  --git a/libcxx/test/std/thread/thread.latch/version.pass.cpp b/libcxx/test/std/thread/thread.latch/version.pass.cpp
new file mode 100644
index 000000000000..8c17dab61af8
--- /dev/null
+++ b/libcxx/test/std/thread/thread.latch/version.pass.cpp
@@ -0,0 +1,25 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: libcpp-has-no-threads
+// UNSUPPORTED: c++98, c++03, c++11
+
+// <latch>
+
+#include <latch>
+
+#include "test_macros.h"
+
+#ifndef _LIBCPP_VERSION
+#error _LIBCPP_VERSION not defined
+#endif
+
+int main(int, char**)
+{
+  return 0;
+}

diff  --git a/libcxx/test/std/thread/thread.semaphore/acquire.pass.cpp b/libcxx/test/std/thread/thread.semaphore/acquire.pass.cpp
new file mode 100644
index 000000000000..bb5f7fb31162
--- /dev/null
+++ b/libcxx/test/std/thread/thread.semaphore/acquire.pass.cpp
@@ -0,0 +1,31 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: libcpp-has-no-threads
+// UNSUPPORTED: c++98, c++03, c++11
+
+// <semaphore>
+
+#include <semaphore>
+#include <thread>
+
+#include "test_macros.h"
+
+int main(int, char**)
+{
+  std::counting_semaphore<> s(2);
+
+  std::thread t([&](){
+    s.acquire();
+  });
+  t.join();
+
+  s.acquire();
+
+  return 0;
+}

diff  --git a/libcxx/test/std/thread/thread.semaphore/binary.pass.cpp b/libcxx/test/std/thread/thread.semaphore/binary.pass.cpp
new file mode 100644
index 000000000000..d46d6ae47719
--- /dev/null
+++ b/libcxx/test/std/thread/thread.semaphore/binary.pass.cpp
@@ -0,0 +1,38 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: libcpp-has-no-threads
+// UNSUPPORTED: c++98, c++03, c++11
+
+// <semaphore>
+
+#include <semaphore>
+#include <chrono>
+#include <thread>
+
+#include "test_macros.h"
+
+int main(int, char**)
+{
+  std::binary_semaphore s(1);
+
+  auto l = [&](){
+    for(int i = 0; i < 1024; ++i) {
+        s.acquire();
+        std::this_thread::sleep_for(std::chrono::microseconds(1));
+        s.release();
+    }
+  };
+
+  std::thread t(l);
+  l();
+
+  t.join();
+
+  return 0;
+}

diff  --git a/libcxx/test/std/thread/thread.semaphore/max.pass.cpp b/libcxx/test/std/thread/thread.semaphore/max.pass.cpp
new file mode 100644
index 000000000000..9ed70efcd0dc
--- /dev/null
+++ b/libcxx/test/std/thread/thread.semaphore/max.pass.cpp
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: libcpp-has-no-threads
+// UNSUPPORTED: c++98, c++03, c++11
+
+// <semaphore>
+
+#include <semaphore>
+#include <thread>
+
+#include "test_macros.h"
+
+int main(int, char**)
+{
+  static_assert(std::counting_semaphore<>::max() > 0, "");
+  static_assert(std::counting_semaphore<1>::max() >= 1, "");
+  static_assert(std::counting_semaphore<std::numeric_limits<int>::max()>::max() >= 1, "");
+  static_assert(std::counting_semaphore<std::numeric_limits<unsigned>::max()>::max() >= 1, "");
+  static_assert(std::counting_semaphore<std::numeric_limits<ptr
diff _t>::max()>::max() >= 1, "");
+  static_assert(std::counting_semaphore<1>::max() == std::binary_semaphore::max(), "");
+  return 0;
+}

diff  --git a/libcxx/test/std/thread/thread.semaphore/release.pass.cpp b/libcxx/test/std/thread/thread.semaphore/release.pass.cpp
new file mode 100644
index 000000000000..0660feb34de7
--- /dev/null
+++ b/libcxx/test/std/thread/thread.semaphore/release.pass.cpp
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: libcpp-has-no-threads
+// UNSUPPORTED: c++98, c++03, c++11
+
+// <semaphore>
+
+#include <semaphore>
+#include <thread>
+
+#include "test_macros.h"
+
+int main(int, char**)
+{
+  std::counting_semaphore<> s(0);
+
+  s.release();
+  s.acquire();
+
+  std::thread t([&](){
+    s.acquire();
+  });
+  s.release(2);
+  t.join();
+  s.acquire();
+
+  return 0;
+}

diff  --git a/libcxx/test/std/thread/thread.semaphore/timed.pass.cpp b/libcxx/test/std/thread/thread.semaphore/timed.pass.cpp
new file mode 100644
index 000000000000..d0bbc91351cc
--- /dev/null
+++ b/libcxx/test/std/thread/thread.semaphore/timed.pass.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: libcpp-has-no-threads
+// UNSUPPORTED: c++98, c++03, c++11
+
+// <semaphore>
+
+#include <semaphore>
+#include <thread>
+#include <chrono>
+
+#include "test_macros.h"
+
+int main(int, char**)
+{
+  auto const start = std::chrono::steady_clock::now();
+
+  std::counting_semaphore<> s(0);
+
+  assert(!s.try_acquire_until(start + std::chrono::milliseconds(250)));
+  assert(!s.try_acquire_for(std::chrono::milliseconds(250)));
+
+  std::thread t([&](){
+    std::this_thread::sleep_for(std::chrono::milliseconds(250));
+    s.release();
+    std::this_thread::sleep_for(std::chrono::milliseconds(250));
+    s.release();
+  });
+
+  assert(s.try_acquire_until(start + std::chrono::seconds(2)));
+  assert(s.try_acquire_for(std::chrono::seconds(2)));
+  t.join();
+
+  auto const end = std::chrono::steady_clock::now();
+  assert(end - start < std::chrono::seconds(10));
+
+  return 0;
+}

diff  --git a/libcxx/test/std/thread/thread.semaphore/try_acquire.pass.cpp b/libcxx/test/std/thread/thread.semaphore/try_acquire.pass.cpp
new file mode 100644
index 000000000000..7f0656785927
--- /dev/null
+++ b/libcxx/test/std/thread/thread.semaphore/try_acquire.pass.cpp
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: libcpp-has-no-threads
+// UNSUPPORTED: c++98, c++03, c++11
+
+// <semaphore>
+
+#include <semaphore>
+#include <thread>
+
+#include "test_macros.h"
+
+int main(int, char**)
+{
+  std::counting_semaphore<> s(1);
+
+  assert(s.try_acquire());
+  s.release();
+  assert(s.try_acquire());
+  s.release(2);
+  std::thread t([&](){
+    assert(s.try_acquire());
+  });
+  t.join();
+  assert(s.try_acquire());
+
+  return 0;
+}

diff  --git a/libcxx/test/std/thread/thread.semaphore/version.pass.cpp b/libcxx/test/std/thread/thread.semaphore/version.pass.cpp
new file mode 100644
index 000000000000..0a3affb876ac
--- /dev/null
+++ b/libcxx/test/std/thread/thread.semaphore/version.pass.cpp
@@ -0,0 +1,25 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// UNSUPPORTED: libcpp-has-no-threads
+// UNSUPPORTED: c++98, c++03, c++11
+
+// <semaphore>
+
+#include <semaphore>
+
+#include "test_macros.h"
+
+#ifndef _LIBCPP_VERSION
+#error _LIBCPP_VERSION not defined
+#endif
+
+int main(int, char**)
+{
+  return 0;
+}

diff  --git a/libcxx/www/cxx2a_status.html b/libcxx/www/cxx2a_status.html
index 9422fb93de15..add53d46670b 100644
--- a/libcxx/www/cxx2a_status.html
+++ b/libcxx/www/cxx2a_status.html
@@ -169,7 +169,7 @@ <h3>Paper Status</h3>
 	<tr><td><a href="https://wg21.link/P1004">P1004</a></td><td>LWG</td><td>Making std::vector constexpr</td><td>Cologne</td><td></td><td></td></tr>
 	<tr><td><a href="https://wg21.link/P1035">P1035</a></td><td>LWG</td><td>Input Range Adaptors</td><td>Cologne</td><td></td><td></td></tr>
 	<tr><td><a href="https://wg21.link/P1065">P1065</a></td><td>LWG</td><td>Constexpr INVOKE</td><td>Cologne</td><td></td><td></td></tr>
-	<tr><td><a href="https://wg21.link/P1135">P1135</a></td><td>LWG</td><td>The C++20 Synchronization Library</td><td>Cologne</td><td></td><td></td></tr>
+	<tr><td><a href="https://wg21.link/P1135">P1135</a></td><td>LWG</td><td>The C++20 Synchronization Library</td><td>Cologne</td><td>Complete</td><td></td></tr>
 	<tr><td><a href="https://wg21.link/P1207">P1207</a></td><td>LWG</td><td>Movability of Single-pass Iterators</td><td>Cologne</td><td></td><td></td></tr>
 	<tr><td><a href="https://wg21.link/P1208">P1208</a></td><td>LWG</td><td>Adopt source_location for C++20</td><td>Cologne</td><td></td><td></td></tr>
 	<tr><td><a href="https://wg21.link/P1355">P1355</a></td><td>LWG</td><td>Exposing a narrow contract for ceil2</td><td>Cologne</td><td>Complete</td><td>9.0</td></tr>


        


More information about the libcxx-commits mailing list