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

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