xref: /aosp_15_r20/external/cronet/third_party/libc++/src/test/std/thread/thread.jthread/cons.func.token.pass.cpp (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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 
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 
main(int,char **)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