xref: /llvm-project/libcxx/test/std/thread/thread.jthread/cons.func.token.pass.cpp (revision 121ed5c1985356436d0040dbe81bca26992b1fae)
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