1 //===----------------------------------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // UNSUPPORTED: no-threads 10 // UNSUPPORTED: c++03, c++11, c++14, c++17 11 // XFAIL: availability-synchronization_library-missing 12 13 // template<class F, class... Args> 14 // explicit jthread(F&& f, Args&&... args); 15 16 #include <cassert> 17 #include <stop_token> 18 #include <thread> 19 #include <type_traits> 20 #include <utility> 21 22 #include "test_macros.h" 23 24 template <class... Args> 25 struct Func { 26 void operator()(Args...) const; 27 }; 28 29 // Constraints: remove_cvref_t<F> is not the same type as jthread. 30 static_assert(std::is_constructible_v<std::jthread, Func<>>); 31 static_assert(std::is_constructible_v<std::jthread, Func<int>, int>); 32 static_assert(!std::is_constructible_v<std::jthread, std::jthread const&>); 33 34 // explicit 35 template <class T> 36 void conversion_test(T); 37 38 template <class T, class... Args> 39 concept ImplicitlyConstructible = requires(Args&&... args) { conversion_test<T>({std::forward<Args>(args)...}); }; 40 41 static_assert(!ImplicitlyConstructible<std::jthread, Func<>>); 42 static_assert(!ImplicitlyConstructible<std::jthread, Func<int>, int>); 43 44 int main(int, char**) { 45 // Effects: Initializes ssource 46 // Postconditions: get_id() != id() is true and ssource.stop_possible() is true 47 // and *this represents the newly started thread. 48 { 49 std::jthread jt{[] {}}; 50 assert(jt.get_stop_source().stop_possible()); 51 assert(jt.get_id() != std::jthread::id()); 52 } 53 54 // The new thread of execution executes 55 // invoke(auto(std::forward<F>(f)), get_stop_token(), auto(std::forward<Args>(args))...) 56 // if that expression is well-formed, 57 { 58 int result = 0; 59 std::jthread jt{[&result](std::stop_token st, int i) { 60 assert(st.stop_possible()); 61 assert(!st.stop_requested()); 62 result += i; 63 }, 64 5}; 65 jt.join(); 66 assert(result == 5); 67 } 68 69 // otherwise 70 // invoke(auto(std::forward<F>(f)), auto(std::forward<Args>(args))...) 71 { 72 int result = 0; 73 std::jthread jt{[&result](int i) { result += i; }, 5}; 74 jt.join(); 75 assert(result == 5); 76 } 77 78 // with the values produced by auto being materialized ([conv.rval]) in the constructing thread. 79 { 80 struct TrackThread { 81 std::jthread::id threadId; 82 bool copyConstructed = false; 83 bool moveConstructed = false; 84 85 TrackThread() : threadId(std::this_thread::get_id()) {} 86 TrackThread(const TrackThread&) : threadId(std::this_thread::get_id()), copyConstructed(true) {} 87 TrackThread(TrackThread&&) : threadId(std::this_thread::get_id()), moveConstructed(true) {} 88 }; 89 90 auto mainThread = std::this_thread::get_id(); 91 92 TrackThread arg1; 93 std::jthread jt1{[mainThread](const TrackThread& arg) { 94 assert(arg.threadId == mainThread); 95 assert(arg.threadId != std::this_thread::get_id()); 96 assert(arg.copyConstructed); 97 }, 98 arg1}; 99 100 TrackThread arg2; 101 std::jthread jt2{[mainThread](const TrackThread& arg) { 102 assert(arg.threadId == mainThread); 103 assert(arg.threadId != std::this_thread::get_id()); 104 assert(arg.moveConstructed); 105 }, 106 std::move(arg2)}; 107 } 108 109 #if !defined(TEST_HAS_NO_EXCEPTIONS) 110 // [Note 1: This implies that any exceptions not thrown from the invocation of the copy 111 // of f will be thrown in the constructing thread, not the new thread. - end note] 112 { 113 struct Exception { 114 std::jthread::id threadId; 115 }; 116 struct ThrowOnCopyFunc { 117 ThrowOnCopyFunc() = default; 118 ThrowOnCopyFunc(const ThrowOnCopyFunc&) { throw Exception{std::this_thread::get_id()}; } 119 void operator()() const {} 120 }; 121 ThrowOnCopyFunc f1; 122 try { 123 std::jthread jt{f1}; 124 assert(false); 125 } catch (const Exception& e) { 126 assert(e.threadId == std::this_thread::get_id()); 127 } 128 } 129 #endif // !defined(TEST_HAS_NO_EXCEPTIONS) 130 131 // Synchronization: The completion of the invocation of the constructor 132 // synchronizes with the beginning of the invocation of the copy of f. 133 { 134 int flag = 0; 135 struct Arg { 136 int& flag_; 137 Arg(int& f) : flag_(f) {} 138 139 Arg(const Arg& other) : flag_(other.flag_) { flag_ = 5; } 140 }; 141 142 Arg arg(flag); 143 std::jthread jt( 144 [&flag](const auto&) { 145 assert(flag == 5); // happens-after the copy-construction of arg 146 }, 147 arg); 148 } 149 150 // Per https://eel.is/c++draft/thread.jthread.class#thread.jthread.cons-8: 151 // 152 // Throws: system_error if unable to start the new thread. 153 // Error conditions: 154 // resource_unavailable_try_again - the system lacked the necessary resources to create another thread, 155 // or the system-imposed limit on the number of threads in a process would be exceeded. 156 // 157 // Unfortunately, this is extremely hard to test portably so we don't have a test for this error condition right now. 158 159 return 0; 160 } 161