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
22 #include <type_traits>
26 #include "make_test_thread.h"
27 #include "test_macros.h"
29 struct CallbackHolder
;
32 CallbackHolder
& holder_
;
33 void operator()() const;
36 struct CallbackHolder
{
37 std::unique_ptr
<std::stop_callback
<DeleteHolder
>> callback_
;
40 void DeleteHolder::operator()() const { holder_
.callback_
.reset(); }
42 int main(int, char**) {
43 // Unregisters the callback from the owned stop state, if any
49 std::stop_callback
sc(ss
.get_token(), [&] { called
= true; });
55 // The destructor does not block waiting for the execution of another
56 // callback registered by an associated stop_callback.
60 std::atomic
<int> startedIndex
= 0;
61 std::atomic
<bool> callbackFinish
= false;
63 std::optional
<std::stop_callback
<std::function
<void()>>> sc1(std::in_place
, ss
.get_token(), [&] {
65 startedIndex
.notify_all();
66 callbackFinish
.wait(false);
69 std::optional
<std::stop_callback
<std::function
<void()>>> sc2(std::in_place
, ss
.get_token(), [&] {
71 startedIndex
.notify_all();
72 callbackFinish
.wait(false);
75 auto thread
= support::make_test_thread([&] { ss
.request_stop(); });
79 // now one of the callback has started but not finished.
80 if (startedIndex
== 1) {
81 sc2
.reset(); // destructor should not block
82 } else if (startedIndex
== 2) {
83 sc1
.reset(); // destructor should not block
85 assert(false); // something is wrong
88 callbackFinish
= true;
89 callbackFinish
.notify_all();
93 // If callback is concurrently executing on another thread, then the
94 // return from the invocation of callback strongly happens before ([intro.races])
95 // callback is destroyed.
98 std::atomic
<bool>& started_
;
99 std::atomic
<bool>& waitDone_
;
100 std::atomic
<bool>& finished_
;
103 Callback(std::atomic
<bool>& started
, std::atomic
<bool>& waitDone
, std::atomic
<bool>& finished
)
104 : started_(started
), waitDone_(waitDone
), finished_(finished
) {}
105 Callback(Callback
&& other
) : started_(other
.started_
), waitDone_(other
.waitDone_
), finished_(other
.finished_
) {
109 void operator()() const {
111 std::atomic
<bool>& g_finished_
;
112 ~ScopedGuard() { g_finished_
.store(true, std::memory_order_relaxed
); }
116 started_
.notify_all();
117 waitDone_
.wait(false);
118 ScopedGuard g
{finished_
};
123 // destructor has to be called after operator() returns
124 assert(finished_
.load(std::memory_order_relaxed
));
131 std::atomic
<bool> started
= false;
132 std::atomic
<bool> waitDone
= false;
133 std::atomic
<bool> finished
= false;
135 std::optional
<std::stop_callback
<Callback
>> sc
{
136 std::in_place
, ss
.get_token(), Callback
{started
, waitDone
, finished
}};
138 auto thread1
= support::make_test_thread([&] { ss
.request_stop(); });
141 auto thread2
= support::make_test_thread([&] {
142 using namespace std::chrono_literals
;
143 std::this_thread::sleep_for(1ms
);
145 waitDone
.notify_all();
148 sc
.reset(); // destructor should block until operator() returns, i.e. waitDone to be true
154 // If callback is executing on the current thread, then the destructor does not block ([defns.block])
155 // waiting for the return from the invocation of callback.
159 CallbackHolder holder
;
160 holder
.callback_
= std::make_unique
<std::stop_callback
<DeleteHolder
>>(ss
.get_token(), DeleteHolder
{holder
});
162 assert(holder
.callback_
!= nullptr);
164 ss
.request_stop(); // the callbacks deletes itself. if the destructor blocks, it would be deadlock
165 assert(holder
.callback_
== nullptr);