[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