[libcxx-commits] [libcxx] 4fa812b - [libc++] Implement `std::condition_variable_any::wait[_for/until]` overloads that take `stop_token`

via libcxx-commits libcxx-commits at lists.llvm.org
Fri Sep 29 05:50:40 PDT 2023


Author: Hui
Date: 2023-09-29T13:50:16+01:00
New Revision: 4fa812bb52a5b1eea22750a1b59f94221d0df622

URL: https://github.com/llvm/llvm-project/commit/4fa812bb52a5b1eea22750a1b59f94221d0df622
DIFF: https://github.com/llvm/llvm-project/commit/4fa812bb52a5b1eea22750a1b59f94221d0df622.diff

LOG: [libc++] Implement `std::condition_variable_any::wait[_for/until]` overloads that take `stop_token`

- This is section 32.6.4 of P0660R10
- https://eel.is/c++draft/thread.condvarany.intwait

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

Added: 
    libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_for_token_pred.pass.cpp
    libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_token_pred.pass.cpp
    libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_until_token_pred.pass.cpp

Modified: 
    libcxx/docs/FeatureTestMacroTable.rst
    libcxx/docs/Status/Cxx20.rst
    libcxx/docs/Status/Cxx20Papers.csv
    libcxx/include/__stop_token/stop_callback.h
    libcxx/include/__stop_token/stop_source.h
    libcxx/include/__stop_token/stop_state.h
    libcxx/include/__stop_token/stop_token.h
    libcxx/include/condition_variable
    libcxx/include/version
    libcxx/test/libcxx/transitive_includes/cxx03.csv
    libcxx/test/libcxx/transitive_includes/cxx11.csv
    libcxx/test/libcxx/transitive_includes/cxx14.csv
    libcxx/test/libcxx/transitive_includes/cxx17.csv
    libcxx/test/libcxx/transitive_includes/cxx20.csv
    libcxx/test/libcxx/transitive_includes/cxx23.csv
    libcxx/test/libcxx/transitive_includes/cxx26.csv
    libcxx/test/std/language.support/support.limits/support.limits.general/stop_token.version.compile.pass.cpp
    libcxx/test/std/language.support/support.limits/support.limits.general/thread.version.compile.pass.cpp
    libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
    libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_terminates.sh.cpp
    libcxx/utils/generate_feature_test_macro_components.py

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index e1b4172b22c53da..2b21ec3d2fe23ac 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -252,7 +252,7 @@ Status
     --------------------------------------------------- -----------------
     ``__cpp_lib_is_pointer_interconvertible``           *unimplemented*
     --------------------------------------------------- -----------------
-    ``__cpp_lib_jthread``                               *unimplemented*
+    ``__cpp_lib_jthread``                               ``201911L``
     --------------------------------------------------- -----------------
     ``__cpp_lib_latch``                                 ``201907L``
     --------------------------------------------------- -----------------

diff  --git a/libcxx/docs/Status/Cxx20.rst b/libcxx/docs/Status/Cxx20.rst
index 94260b257fce8ab..227b3197d82e680 100644
--- a/libcxx/docs/Status/Cxx20.rst
+++ b/libcxx/docs/Status/Cxx20.rst
@@ -48,7 +48,7 @@ Paper Status
    .. [#note-P0883.1] P0883: shared_ptr and floating-point changes weren't applied as they themselves aren't implemented yet.
    .. [#note-P0883.2] P0883: ``ATOMIC_FLAG_INIT`` was marked deprecated in version 14.0, but was undeprecated with the implementation of LWG3659 in version 15.0.
    .. [#note-P2231] P2231: Optional is complete. The changes to variant haven't been implemented yet.
-   .. [#note-P0660] P0660: Section 32.3 Stop Tokens is complete. ``jthread`` hasn't been implemented yet.
+   .. [#note-P0660] P0660: The paper is implemented but the features are experimental and can be enabled via ``-fexperimental-library``.
    .. [#note-P0355] P0355: The implementation status is:
 
       * ``Calendars`` mostly done in Clang 7

diff  --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv
index 8abe9d6a988a492..dd6fcf9a7583ce3 100644
--- a/libcxx/docs/Status/Cxx20Papers.csv
+++ b/libcxx/docs/Status/Cxx20Papers.csv
@@ -104,7 +104,7 @@
 "`P0553R4 <https://wg21.link/P0553R4>`__","LWG","Bit operations","Cologne","|Complete|","9.0"
 "`P0631R8 <https://wg21.link/P0631R8>`__","LWG","Math Constants","Cologne","|Complete|","11.0"
 "`P0645R10 <https://wg21.link/P0645R10>`__","LWG","Text Formatting","Cologne","|Complete| [#note-P0645]_","14.0"
-"`P0660R10 <https://wg21.link/P0660R10>`__","LWG","Stop Token and Joining Thread, Rev 10.","Cologne","|In Progress| [#note-P0660]_",""
+"`P0660R10 <https://wg21.link/P0660R10>`__","LWG","Stop Token and Joining Thread, Rev 10.","Cologne","|Complete| [#note-P0660]_","18.0.0"
 "`P0784R7 <https://wg21.link/P0784R7>`__","CWG","More constexpr containers","Cologne","|Complete|","12.0"
 "`P0980R1 <https://wg21.link/P0980R1>`__","LWG","Making std::string constexpr","Cologne","|Complete|","15.0"
 "`P1004R2 <https://wg21.link/P1004R2>`__","LWG","Making std::vector constexpr","Cologne","|Complete|","15.0"

diff  --git a/libcxx/include/__stop_token/stop_callback.h b/libcxx/include/__stop_token/stop_callback.h
index e52f0350dacc5bb..9e5b0338d4667a8 100644
--- a/libcxx/include/__stop_token/stop_callback.h
+++ b/libcxx/include/__stop_token/stop_callback.h
@@ -31,7 +31,7 @@ _LIBCPP_PUSH_MACROS
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#if _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN)
+#if _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN) && !defined(_LIBCPP_HAS_NO_THREADS)
 
 template <class _Callback>
 class _LIBCPP_AVAILABILITY_SYNC stop_callback : private __stop_callback_base {
@@ -101,4 +101,4 @@ _LIBCPP_END_NAMESPACE_STD
 
 _LIBCPP_POP_MACROS
 
-#endif // _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN)
+#endif // _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN) && !defined(_LIBCPP_HAS_NO_THREADS)

diff  --git a/libcxx/include/__stop_token/stop_source.h b/libcxx/include/__stop_token/stop_source.h
index 9d7ffd3e91e2830..1080069cf3b8bea 100644
--- a/libcxx/include/__stop_token/stop_source.h
+++ b/libcxx/include/__stop_token/stop_source.h
@@ -23,7 +23,7 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#if _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN)
+#if _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN) && !defined(_LIBCPP_HAS_NO_THREADS)
 
 struct nostopstate_t {
   explicit nostopstate_t() = default;
@@ -89,4 +89,4 @@ class _LIBCPP_AVAILABILITY_SYNC stop_source {
 
 _LIBCPP_END_NAMESPACE_STD
 
-#endif // _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN)
+#endif // _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN) && !defined(_LIBCPP_HAS_NO_THREADS)

diff  --git a/libcxx/include/__stop_token/stop_state.h b/libcxx/include/__stop_token/stop_state.h
index 0e43292faae8819..462aa73952b84f9 100644
--- a/libcxx/include/__stop_token/stop_state.h
+++ b/libcxx/include/__stop_token/stop_state.h
@@ -24,7 +24,7 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#if _LIBCPP_STD_VER >= 20
+#if _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_THREADS)
 
 struct __stop_callback_base : __intrusive_node_base<__stop_callback_base> {
   using __callback_fn_t = void(__stop_callback_base*) noexcept;
@@ -229,7 +229,7 @@ struct __intrusive_shared_ptr_traits<__stop_state> {
   }
 };
 
-#endif // _LIBCPP_STD_VER >= 20
+#endif // _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_THREADS)
 
 _LIBCPP_END_NAMESPACE_STD
 

diff  --git a/libcxx/include/__stop_token/stop_token.h b/libcxx/include/__stop_token/stop_token.h
index 4ec72078226aa88..f2eadb990bdeca2 100644
--- a/libcxx/include/__stop_token/stop_token.h
+++ b/libcxx/include/__stop_token/stop_token.h
@@ -21,7 +21,7 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#if _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN)
+#if _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN) && !defined(_LIBCPP_HAS_NO_THREADS)
 
 class _LIBCPP_AVAILABILITY_SYNC stop_token {
 public:
@@ -57,7 +57,7 @@ class _LIBCPP_AVAILABILITY_SYNC stop_token {
   _LIBCPP_HIDE_FROM_ABI explicit stop_token(const __intrusive_shared_ptr<__stop_state>& __state) : __state_(__state) {}
 };
 
-#endif // _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN)
+#endif // _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN) && !defined(_LIBCPP_HAS_NO_THREADS)
 
 _LIBCPP_END_NAMESPACE_STD
 

diff  --git a/libcxx/include/condition_variable b/libcxx/include/condition_variable
index ac44eb324816d82..f6d72b48337187b 100644
--- a/libcxx/include/condition_variable
+++ b/libcxx/include/condition_variable
@@ -100,6 +100,18 @@ public:
         wait_for(Lock& lock,
                  const chrono::duration<Rep, Period>& rel_time,
                  Predicate pred);
+
+    // [thread.condvarany.intwait], interruptible waits
+    template <class Lock, class Predicate>
+      bool wait(Lock& lock, stop_token stoken, Predicate pred);                               // since C++20
+
+    template <class Lock, class Clock, class Duration, class Predicate>
+      bool wait_until(Lock& lock, stop_token stoken,
+                      const chrono::time_point<Clock, Duration>& abs_time, Predicate pred);   // since C++20
+
+    template <class Lock, class Rep, class Period, class Predicate>
+      bool wait_for(Lock& lock, stop_token stoken,
+                    const chrono::duration<Rep, Period>& rel_time, Predicate pred);           // since C++20
 };
 
 }  // std
@@ -107,6 +119,7 @@ public:
 */
 
 #include <__assert> // all public C++ headers provide the assertion handler
+#include <__availability>
 #include <__chrono/duration.h>
 #include <__chrono/steady_clock.h>
 #include <__chrono/time_point.h>
@@ -118,6 +131,7 @@ public:
 #include <__mutex/mutex.h>
 #include <__mutex/tag_types.h>
 #include <__mutex/unique_lock.h>
+#include <__stop_token/stop_token.h>
 #include <__utility/move.h>
 #include <version>
 
@@ -174,6 +188,21 @@ public:
         wait_for(_Lock& __lock,
                  const chrono::duration<_Rep, _Period>& __d,
                  _Predicate __pred);
+
+#if _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN)
+
+    template <class _Lock, class _Predicate>
+    _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI bool wait(_Lock& __lock, stop_token __stoken, _Predicate __pred);
+
+    template <class _Lock, class _Clock, class _Duration, class _Predicate>
+    _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI bool wait_until(_Lock& __lock, stop_token __stoken,
+                const chrono::time_point<_Clock, _Duration>& __abs_time, _Predicate __pred);
+
+    template <class _Lock, class _Rep, class _Period, class _Predicate>
+    _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI bool wait_for(_Lock& __lock, stop_token __stoken,
+                const chrono::duration<_Rep, _Period>& __rel_time, _Predicate __pred);
+
+#endif // _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN)
 };
 
 inline
@@ -269,6 +298,38 @@ condition_variable_any::wait_for(_Lock& __lock,
                       _VSTD::move(__pred));
 }
 
+#if _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN)
+
+template <class _Lock, class _Predicate>
+bool condition_variable_any::wait(_Lock& __lock, stop_token __stoken, _Predicate __pred) {
+    while (!__stoken.stop_requested()) {
+        if (__pred())
+            return true;
+        wait(__lock);
+    }
+    return __pred();
+}
+
+template <class _Lock, class _Clock, class _Duration, class _Predicate>
+bool condition_variable_any::wait_until(
+    _Lock& __lock, stop_token __stoken, const chrono::time_point<_Clock, _Duration>& __abs_time, _Predicate __pred) {
+    while (!__stoken.stop_requested()) {
+        if (__pred())
+            return true;
+        if (wait_until(__lock, __abs_time) == cv_status::timeout)
+            return __pred();
+    }
+    return __pred();
+}
+
+template <class _Lock, class _Rep, class _Period, class _Predicate>
+bool condition_variable_any::wait_for(
+    _Lock& __lock, stop_token __stoken, const chrono::duration<_Rep, _Period>& __rel_time, _Predicate __pred) {
+    return wait_until(__lock, std::move(__stoken), chrono::steady_clock::now() + __rel_time, std::move(__pred));
+}
+
+#endif // _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN)
+
 _LIBCPP_EXPORTED_FROM_ABI void notify_all_at_thread_exit(condition_variable&, unique_lock<mutex>);
 
 _LIBCPP_END_NAMESPACE_STD

diff  --git a/libcxx/include/version b/libcxx/include/version
index e5a995366a7aa48..4fdfec4f34e3f6e 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -372,8 +372,8 @@ __cpp_lib_within_lifetime                               202306L <type_traits>
 // # define __cpp_lib_is_layout_compatible                 201907L
 # define __cpp_lib_is_nothrow_convertible               201806L
 // # define __cpp_lib_is_pointer_interconvertible          201907L
-# if !defined(_LIBCPP_HAS_NO_THREADS)
-// #   define __cpp_lib_jthread                            201911L
+# if !defined(_LIBCPP_HAS_NO_THREADS) && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN) && !defined(_LIBCPP_AVAILABILITY_HAS_NO_SYNC)
+#   define __cpp_lib_jthread                            201911L
 # endif
 # if !defined(_LIBCPP_HAS_NO_THREADS) && !defined(_LIBCPP_AVAILABILITY_HAS_NO_SYNC)
 #   define __cpp_lib_latch                              201907L

diff  --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index 57420b9a3795d41..70dbad21780a1c7 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -175,6 +175,7 @@ condition_variable cstdlib
 condition_variable cstring
 condition_variable ctime
 condition_variable initializer_list
+condition_variable iosfwd
 condition_variable limits
 condition_variable new
 condition_variable ratio

diff  --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index 464c0ef892c6195..1ee950f9fc30ea2 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -176,6 +176,7 @@ condition_variable cstdlib
 condition_variable cstring
 condition_variable ctime
 condition_variable initializer_list
+condition_variable iosfwd
 condition_variable limits
 condition_variable new
 condition_variable ratio

diff  --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index eb698e29d922ddc..22e1d30f7fd67b2 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -176,6 +176,7 @@ condition_variable cstdlib
 condition_variable cstring
 condition_variable ctime
 condition_variable initializer_list
+condition_variable iosfwd
 condition_variable limits
 condition_variable new
 condition_variable ratio

diff  --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index eb698e29d922ddc..22e1d30f7fd67b2 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -176,6 +176,7 @@ condition_variable cstdlib
 condition_variable cstring
 condition_variable ctime
 condition_variable initializer_list
+condition_variable iosfwd
 condition_variable limits
 condition_variable new
 condition_variable ratio

diff  --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv
index 9ba59c10734b3ec..0cbd4c52c6e4c9b 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx20.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv
@@ -182,6 +182,7 @@ condition_variable cstdlib
 condition_variable cstring
 condition_variable ctime
 condition_variable initializer_list
+condition_variable iosfwd
 condition_variable limits
 condition_variable new
 condition_variable ratio

diff  --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index baaf19a002b0eb1..8b32cad73fb5ada 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -118,8 +118,11 @@ complex stdexcept
 complex version
 concepts cstddef
 concepts version
+condition_variable atomic
 condition_variable cerrno
 condition_variable cstddef
+condition_variable cstdint
+condition_variable cstring
 condition_variable ctime
 condition_variable iosfwd
 condition_variable limits

diff  --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index baaf19a002b0eb1..8b32cad73fb5ada 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -118,8 +118,11 @@ complex stdexcept
 complex version
 concepts cstddef
 concepts version
+condition_variable atomic
 condition_variable cerrno
 condition_variable cstddef
+condition_variable cstdint
+condition_variable cstring
 condition_variable ctime
 condition_variable iosfwd
 condition_variable limits

diff  --git a/libcxx/test/std/language.support/support.limits/support.limits.general/stop_token.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/stop_token.version.compile.pass.cpp
index ad5fd9407ec2e7f..44447488aea938d 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/stop_token.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/stop_token.version.compile.pass.cpp
@@ -44,46 +44,46 @@
 
 #elif TEST_STD_VER == 20
 
-# if !defined(_LIBCPP_VERSION)
+# if !defined(_LIBCPP_HAS_NO_THREADS) && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN) && !defined(_LIBCPP_AVAILABILITY_HAS_NO_SYNC)
 #   ifndef __cpp_lib_jthread
 #     error "__cpp_lib_jthread should be defined in c++20"
 #   endif
 #   if __cpp_lib_jthread != 201911L
 #     error "__cpp_lib_jthread should have the value 201911L in c++20"
 #   endif
-# else // _LIBCPP_VERSION
+# else
 #   ifdef __cpp_lib_jthread
-#     error "__cpp_lib_jthread should not be defined because it is unimplemented in libc++!"
+#     error "__cpp_lib_jthread should not be defined when the requirement '!defined(_LIBCPP_HAS_NO_THREADS) && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN) && !defined(_LIBCPP_AVAILABILITY_HAS_NO_SYNC)' is not met!"
 #   endif
 # endif
 
 #elif TEST_STD_VER == 23
 
-# if !defined(_LIBCPP_VERSION)
+# if !defined(_LIBCPP_HAS_NO_THREADS) && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN) && !defined(_LIBCPP_AVAILABILITY_HAS_NO_SYNC)
 #   ifndef __cpp_lib_jthread
 #     error "__cpp_lib_jthread should be defined in c++23"
 #   endif
 #   if __cpp_lib_jthread != 201911L
 #     error "__cpp_lib_jthread should have the value 201911L in c++23"
 #   endif
-# else // _LIBCPP_VERSION
+# else
 #   ifdef __cpp_lib_jthread
-#     error "__cpp_lib_jthread should not be defined because it is unimplemented in libc++!"
+#     error "__cpp_lib_jthread should not be defined when the requirement '!defined(_LIBCPP_HAS_NO_THREADS) && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN) && !defined(_LIBCPP_AVAILABILITY_HAS_NO_SYNC)' is not met!"
 #   endif
 # endif
 
 #elif TEST_STD_VER > 23
 
-# if !defined(_LIBCPP_VERSION)
+# if !defined(_LIBCPP_HAS_NO_THREADS) && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN) && !defined(_LIBCPP_AVAILABILITY_HAS_NO_SYNC)
 #   ifndef __cpp_lib_jthread
 #     error "__cpp_lib_jthread should be defined in c++26"
 #   endif
 #   if __cpp_lib_jthread != 201911L
 #     error "__cpp_lib_jthread should have the value 201911L in c++26"
 #   endif
-# else // _LIBCPP_VERSION
+# else
 #   ifdef __cpp_lib_jthread
-#     error "__cpp_lib_jthread should not be defined because it is unimplemented in libc++!"
+#     error "__cpp_lib_jthread should not be defined when the requirement '!defined(_LIBCPP_HAS_NO_THREADS) && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN) && !defined(_LIBCPP_AVAILABILITY_HAS_NO_SYNC)' is not met!"
 #   endif
 # endif
 

diff  --git a/libcxx/test/std/language.support/support.limits/support.limits.general/thread.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/thread.version.compile.pass.cpp
index c8fa0daf142d44a..756ed34113dc011 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/thread.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/thread.version.compile.pass.cpp
@@ -61,16 +61,16 @@
 #   error "__cpp_lib_formatters should not be defined before c++23"
 # endif
 
-# if !defined(_LIBCPP_VERSION)
+# if !defined(_LIBCPP_HAS_NO_THREADS) && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN) && !defined(_LIBCPP_AVAILABILITY_HAS_NO_SYNC)
 #   ifndef __cpp_lib_jthread
 #     error "__cpp_lib_jthread should be defined in c++20"
 #   endif
 #   if __cpp_lib_jthread != 201911L
 #     error "__cpp_lib_jthread should have the value 201911L in c++20"
 #   endif
-# else // _LIBCPP_VERSION
+# else
 #   ifdef __cpp_lib_jthread
-#     error "__cpp_lib_jthread should not be defined because it is unimplemented in libc++!"
+#     error "__cpp_lib_jthread should not be defined when the requirement '!defined(_LIBCPP_HAS_NO_THREADS) && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN) && !defined(_LIBCPP_AVAILABILITY_HAS_NO_SYNC)' is not met!"
 #   endif
 # endif
 
@@ -89,16 +89,16 @@
 #   endif
 # endif
 
-# if !defined(_LIBCPP_VERSION)
+# if !defined(_LIBCPP_HAS_NO_THREADS) && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN) && !defined(_LIBCPP_AVAILABILITY_HAS_NO_SYNC)
 #   ifndef __cpp_lib_jthread
 #     error "__cpp_lib_jthread should be defined in c++23"
 #   endif
 #   if __cpp_lib_jthread != 201911L
 #     error "__cpp_lib_jthread should have the value 201911L in c++23"
 #   endif
-# else // _LIBCPP_VERSION
+# else
 #   ifdef __cpp_lib_jthread
-#     error "__cpp_lib_jthread should not be defined because it is unimplemented in libc++!"
+#     error "__cpp_lib_jthread should not be defined when the requirement '!defined(_LIBCPP_HAS_NO_THREADS) && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN) && !defined(_LIBCPP_AVAILABILITY_HAS_NO_SYNC)' is not met!"
 #   endif
 # endif
 
@@ -117,16 +117,16 @@
 #   endif
 # endif
 
-# if !defined(_LIBCPP_VERSION)
+# if !defined(_LIBCPP_HAS_NO_THREADS) && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN) && !defined(_LIBCPP_AVAILABILITY_HAS_NO_SYNC)
 #   ifndef __cpp_lib_jthread
 #     error "__cpp_lib_jthread should be defined in c++26"
 #   endif
 #   if __cpp_lib_jthread != 201911L
 #     error "__cpp_lib_jthread should have the value 201911L in c++26"
 #   endif
-# else // _LIBCPP_VERSION
+# else
 #   ifdef __cpp_lib_jthread
-#     error "__cpp_lib_jthread should not be defined because it is unimplemented in libc++!"
+#     error "__cpp_lib_jthread should not be defined when the requirement '!defined(_LIBCPP_HAS_NO_THREADS) && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN) && !defined(_LIBCPP_AVAILABILITY_HAS_NO_SYNC)' is not met!"
 #   endif
 # endif
 

diff  --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index 566c595d8c30823..3804a8b2bd3459d 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
@@ -3369,16 +3369,16 @@
 #   error "__cpp_lib_is_swappable should have the value 201603L in c++20"
 # endif
 
-# if !defined(_LIBCPP_VERSION)
+# if !defined(_LIBCPP_HAS_NO_THREADS) && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN) && !defined(_LIBCPP_AVAILABILITY_HAS_NO_SYNC)
 #   ifndef __cpp_lib_jthread
 #     error "__cpp_lib_jthread should be defined in c++20"
 #   endif
 #   if __cpp_lib_jthread != 201911L
 #     error "__cpp_lib_jthread should have the value 201911L in c++20"
 #   endif
-# else // _LIBCPP_VERSION
+# else
 #   ifdef __cpp_lib_jthread
-#     error "__cpp_lib_jthread should not be defined because it is unimplemented in libc++!"
+#     error "__cpp_lib_jthread should not be defined when the requirement '!defined(_LIBCPP_HAS_NO_THREADS) && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN) && !defined(_LIBCPP_AVAILABILITY_HAS_NO_SYNC)' is not met!"
 #   endif
 # endif
 
@@ -4709,16 +4709,16 @@
 #   error "__cpp_lib_is_swappable should have the value 201603L in c++23"
 # endif
 
-# if !defined(_LIBCPP_VERSION)
+# if !defined(_LIBCPP_HAS_NO_THREADS) && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN) && !defined(_LIBCPP_AVAILABILITY_HAS_NO_SYNC)
 #   ifndef __cpp_lib_jthread
 #     error "__cpp_lib_jthread should be defined in c++23"
 #   endif
 #   if __cpp_lib_jthread != 201911L
 #     error "__cpp_lib_jthread should have the value 201911L in c++23"
 #   endif
-# else // _LIBCPP_VERSION
+# else
 #   ifdef __cpp_lib_jthread
-#     error "__cpp_lib_jthread should not be defined because it is unimplemented in libc++!"
+#     error "__cpp_lib_jthread should not be defined when the requirement '!defined(_LIBCPP_HAS_NO_THREADS) && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN) && !defined(_LIBCPP_AVAILABILITY_HAS_NO_SYNC)' is not met!"
 #   endif
 # endif
 
@@ -6250,16 +6250,16 @@
 #   error "__cpp_lib_is_swappable should have the value 201603L in c++26"
 # endif
 
-# if !defined(_LIBCPP_VERSION)
+# if !defined(_LIBCPP_HAS_NO_THREADS) && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN) && !defined(_LIBCPP_AVAILABILITY_HAS_NO_SYNC)
 #   ifndef __cpp_lib_jthread
 #     error "__cpp_lib_jthread should be defined in c++26"
 #   endif
 #   if __cpp_lib_jthread != 201911L
 #     error "__cpp_lib_jthread should have the value 201911L in c++26"
 #   endif
-# else // _LIBCPP_VERSION
+# else
 #   ifdef __cpp_lib_jthread
-#     error "__cpp_lib_jthread should not be defined because it is unimplemented in libc++!"
+#     error "__cpp_lib_jthread should not be defined when the requirement '!defined(_LIBCPP_HAS_NO_THREADS) && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN) && !defined(_LIBCPP_AVAILABILITY_HAS_NO_SYNC)' is not met!"
 #   endif
 # endif
 

diff  --git a/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_for_token_pred.pass.cpp b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_for_token_pred.pass.cpp
new file mode 100644
index 000000000000000..fb3f0287726eea0
--- /dev/null
+++ b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_for_token_pred.pass.cpp
@@ -0,0 +1,181 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-experimental-stop_token
+// XFAIL: availability-synchronization_library-missing
+
+// <condition_variable>
+
+// class condition_variable_any;
+
+// template<class Lock, class Rep, class Period, class Predicate>
+//   bool wait_for(Lock& lock, stop_token stoken,
+//                 const chrono::duration<Rep, Period>& rel_time, Predicate pred);
+
+#include <cassert>
+#include <chrono>
+#include <concepts>
+#include <condition_variable>
+#include <functional>
+#include <mutex>
+#include <shared_mutex>
+#include <stop_token>
+#include <thread>
+
+#include "make_test_thread.h"
+#include "test_macros.h"
+
+template <class Mutex, class Lock>
+void test() {
+  using namespace std::chrono_literals;
+
+  // stop_requested before hand
+  {
+    std::stop_source ss;
+    std::condition_variable_any cv;
+    Mutex mutex;
+    Lock lock{mutex};
+    ss.request_stop();
+
+    // [Note 4: The returned value indicates whether the predicate evaluated to true
+    // regardless of whether the timeout was triggered or a stop request was made.]
+    std::same_as<bool> auto r1 = cv.wait_for(lock, ss.get_token(), -1h, []() { return false; });
+    assert(!r1);
+
+    std::same_as<bool> auto r2 = cv.wait_for(lock, ss.get_token(), 1h, []() { return false; });
+    assert(!r2);
+
+    std::same_as<bool> auto r3 = cv.wait_for(lock, ss.get_token(), -1h, []() { return true; });
+    assert(r3);
+
+    std::same_as<bool> auto r4 = cv.wait_for(lock, ss.get_token(), 1h, []() { return true; });
+    assert(r4);
+
+    // Postconditions: lock is locked by the calling thread.
+    assert(lock.owns_lock());
+  }
+
+  // no stop request, pred was true
+  {
+    std::stop_source ss;
+    std::condition_variable_any cv;
+    Mutex mutex;
+    Lock lock{mutex};
+
+    std::same_as<bool> auto r1 = cv.wait_for(lock, ss.get_token(), -1h, []() { return true; });
+    assert(r1);
+
+    std::same_as<bool> auto r2 = cv.wait_for(lock, ss.get_token(), 1h, []() { return true; });
+    assert(r2);
+  }
+
+  // no stop request, pred was false, abs_time was in the past
+  {
+    std::stop_source ss;
+    std::condition_variable_any cv;
+    Mutex mutex;
+    Lock lock{mutex};
+
+    std::same_as<bool> auto r1 = cv.wait_for(lock, ss.get_token(), -1h, []() { return false; });
+    assert(!r1);
+  }
+
+  // no stop request, pred was false until timeout
+  {
+    std::stop_source ss;
+    std::condition_variable_any cv;
+    Mutex mutex;
+    Lock lock{mutex};
+
+    auto old_time = std::chrono::steady_clock::now();
+
+    std::same_as<bool> auto r1 = cv.wait_for(lock, ss.get_token(), 2ms, [&]() { return false; });
+
+    assert((std::chrono::steady_clock::now() - old_time) >= 2ms);
+    assert(!r1);
+  }
+
+  // no stop request, pred was false, changed to true before timeout
+  {
+    std::stop_source ss;
+    std::condition_variable_any cv;
+    Mutex mutex;
+    Lock lock{mutex};
+
+    bool flag   = false;
+    auto thread = support::make_test_thread([&]() {
+      std::this_thread::sleep_for(2ms);
+      Lock lock2{mutex};
+      flag = true;
+      cv.notify_all();
+    });
+
+    std::same_as<bool> auto r1 = cv.wait_for(lock, ss.get_token(), 1h, [&]() { return flag; });
+    assert(flag);
+    assert(r1);
+
+    thread.join();
+  }
+
+  // stop request comes while waiting
+  {
+    std::stop_source ss;
+    std::condition_variable_any cv;
+    Mutex mutex;
+    Lock lock{mutex};
+
+    std::atomic_bool start = false;
+    std::atomic_bool done  = false;
+    auto thread            = support::make_test_thread([&]() {
+      start.wait(false);
+      ss.request_stop();
+
+      while (!done) {
+        cv.notify_all();
+        std::this_thread::sleep_for(2ms);
+      }
+    });
+
+    std::same_as<bool> auto r = cv.wait_for(lock, ss.get_token(), 1h, [&]() {
+      start.store(true);
+      start.notify_all();
+      return false;
+    });
+    assert(!r);
+    done = true;
+    thread.join();
+
+    assert(lock.owns_lock());
+  }
+
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+  // Throws: Any exception thrown by pred.
+  {
+    std::stop_source ss;
+    std::condition_variable_any cv;
+    Mutex mutex;
+    Lock lock{mutex};
+
+    try {
+      cv.wait_for(lock, ss.get_token(), 1h, []() -> bool { throw 5; });
+      assert(false);
+    } catch (int i) {
+      assert(i == 5);
+    }
+  }
+#endif //!defined(TEST_HAS_NO_EXCEPTIONS)
+}
+
+int main(int, char**) {
+  test<std::mutex, std::unique_lock<std::mutex>>();
+  test<std::shared_mutex, std::shared_lock<std::shared_mutex>>();
+
+  return 0;
+}

diff  --git a/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_terminates.sh.cpp b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_terminates.sh.cpp
index 4e2086220e9632d..a01bc1d87faba9d 100644
--- a/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_terminates.sh.cpp
+++ b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_terminates.sh.cpp
@@ -20,6 +20,9 @@
 // RUN: %{run} 4
 // RUN: %{run} 5
 // RUN: %{run} 6
+// RUN: %{run} 7
+// RUN: %{run} 8
+// RUN: %{run} 9
 
 // -----------------------------------------------------------------------------
 // Overview
@@ -34,6 +37,9 @@
 //   4.  void wait_for(Lock& lock, Duration, Pred);
 //   5.  void wait_until(Lock& lock, TimePoint);
 //   6.  void wait_until(Lock& lock, TimePoint, Pred);
+//   7.  bool wait(Lock& lock, stop_token stoken, Predicate pred);
+//   8.  bool wait_for(Lock& lock, stop_token stoken, Duration, Predicate pred);
+//   9.  bool wait_until(Lock& lock, stop_token stoken, TimePoint, Predicate pred);
 //
 // Plan
 //   1 Create a mutex type, 'ThrowingMutex', that throws when the lock is acquired
@@ -62,9 +68,11 @@
 #include <cstdlib>
 #include <exception>
 #include <string>
+#include <stop_token>
 #include <thread>
 
 #include "make_test_thread.h"
+#include "test_macros.h"
 
 void my_terminate() {
   std::_Exit(0); // Use _Exit to prevent cleanup from taking place.
@@ -115,7 +123,7 @@ typedef std::chrono::milliseconds MS;
 int main(int argc, char **argv) {
   assert(argc == 2);
   int id = std::stoi(argv[1]);
-  assert(id >= 1 && id <= 6);
+  assert(id >= 1 && id <= 9);
   std::set_terminate(my_terminate); // set terminate after std::stoi because it can throw.
   MS wait(250);
   try {
@@ -129,6 +137,16 @@ int main(int argc, char **argv) {
       case 4: cv.wait_for(mut, wait, pred_function); break;
       case 5: cv.wait_until(mut, Clock::now() + wait); break;
       case 6: cv.wait_until(mut, Clock::now() + wait, pred_function); break;
+#if TEST_STD_VER >=20 && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN) && !defined(_LIBCPP_AVAILABILITY_HAS_NO_SYNC)
+      case 7: cv.wait(mut, std::stop_source{}.get_token(), pred_function); break;
+      case 8: cv.wait_for(mut, std::stop_source{}.get_token(), wait, pred_function); break;
+      case 9: cv.wait_until(mut, std::stop_source{}.get_token(), Clock::now() + wait, pred_function); break;
+#else
+      case 7:
+      case 8:
+      case 9:
+        return 0;
+#endif //TEST_STD_VER >=20 && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN)
       default: assert(false);
     }
   } catch (...) {}

diff  --git a/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_token_pred.pass.cpp b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_token_pred.pass.cpp
new file mode 100644
index 000000000000000..451df9ab7ee2874
--- /dev/null
+++ b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_token_pred.pass.cpp
@@ -0,0 +1,133 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-experimental-stop_token
+// XFAIL: availability-synchronization_library-missing
+
+// <condition_variable>
+
+// class condition_variable_any;
+
+// template<class Lock, class Predicate>
+//   bool wait(Lock& lock, stop_token stoken, Predicate pred);
+
+#include <cassert>
+#include <concepts>
+#include <condition_variable>
+#include <functional>
+#include <mutex>
+#include <shared_mutex>
+#include <stop_token>
+#include <thread>
+
+#include "make_test_thread.h"
+#include "test_macros.h"
+
+template <class Mutex, class Lock>
+void test() {
+  // stop_requested before hand
+  {
+    std::stop_source ss;
+    std::condition_variable_any cv;
+    Mutex mutex;
+    Lock lock{mutex};
+    ss.request_stop();
+
+    // [Note 1: The returned value indicates whether the predicate evaluated to true regardless of whether there was a stop request.]
+    std::same_as<bool> auto r1 = cv.wait(lock, ss.get_token(), []() { return false; });
+    assert(!r1);
+
+    std::same_as<bool> auto r2 = cv.wait(lock, ss.get_token(), []() { return true; });
+    assert(r2);
+
+    // Postconditions: lock is locked by the calling thread.
+    assert(lock.owns_lock());
+  }
+
+  // no stop request
+  {
+    std::stop_source ss;
+    std::condition_variable_any cv;
+    Mutex mutex;
+    Lock lock{mutex};
+    std::same_as<bool> auto r1 = cv.wait(lock, ss.get_token(), []() { return true; });
+    assert(r1);
+
+    bool flag   = false;
+    auto thread = support::make_test_thread([&]() {
+      std::this_thread::sleep_for(std::chrono::milliseconds(2));
+      Lock lock2{mutex};
+      flag = true;
+      cv.notify_all();
+    });
+
+    std::same_as<bool> auto r2 = cv.wait(lock, ss.get_token(), [&]() { return flag; });
+    assert(flag);
+    assert(r2);
+    thread.join();
+
+    assert(lock.owns_lock());
+  }
+
+  // stop request comes while waiting
+  {
+    std::stop_source ss;
+    std::condition_variable_any cv;
+    Mutex mutex;
+    Lock lock{mutex};
+
+    std::atomic_bool start = false;
+    std::atomic_bool done  = false;
+    auto thread            = support::make_test_thread([&]() {
+      start.wait(false);
+      ss.request_stop();
+
+      while (!done) {
+        cv.notify_all();
+        std::this_thread::sleep_for(std::chrono::milliseconds(2));
+      }
+    });
+
+    std::same_as<bool> auto r = cv.wait(lock, ss.get_token(), [&]() {
+      start.store(true);
+      start.notify_all();
+      return false;
+    });
+    assert(!r);
+    done = true;
+    thread.join();
+
+    assert(lock.owns_lock());
+  }
+
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+  // Throws: Any exception thrown by pred.
+  {
+    std::stop_source ss;
+    std::condition_variable_any cv;
+    Mutex mutex;
+    Lock lock{mutex};
+
+    try {
+      cv.wait(lock, ss.get_token(), []() -> bool { throw 5; });
+      assert(false);
+    } catch (int i) {
+      assert(i == 5);
+    }
+  }
+#endif //!defined(TEST_HAS_NO_EXCEPTIONS)
+}
+
+int main(int, char**) {
+  test<std::mutex, std::unique_lock<std::mutex>>();
+  test<std::shared_mutex, std::shared_lock<std::shared_mutex>>();
+
+  return 0;
+}

diff  --git a/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_until_token_pred.pass.cpp b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_until_token_pred.pass.cpp
new file mode 100644
index 000000000000000..6cdcbe36d98598b
--- /dev/null
+++ b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_until_token_pred.pass.cpp
@@ -0,0 +1,183 @@
+//===----------------------------------------------------------------------===//
+//
+// 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: no-threads
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-experimental-stop_token
+// XFAIL: availability-synchronization_library-missing
+
+// <condition_variable>
+
+// class condition_variable_any;
+
+// template<class Lock, class Clock, class Duration, class Predicate>
+//   bool wait_until(Lock& lock, stop_token stoken,
+//                   const chrono::time_point<Clock, Duration>& abs_time, Predicate pred);
+
+#include <cassert>
+#include <chrono>
+#include <concepts>
+#include <condition_variable>
+#include <functional>
+#include <mutex>
+#include <shared_mutex>
+#include <stop_token>
+#include <thread>
+
+#include "make_test_thread.h"
+#include "test_macros.h"
+
+template <class Mutex, class Lock>
+void test() {
+  const auto past   = std::chrono::steady_clock::now() - std::chrono::hours(1);
+  const auto future = std::chrono::steady_clock::now() + std::chrono::hours(1);
+
+  // stop_requested before hand
+  {
+    std::stop_source ss;
+    std::condition_variable_any cv;
+    Mutex mutex;
+    Lock lock{mutex};
+    ss.request_stop();
+
+    // [Note 4: The returned value indicates whether the predicate evaluated to true
+    // regardless of whether the timeout was triggered or a stop request was made.]
+    std::same_as<bool> auto r1 = cv.wait_until(lock, ss.get_token(), past, []() { return false; });
+    assert(!r1);
+
+    std::same_as<bool> auto r2 = cv.wait_until(lock, ss.get_token(), future, []() { return false; });
+    assert(!r2);
+
+    std::same_as<bool> auto r3 = cv.wait_until(lock, ss.get_token(), past, []() { return true; });
+    assert(r3);
+
+    std::same_as<bool> auto r4 = cv.wait_until(lock, ss.get_token(), future, []() { return true; });
+    assert(r4);
+
+    // Postconditions: lock is locked by the calling thread.
+    assert(lock.owns_lock());
+  }
+
+  // no stop request, pred was true
+  {
+    std::stop_source ss;
+    std::condition_variable_any cv;
+    Mutex mutex;
+    Lock lock{mutex};
+
+    std::same_as<bool> auto r1 = cv.wait_until(lock, ss.get_token(), past, []() { return true; });
+    assert(r1);
+
+    std::same_as<bool> auto r2 = cv.wait_until(lock, ss.get_token(), future, []() { return true; });
+    assert(r2);
+  }
+
+  // no stop request, pred was false, abs_time was in the past
+  {
+    std::stop_source ss;
+    std::condition_variable_any cv;
+    Mutex mutex;
+    Lock lock{mutex};
+
+    std::same_as<bool> auto r1 = cv.wait_until(lock, ss.get_token(), past, []() { return false; });
+    assert(!r1);
+  }
+
+  // no stop request, pred was false until timeout
+  {
+    std::stop_source ss;
+    std::condition_variable_any cv;
+    Mutex mutex;
+    Lock lock{mutex};
+
+    auto oldTime = std::chrono::steady_clock::now();
+
+    std::same_as<bool> auto r1 =
+        cv.wait_until(lock, ss.get_token(), oldTime + std::chrono::milliseconds(2), [&]() { return false; });
+
+    assert((std::chrono::steady_clock::now() - oldTime) >= std::chrono::milliseconds(2));
+    assert(!r1);
+  }
+
+  // no stop request, pred was false, changed to true before timeout
+  {
+    std::stop_source ss;
+    std::condition_variable_any cv;
+    Mutex mutex;
+    Lock lock{mutex};
+
+    bool flag   = false;
+    auto thread = support::make_test_thread([&]() {
+      std::this_thread::sleep_for(std::chrono::milliseconds(2));
+      Lock lock2{mutex};
+      flag = true;
+      cv.notify_all();
+    });
+
+    std::same_as<bool> auto r1 = cv.wait_until(lock, ss.get_token(), future, [&]() { return flag; });
+    assert(flag);
+    assert(r1);
+
+    thread.join();
+  }
+
+  // stop request comes while waiting
+  {
+    std::stop_source ss;
+    std::condition_variable_any cv;
+    Mutex mutex;
+    Lock lock{mutex};
+
+    std::atomic_bool start = false;
+    std::atomic_bool done  = false;
+    auto thread            = support::make_test_thread([&]() {
+      start.wait(false);
+      ss.request_stop();
+
+      while (!done) {
+        cv.notify_all();
+        std::this_thread::sleep_for(std::chrono::milliseconds(2));
+      }
+    });
+
+    std::same_as<bool> auto r = cv.wait_until(lock, ss.get_token(), future, [&]() {
+      start.store(true);
+      start.notify_all();
+      return false;
+    });
+    assert(!r);
+    done = true;
+    thread.join();
+
+    assert(lock.owns_lock());
+  }
+
+#if !defined(TEST_HAS_NO_EXCEPTIONS)
+  // Throws: Any exception thrown by pred.
+  {
+    std::stop_source ss;
+    std::condition_variable_any cv;
+    Mutex mutex;
+    Lock lock{mutex};
+
+    try {
+      cv.wait_until(lock, ss.get_token(), future, []() -> bool { throw 5; });
+      assert(false);
+    } catch (int i) {
+      assert(i == 5);
+    }
+  }
+#endif //!defined(TEST_HAS_NO_EXCEPTIONS)
+}
+
+int main(int, char**) {
+  test<std::mutex, std::unique_lock<std::mutex>>();
+  test<std::shared_mutex, std::shared_lock<std::shared_mutex>>();
+
+  return 0;
+}

diff  --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index ac342aff0beb701..4b114d3824d7bb8 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -629,9 +629,8 @@ def add_version_header(tc):
             "name": "__cpp_lib_jthread",
             "values": {"c++20": 201911},
             "headers": ["stop_token", "thread"],
-            "test_suite_guard": "!defined(_LIBCPP_HAS_NO_THREADS)",
-            "libcxx_guard": "!defined(_LIBCPP_HAS_NO_THREADS)",
-            "unimplemented": True,
+            "test_suite_guard": "!defined(_LIBCPP_HAS_NO_THREADS) && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN) && !defined(_LIBCPP_AVAILABILITY_HAS_NO_SYNC)",
+            "libcxx_guard": "!defined(_LIBCPP_HAS_NO_THREADS) && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN) && !defined(_LIBCPP_AVAILABILITY_HAS_NO_SYNC)",
         },
         {
             "name": "__cpp_lib_latch",


        


More information about the libcxx-commits mailing list