| 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{.threadId: 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 | |