diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst --- a/libcxx/docs/FeatureTestMacroTable.rst +++ b/libcxx/docs/FeatureTestMacroTable.rst @@ -252,7 +252,7 @@ --------------------------------------------------- ----------------- ``__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 --- a/libcxx/docs/Status/Cxx20.rst +++ b/libcxx/docs/Status/Cxx20.rst @@ -48,7 +48,7 @@ .. [#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 --- a/libcxx/docs/Status/Cxx20Papers.csv +++ b/libcxx/docs/Status/Cxx20Papers.csv @@ -104,7 +104,7 @@ "`P0553R4 `__","LWG","Bit operations","Cologne","|Complete|","9.0" "`P0631R8 `__","LWG","Math Constants","Cologne","|Complete|","11.0" "`P0645R10 `__","LWG","Text Formatting","Cologne","|Complete| [#note-P0645]_","14.0" -"`P0660R10 `__","LWG","Stop Token and Joining Thread, Rev 10.","Cologne","|In Progress| [#note-P0660]_","" +"`P0660R10 `__","LWG","Stop Token and Joining Thread, Rev 10.","Cologne","|Done| [#note-P0660]_","18.0.0" "`P0784R7 `__","CWG","More constexpr containers","Cologne","|Complete|","12.0" "`P0980R1 `__","LWG","Making std::string constexpr","Cologne","|Complete|","15.0" "`P1004R2 `__","LWG","Making std::vector constexpr","Cologne","|Complete|","15.0" diff --git a/libcxx/include/condition_variable b/libcxx/include/condition_variable --- a/libcxx/include/condition_variable +++ b/libcxx/include/condition_variable @@ -100,6 +100,18 @@ wait_for(Lock& lock, const chrono::duration& rel_time, Predicate pred); + + // [thread.condvarany.intwait], interruptible waits + template + bool wait(Lock& lock, stop_token stoken, Predicate pred); // since C++20 + + template + bool wait_until(Lock& lock, stop_token stoken, + const chrono::time_point& abs_time, Predicate pred); // since C++20 + + template + bool wait_for(Lock& lock, stop_token stoken, + const chrono::duration& rel_time, Predicate pred); // since C++20 }; } // std @@ -107,6 +119,7 @@ */ #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 @@ #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 @@ -174,6 +188,21 @@ 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 + _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI bool wait(_Lock& __lock, stop_token __stoken, _Predicate __pred); + + template + _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 + _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 @@ _VSTD::move(__pred)); } +#if _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN) + +template +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 +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 +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); _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/version b/libcxx/include/version --- a/libcxx/include/version +++ b/libcxx/include/version @@ -372,8 +372,8 @@ // # 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) +# 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 --- a/libcxx/test/libcxx/transitive_includes/cxx03.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv @@ -175,6 +175,7 @@ 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 --- a/libcxx/test/libcxx/transitive_includes/cxx11.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv @@ -176,6 +176,7 @@ 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 --- a/libcxx/test/libcxx/transitive_includes/cxx14.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv @@ -176,6 +176,7 @@ 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 --- a/libcxx/test/libcxx/transitive_includes/cxx17.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv @@ -176,6 +176,7 @@ 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 --- a/libcxx/test/libcxx/transitive_includes/cxx20.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv @@ -182,6 +182,7 @@ 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 --- a/libcxx/test/libcxx/transitive_includes/cxx23.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv @@ -118,8 +118,11 @@ 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 --- a/libcxx/test/libcxx/transitive_includes/cxx26.csv +++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv @@ -118,8 +118,11 @@ 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 --- 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) # 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)' 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) # 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)' 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) # 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)' 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 --- 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) # 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)' 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) # 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)' 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) # 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)' 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 --- 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) # 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)' 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) # 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)' 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) # 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)' 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 --- /dev/null +++ b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_for_token_pred.pass.cpp @@ -0,0 +1,176 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +// class condition_variable_any; + +// template +// bool wait_for(Lock& lock, stop_token stoken, +// const chrono::duration& rel_time, Predicate pred); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "make_test_thread.h" +#include "test_macros.h" + +template +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 auto r1 = cv.wait_for(lock, ss.get_token(), -1h, []() { return false; }); + assert(!r1); + + std::same_as auto r2 = cv.wait_for(lock, ss.get_token(), 1h, []() { return false; }); + assert(!r2); + + std::same_as auto r3 = cv.wait_for(lock, ss.get_token(), -1h, []() { return true; }); + assert(r3); + + std::same_as 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 auto r1 = cv.wait_for(lock, ss.get_token(), -1h, []() { return true; }); + assert(r1); + + std::same_as 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 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 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 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 done = false; + auto thread = support::make_test_thread([&]() { + std::this_thread::sleep_for(2ms); + ss.request_stop(); + + while (!done) { + cv.notify_all(); + std::this_thread::sleep_for(2ms); + } + }); + + std::same_as auto r = cv.wait_for(lock, ss.get_token(), 1h, [&]() { 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>(); + test>(); + + 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 --- 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 #include #include +#include #include #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 @@ 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 @@ 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) + 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 --- /dev/null +++ b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_token_pred.pass.cpp @@ -0,0 +1,128 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +// class condition_variable_any; + +// template +// bool wait(Lock& lock, stop_token stoken, Predicate pred); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "make_test_thread.h" +#include "test_macros.h" + +template +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 auto r1 = cv.wait(lock, ss.get_token(), []() { return false; }); + assert(!r1); + + std::same_as 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 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 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 done = false; + auto thread = support::make_test_thread([&]() { + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + ss.request_stop(); + + while (!done) { + cv.notify_all(); + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } + }); + + std::same_as auto r = cv.wait(lock, ss.get_token(), [&]() { 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>(); + test>(); + + 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 --- /dev/null +++ b/libcxx/test/std/thread/thread.condition/thread.condition.condvarany/wait_until_token_pred.pass.cpp @@ -0,0 +1,178 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +// class condition_variable_any; + +// template +// bool wait_until(Lock& lock, stop_token stoken, +// const chrono::time_point& abs_time, Predicate pred); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "make_test_thread.h" +#include "test_macros.h" + +template +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 auto r1 = cv.wait_until(lock, ss.get_token(), past, []() { return false; }); + assert(!r1); + + std::same_as auto r2 = cv.wait_until(lock, ss.get_token(), future, []() { return false; }); + assert(!r2); + + std::same_as auto r3 = cv.wait_until(lock, ss.get_token(), past, []() { return true; }); + assert(r3); + + std::same_as 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 auto r1 = cv.wait_until(lock, ss.get_token(), past, []() { return true; }); + assert(r1); + + std::same_as 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 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 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 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 done = false; + auto thread = support::make_test_thread([&]() { + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + ss.request_stop(); + + while (!done) { + cv.notify_all(); + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + } + }); + + std::same_as auto r = cv.wait_until(lock, ss.get_token(), future, [&]() { 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>(); + test>(); + + return 0; +} diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -629,9 +629,8 @@ "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)", + "libcxx_guard": "!defined(_LIBCPP_HAS_NO_THREADS) && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN)", }, { "name": "__cpp_lib_latch",