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 C>
14// explicit stop_callback(stop_token&& st, C&& cb)
15// noexcept(is_nothrow_constructible_v<Callback, C>);
16
17#include <atomic>
18#include <cassert>
19#include <chrono>
20#include <stop_token>
21#include <type_traits>
22#include <utility>
23#include <vector>
24
25#include "make_test_thread.h"
26#include "test_macros.h"
27
28struct Cb {
29 void operator()() const;
30};
31
32// Constraints: Callback and C satisfy constructible_from<Callback, C>.
33static_assert(std::is_constructible_v<std::stop_callback<void (*)()>, std::stop_token&&, void (*)()>);
34static_assert(!std::is_constructible_v<std::stop_callback<void (*)()>, std::stop_token&&, void (*)(int)>);
35static_assert(std::is_constructible_v<std::stop_callback<Cb>, std::stop_token&&, Cb&>);
36static_assert(std::is_constructible_v<std::stop_callback<Cb&>, std::stop_token&&, Cb&>);
37static_assert(!std::is_constructible_v<std::stop_callback<Cb>, std::stop_token&&, int>);
38
39// explicit
40template <class T>
41void conversion_test(T);
42
43template <class T, class... Args>
44concept ImplicitlyConstructible = requires(Args&&... args) { conversion_test<T>({std::forward<Args>(args)...}); };
45static_assert(ImplicitlyConstructible<int, int>);
46static_assert(!ImplicitlyConstructible<std::stop_callback<Cb>, std::stop_token&&, Cb>);
47
48// noexcept
49template <bool NoExceptCtor>
50struct CbNoExcept {
51 CbNoExcept(int) noexcept(NoExceptCtor);
52 void operator()() const;
53};
54static_assert(std::is_nothrow_constructible_v<std::stop_callback<CbNoExcept<true>>, std::stop_token&&, int>);
55static_assert(!std::is_nothrow_constructible_v<std::stop_callback<CbNoExcept<false>>, std::stop_token&&, int>);
56
57int main(int, char**) {
58 // was requested
59 {
60 std::stop_source ss;
61 ss.request_stop();
62
63 bool called = false;
64 std::stop_callback sc(ss.get_token(), [&] { called = true; });
65 assert(called);
66 }
67
68 // was not requested
69 {
70 std::stop_source ss;
71
72 bool called = false;
73 std::stop_callback sc(ss.get_token(), [&] { called = true; });
74 assert(!called);
75
76 ss.request_stop();
77 assert(called);
78 }
79
80 // token has no state
81 {
82 std::stop_token st;
83 bool called = false;
84 std::stop_callback sc(std::move(st), [&] { called = true; });
85 assert(!called);
86 }
87
88 // should not be called multiple times
89 {
90 std::stop_source ss;
91
92 int calledTimes = 0;
93 std::stop_callback sc(ss.get_token(), [&] { ++calledTimes; });
94
95 std::vector<std::thread> threads;
96 for (auto i = 0; i < 10; ++i) {
97 threads.emplace_back(support::make_test_thread([&] { ss.request_stop(); }));
98 }
99
100 for (auto& thread : threads) {
101 thread.join();
102 }
103 assert(calledTimes == 1);
104 }
105
106 // adding more callbacks during invoking other callbacks
107 {
108 std::stop_source ss;
109
110 std::atomic<bool> startedFlag = false;
111 std::atomic<bool> finishFlag = false;
112 std::stop_callback sc(ss.get_token(), [&] {
113 startedFlag = true;
114 startedFlag.notify_all();
115 finishFlag.wait(false);
116 });
117
118 auto thread = support::make_test_thread([&] { ss.request_stop(); });
119
120 startedFlag.wait(false);
121
122 // first callback is still running, adding another one;
123 bool secondCallbackCalled = false;
124 std::stop_callback sc2(ss.get_token(), [&] { secondCallbackCalled = true; });
125
126 finishFlag = true;
127 finishFlag.notify_all();
128
129 thread.join();
130 assert(secondCallbackCalled);
131 }
132
133 // adding callbacks on different threads
134 {
135 std::stop_source ss;
136
137 std::vector<std::thread> threads;
138 std::atomic<int> callbackCalledTimes = 0;
139 std::atomic<bool> done = false;
140 for (auto i = 0; i < 10; ++i) {
141 threads.emplace_back(support::make_test_thread([&] {
142 std::stop_callback sc{ss.get_token(), [&] { callbackCalledTimes.fetch_add(1, std::memory_order_relaxed); }};
143 done.wait(false);
144 }));
145 }
146 using namespace std::chrono_literals;
147 std::this_thread::sleep_for(1ms);
148 ss.request_stop();
149 done = true;
150 done.notify_all();
151 for (auto& thread : threads) {
152 thread.join();
153 }
154 assert(callbackCalledTimes.load(std::memory_order_relaxed) == 10);
155 }
156
157 // correct overload
158 {
159 struct CBWithTracking {
160 bool& lvalueCalled;
161 bool& lvalueConstCalled;
162 bool& rvalueCalled;
163 bool& rvalueConstCalled;
164
165 void operator()() & { lvalueCalled = true; }
166 void operator()() const& { lvalueConstCalled = true; }
167 void operator()() && { rvalueCalled = true; }
168 void operator()() const&& { rvalueConstCalled = true; }
169 };
170
171 // RValue
172 {
173 bool lvalueCalled = false;
174 bool lvalueConstCalled = false;
175 bool rvalueCalled = false;
176 bool rvalueConstCalled = false;
177 std::stop_source ss;
178 ss.request_stop();
179
180 std::stop_callback<CBWithTracking> sc(
181 ss.get_token(), CBWithTracking{lvalueCalled, lvalueConstCalled, rvalueCalled, rvalueConstCalled});
182 assert(rvalueCalled);
183 }
184
185 // RValue
186 {
187 bool lvalueCalled = false;
188 bool lvalueConstCalled = false;
189 bool rvalueCalled = false;
190 bool rvalueConstCalled = false;
191 std::stop_source ss;
192 ss.request_stop();
193
194 std::stop_callback<const CBWithTracking> sc(
195 ss.get_token(), CBWithTracking{lvalueCalled, lvalueConstCalled, rvalueCalled, rvalueConstCalled});
196 assert(rvalueConstCalled);
197 }
198
199 // LValue
200 {
201 bool lvalueCalled = false;
202 bool lvalueConstCalled = false;
203 bool rvalueCalled = false;
204 bool rvalueConstCalled = false;
205 std::stop_source ss;
206 ss.request_stop();
207 CBWithTracking cb{.lvalueCalled: lvalueCalled, .lvalueConstCalled: lvalueConstCalled, .rvalueCalled: rvalueCalled, .rvalueConstCalled: rvalueConstCalled};
208 std::stop_callback<CBWithTracking&> sc(ss.get_token(), cb);
209 assert(lvalueCalled);
210 }
211
212 // const LValue
213 {
214 bool lvalueCalled = false;
215 bool lvalueConstCalled = false;
216 bool rvalueCalled = false;
217 bool rvalueConstCalled = false;
218 std::stop_source ss;
219 ss.request_stop();
220 CBWithTracking cb{.lvalueCalled: lvalueCalled, .lvalueConstCalled: lvalueConstCalled, .rvalueCalled: rvalueCalled, .rvalueConstCalled: rvalueConstCalled};
221 std::stop_callback<const CBWithTracking&> sc(ss.get_token(), cb);
222 assert(lvalueConstCalled);
223 }
224 }
225
226 return 0;
227}
228

source code of libcxx/test/std/thread/thread.stoptoken/stopcallback/cons.rvalue.token.pass.cpp