1 //===----------------------------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
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
15 // explicit stop_callback(const stop_token& st, C&& cb)
16 // noexcept(is_nothrow_constructible_v<Callback, C>);
22 #include <type_traits>
26 #include "make_test_thread.h"
27 #include "test_macros.h"
30 void operator()() const;
33 // Constraints: Callback and C satisfy constructible_from<Callback, C>.
34 static_assert(std::is_constructible_v
<std::stop_callback
<void (*)()>, const std::stop_token
&, void (*)()>);
35 static_assert(!std::is_constructible_v
<std::stop_callback
<void (*)()>, const std::stop_token
&, void (*)(int)>);
36 static_assert(std::is_constructible_v
<std::stop_callback
<Cb
>, const std::stop_token
&, Cb
&>);
37 static_assert(std::is_constructible_v
<std::stop_callback
<Cb
&>, const std::stop_token
&, Cb
&>);
38 static_assert(!std::is_constructible_v
<std::stop_callback
<Cb
>, const std::stop_token
&, int>);
42 void conversion_test(T
);
44 template <class T
, class... Args
>
45 concept ImplicitlyConstructible
= requires(Args
&&... args
) { conversion_test
<T
>({std::forward
<Args
>(args
)...}); };
46 static_assert(ImplicitlyConstructible
<int, int>);
47 static_assert(!ImplicitlyConstructible
<std::stop_callback
<Cb
>, const std::stop_token
&, Cb
>);
50 template <bool NoExceptCtor
>
52 CbNoExcept(int) noexcept(NoExceptCtor
);
53 void operator()() const;
55 static_assert(std::is_nothrow_constructible_v
<std::stop_callback
<CbNoExcept
<true>>, const std::stop_token
&, int>);
56 static_assert(!std::is_nothrow_constructible_v
<std::stop_callback
<CbNoExcept
<false>>, const std::stop_token
&, int>);
58 int main(int, char**) {
62 const std::stop_token st
= ss
.get_token();
66 std::stop_callback
sc(st
, [&] { called
= true; });
73 const std::stop_token st
= ss
.get_token();
76 std::stop_callback
sc(st
, [&] { called
= true; });
87 std::stop_callback
sc(st
, [&] { called
= true; });
91 // should not be called multiple times
94 const std::stop_token st
= ss
.get_token();
97 std::stop_callback
sc(st
, [&] { ++calledTimes
; });
99 std::vector
<std::thread
> threads
;
100 for (auto i
= 0; i
< 10; ++i
) {
101 threads
.emplace_back(support::make_test_thread([&] { ss
.request_stop(); }));
104 for (auto& thread
: threads
) {
107 assert(calledTimes
== 1);
110 // adding more callbacks during invoking other callbacks
113 const std::stop_token st
= ss
.get_token();
115 std::atomic
<bool> startedFlag
= false;
116 std::atomic
<bool> finishFlag
= false;
117 std::stop_callback
sc(st
, [&] {
119 startedFlag
.notify_all();
120 finishFlag
.wait(false);
123 auto thread
= support::make_test_thread([&] { ss
.request_stop(); });
125 startedFlag
.wait(false);
127 // first callback is still running, adding another one;
128 bool secondCallbackCalled
= false;
129 std::stop_callback
sc2(st
, [&] { secondCallbackCalled
= true; });
132 finishFlag
.notify_all();
135 assert(secondCallbackCalled
);
138 // adding callbacks on different threads
141 const std::stop_token st
= ss
.get_token();
143 std::vector
<std::thread
> threads
;
144 std::atomic
<int> callbackCalledTimes
= 0;
145 std::atomic
<bool> done
= false;
146 for (auto i
= 0; i
< 10; ++i
) {
147 threads
.emplace_back(support::make_test_thread([&] {
148 std::stop_callback sc
{st
, [&] { callbackCalledTimes
.fetch_add(1, std::memory_order_relaxed
); }};
152 using namespace std::chrono_literals
;
153 std::this_thread::sleep_for(1ms
);
157 for (auto& thread
: threads
) {
160 assert(callbackCalledTimes
.load(std::memory_order_relaxed
) == 10);
165 struct CBWithTracking
{
167 bool& lvalueConstCalled
;
169 bool& rvalueConstCalled
;
171 void operator()() & { lvalueCalled
= true; }
172 void operator()() const& { lvalueConstCalled
= true; }
173 void operator()() && { rvalueCalled
= true; }
174 void operator()() const&& { rvalueConstCalled
= true; }
179 bool lvalueCalled
= false;
180 bool lvalueConstCalled
= false;
181 bool rvalueCalled
= false;
182 bool rvalueConstCalled
= false;
184 const std::stop_token st
= ss
.get_token();
187 std::stop_callback
<CBWithTracking
> sc(
188 st
, CBWithTracking
{lvalueCalled
, lvalueConstCalled
, rvalueCalled
, rvalueConstCalled
});
189 assert(rvalueCalled
);
194 bool lvalueCalled
= false;
195 bool lvalueConstCalled
= false;
196 bool rvalueCalled
= false;
197 bool rvalueConstCalled
= false;
199 const std::stop_token st
= ss
.get_token();
202 std::stop_callback
<const CBWithTracking
> sc(
203 st
, CBWithTracking
{lvalueCalled
, lvalueConstCalled
, rvalueCalled
, rvalueConstCalled
});
204 assert(rvalueConstCalled
);
209 bool lvalueCalled
= false;
210 bool lvalueConstCalled
= false;
211 bool rvalueCalled
= false;
212 bool rvalueConstCalled
= false;
214 const std::stop_token st
= ss
.get_token();
216 CBWithTracking cb
{lvalueCalled
, lvalueConstCalled
, rvalueCalled
, rvalueConstCalled
};
217 std::stop_callback
<CBWithTracking
&> sc(st
, cb
);
218 assert(lvalueCalled
);
223 bool lvalueCalled
= false;
224 bool lvalueConstCalled
= false;
225 bool rvalueCalled
= false;
226 bool rvalueConstCalled
= false;
228 const std::stop_token st
= ss
.get_token();
230 CBWithTracking cb
{lvalueCalled
, lvalueConstCalled
, rvalueCalled
, rvalueConstCalled
};
231 std::stop_callback
<const CBWithTracking
&> sc(st
, cb
);
232 assert(lvalueConstCalled
);