[RISCV] Fix mgather -> riscv.masked.strided.load combine not extending indices (...
[llvm-project.git] / libcxx / test / std / thread / thread.stoptoken / stopcallback / dtor.pass.cpp
blob93137111b676cdfcc6e8bd300d05cea3dbec9d59
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
14 // ~stop_callback();
16 #include <atomic>
17 #include <cassert>
18 #include <chrono>
19 #include <functional>
20 #include <optional>
21 #include <stop_token>
22 #include <type_traits>
23 #include <utility>
24 #include <vector>
26 #include "make_test_thread.h"
27 #include "test_macros.h"
29 struct CallbackHolder;
31 struct DeleteHolder {
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
45 std::stop_source ss;
46 bool called = false;
49 std::stop_callback sc(ss.get_token(), [&] { called = true; });
51 ss.request_stop();
52 assert(!called);
55 // The destructor does not block waiting for the execution of another
56 // callback registered by an associated stop_callback.
58 std::stop_source ss;
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(), [&] {
64 startedIndex = 1;
65 startedIndex.notify_all();
66 callbackFinish.wait(false);
67 });
69 std::optional<std::stop_callback<std::function<void()>>> sc2(std::in_place, ss.get_token(), [&] {
70 startedIndex = 2;
71 startedIndex.notify_all();
72 callbackFinish.wait(false);
73 });
75 auto thread = support::make_test_thread([&] { ss.request_stop(); });
77 startedIndex.wait(0);
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
84 } else {
85 assert(false); // something is wrong
88 callbackFinish = true;
89 callbackFinish.notify_all();
90 thread.join();
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.
97 struct Callback {
98 std::atomic<bool>& started_;
99 std::atomic<bool>& waitDone_;
100 std::atomic<bool>& finished_;
101 bool moved = false;
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_) {
106 other.moved = true;
109 void operator()() const {
110 struct ScopedGuard {
111 std::atomic<bool>& g_finished_;
112 ~ScopedGuard() { g_finished_.store(true, std::memory_order_relaxed); }
115 started_ = true;
116 started_.notify_all();
117 waitDone_.wait(false);
118 ScopedGuard g{finished_};
121 ~Callback() {
122 if (!moved) {
123 // destructor has to be called after operator() returns
124 assert(finished_.load(std::memory_order_relaxed));
129 std::stop_source ss;
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(); });
139 started.wait(false);
141 auto thread2 = support::make_test_thread([&] {
142 using namespace std::chrono_literals;
143 std::this_thread::sleep_for(1ms);
144 waitDone = true;
145 waitDone.notify_all();
148 sc.reset(); // destructor should block until operator() returns, i.e. waitDone to be true
150 thread1.join();
151 thread2.join();
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.
157 std::stop_source ss;
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);
168 return 0;