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: libcpp-has-no-experimental-stop_token
11// UNSUPPORTED: c++03, c++11, c++14, c++17
12// XFAIL: availability-synchronization_library-missing
13
14// template<class F, class... Args>
15// explicit jthread(F&& f, Args&&... args);
16
17#include <cassert>
18#include <stop_token>
19#include <thread>
20#include <type_traits>
21
22#include "test_macros.h"
23
24template <class... Args>
25struct Func {
26 void operator()(Args...) const;
27};
28
29// Constraints: remove_cvref_t<F> is not the same type as jthread.
30static_assert(std::is_constructible_v<std::jthread, Func<>>);
31static_assert(std::is_constructible_v<std::jthread, Func<int>, int>);
32static_assert(!std::is_constructible_v<std::jthread, std::jthread const&>);
33
34// explicit
35template <class T>
36void conversion_test(T);
37
38template <class T, class... Args>
39concept ImplicitlyConstructible = requires(Args&&... args) { conversion_test<T>({std::forward<Args>(args)...}); };
40
41static_assert(!ImplicitlyConstructible<std::jthread, Func<>>);
42static_assert(!ImplicitlyConstructible<std::jthread, Func<int>, int>);
43
44int 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

source code of libcxx/test/std/thread/thread.jthread/cons.func.token.pass.cpp