1695138caSHui //===----------------------------------------------------------------------===// 2695138caSHui // 3695138caSHui // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4695138caSHui // See https://llvm.org/LICENSE.txt for license information. 5695138caSHui // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6695138caSHui // 7695138caSHui //===----------------------------------------------------------------------===// 8695138caSHui // 9695138caSHui // UNSUPPORTED: no-threads 10695138caSHui // UNSUPPORTED: c++03, c++11, c++14, c++17 11695138caSHui // XFAIL: availability-synchronization_library-missing 12695138caSHui 13695138caSHui // template<class F, class... Args> 14695138caSHui // explicit jthread(F&& f, Args&&... args); 15695138caSHui 16695138caSHui #include <cassert> 17695138caSHui #include <stop_token> 18695138caSHui #include <thread> 19695138caSHui #include <type_traits> 20*09e3a360SLouis Dionne #include <utility> 21695138caSHui 22695138caSHui #include "test_macros.h" 23695138caSHui 24695138caSHui template <class... Args> 25695138caSHui struct Func { 26695138caSHui void operator()(Args...) const; 27695138caSHui }; 28695138caSHui 29695138caSHui // Constraints: remove_cvref_t<F> is not the same type as jthread. 30695138caSHui static_assert(std::is_constructible_v<std::jthread, Func<>>); 31695138caSHui static_assert(std::is_constructible_v<std::jthread, Func<int>, int>); 32695138caSHui static_assert(!std::is_constructible_v<std::jthread, std::jthread const&>); 33695138caSHui 34695138caSHui // explicit 35695138caSHui template <class T> 36695138caSHui void conversion_test(T); 37695138caSHui 38695138caSHui template <class T, class... Args> 39695138caSHui concept ImplicitlyConstructible = requires(Args&&... args) { conversion_test<T>({std::forward<Args>(args)...}); }; 40695138caSHui 41695138caSHui static_assert(!ImplicitlyConstructible<std::jthread, Func<>>); 42695138caSHui static_assert(!ImplicitlyConstructible<std::jthread, Func<int>, int>); 43695138caSHui 44695138caSHui int main(int, char**) { 45695138caSHui // Effects: Initializes ssource 46695138caSHui // Postconditions: get_id() != id() is true and ssource.stop_possible() is true 47695138caSHui // and *this represents the newly started thread. 48695138caSHui { 49695138caSHui std::jthread jt{[] {}}; 50695138caSHui assert(jt.get_stop_source().stop_possible()); 51695138caSHui assert(jt.get_id() != std::jthread::id()); 52695138caSHui } 53695138caSHui 54695138caSHui // The new thread of execution executes 55695138caSHui // invoke(auto(std::forward<F>(f)), get_stop_token(), auto(std::forward<Args>(args))...) 56695138caSHui // if that expression is well-formed, 57695138caSHui { 58695138caSHui int result = 0; 59695138caSHui std::jthread jt{[&result](std::stop_token st, int i) { 60695138caSHui assert(st.stop_possible()); 61695138caSHui assert(!st.stop_requested()); 62695138caSHui result += i; 63695138caSHui }, 64695138caSHui 5}; 65695138caSHui jt.join(); 66695138caSHui assert(result == 5); 67695138caSHui } 68695138caSHui 69695138caSHui // otherwise 70695138caSHui // invoke(auto(std::forward<F>(f)), auto(std::forward<Args>(args))...) 71695138caSHui { 72695138caSHui int result = 0; 73695138caSHui std::jthread jt{[&result](int i) { result += i; }, 5}; 74695138caSHui jt.join(); 75695138caSHui assert(result == 5); 76695138caSHui } 77695138caSHui 78695138caSHui // with the values produced by auto being materialized ([conv.rval]) in the constructing thread. 79695138caSHui { 80695138caSHui struct TrackThread { 81695138caSHui std::jthread::id threadId; 82695138caSHui bool copyConstructed = false; 83695138caSHui bool moveConstructed = false; 84695138caSHui 85695138caSHui TrackThread() : threadId(std::this_thread::get_id()) {} 86695138caSHui TrackThread(const TrackThread&) : threadId(std::this_thread::get_id()), copyConstructed(true) {} 87695138caSHui TrackThread(TrackThread&&) : threadId(std::this_thread::get_id()), moveConstructed(true) {} 88695138caSHui }; 89695138caSHui 90695138caSHui auto mainThread = std::this_thread::get_id(); 91695138caSHui 92695138caSHui TrackThread arg1; 93695138caSHui std::jthread jt1{[mainThread](const TrackThread& arg) { 94695138caSHui assert(arg.threadId == mainThread); 95695138caSHui assert(arg.threadId != std::this_thread::get_id()); 96695138caSHui assert(arg.copyConstructed); 97695138caSHui }, 98695138caSHui arg1}; 99695138caSHui 100695138caSHui TrackThread arg2; 101695138caSHui std::jthread jt2{[mainThread](const TrackThread& arg) { 102695138caSHui assert(arg.threadId == mainThread); 103695138caSHui assert(arg.threadId != std::this_thread::get_id()); 104695138caSHui assert(arg.moveConstructed); 105695138caSHui }, 106695138caSHui std::move(arg2)}; 107695138caSHui } 108695138caSHui 109695138caSHui #if !defined(TEST_HAS_NO_EXCEPTIONS) 110695138caSHui // [Note 1: This implies that any exceptions not thrown from the invocation of the copy 111695138caSHui // of f will be thrown in the constructing thread, not the new thread. - end note] 112695138caSHui { 113695138caSHui struct Exception { 114695138caSHui std::jthread::id threadId; 115695138caSHui }; 116695138caSHui struct ThrowOnCopyFunc { 117695138caSHui ThrowOnCopyFunc() = default; 118695138caSHui ThrowOnCopyFunc(const ThrowOnCopyFunc&) { throw Exception{std::this_thread::get_id()}; } 119695138caSHui void operator()() const {} 120695138caSHui }; 121695138caSHui ThrowOnCopyFunc f1; 122695138caSHui try { 123695138caSHui std::jthread jt{f1}; 124695138caSHui assert(false); 125695138caSHui } catch (const Exception& e) { 126695138caSHui assert(e.threadId == std::this_thread::get_id()); 127695138caSHui } 128695138caSHui } 129695138caSHui #endif // !defined(TEST_HAS_NO_EXCEPTIONS) 130695138caSHui 131695138caSHui // Synchronization: The completion of the invocation of the constructor 132695138caSHui // synchronizes with the beginning of the invocation of the copy of f. 133695138caSHui { 134695138caSHui int flag = 0; 135695138caSHui struct Arg { 136695138caSHui int& flag_; 137695138caSHui Arg(int& f) : flag_(f) {} 138695138caSHui 139695138caSHui Arg(const Arg& other) : flag_(other.flag_) { flag_ = 5; } 140695138caSHui }; 141695138caSHui 142695138caSHui Arg arg(flag); 143695138caSHui std::jthread jt( 144695138caSHui [&flag](const auto&) { 145695138caSHui assert(flag == 5); // happens-after the copy-construction of arg 146695138caSHui }, 147695138caSHui arg); 148695138caSHui } 149695138caSHui 150695138caSHui // Per https://eel.is/c++draft/thread.jthread.class#thread.jthread.cons-8: 151695138caSHui // 152695138caSHui // Throws: system_error if unable to start the new thread. 153695138caSHui // Error conditions: 154695138caSHui // resource_unavailable_try_again - the system lacked the necessary resources to create another thread, 155695138caSHui // or the system-imposed limit on the number of threads in a process would be exceeded. 156695138caSHui // 157695138caSHui // Unfortunately, this is extremely hard to test portably so we don't have a test for this error condition right now. 158695138caSHui 159695138caSHui return 0; 160695138caSHui } 161