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