236 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(const 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 (*)()>, const std::stop_token&, void (*)()>);33static_assert(!std::is_constructible_v<std::stop_callback<void (*)()>, const std::stop_token&, void (*)(int)>);34static_assert(std::is_constructible_v<std::stop_callback<Cb>, const std::stop_token&, Cb&>);35static_assert(std::is_constructible_v<std::stop_callback<Cb&>, const std::stop_token&, Cb&>);36static_assert(!std::is_constructible_v<std::stop_callback<Cb>, const 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>, const 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>>, const std::stop_token&, int>);54static_assert(!std::is_nothrow_constructible_v<std::stop_callback<CbNoExcept<false>>, const std::stop_token&, int>);55 56int main(int, char**) {57 // was requested58 {59 std::stop_source ss;60 const std::stop_token st = ss.get_token();61 ss.request_stop();62 63 bool called = false;64 std::stop_callback sc(st, [&] { called = true; });65 assert(called);66 }67 68 // was not requested69 {70 std::stop_source ss;71 const std::stop_token st = ss.get_token();72 73 bool called = false;74 std::stop_callback sc(st, [&] { called = true; });75 assert(!called);76 77 ss.request_stop();78 assert(called);79 }80 81 // token has no state82 {83 std::stop_token st;84 bool called = false;85 std::stop_callback sc(st, [&] { called = true; });86 assert(!called);87 }88 89 // should not be called multiple times90 {91 std::stop_source ss;92 const std::stop_token st = ss.get_token();93 94 int calledTimes = 0;95 std::stop_callback sc(st, [&] { ++calledTimes; });96 97 std::vector<std::thread> threads;98 for (auto i = 0; i < 10; ++i) {99 threads.emplace_back(support::make_test_thread([&] { ss.request_stop(); }));100 }101 102 for (auto& thread : threads) {103 thread.join();104 }105 assert(calledTimes == 1);106 }107 108 // adding more callbacks during invoking other callbacks109 {110 std::stop_source ss;111 const std::stop_token st = ss.get_token();112 113 std::atomic<bool> startedFlag = false;114 std::atomic<bool> finishFlag = false;115 std::stop_callback sc(st, [&] {116 startedFlag = true;117 startedFlag.notify_all();118 finishFlag.wait(false);119 });120 121 auto thread = support::make_test_thread([&] { ss.request_stop(); });122 123 startedFlag.wait(false);124 125 // first callback is still running, adding another one;126 bool secondCallbackCalled = false;127 std::stop_callback sc2(st, [&] { secondCallbackCalled = true; });128 129 finishFlag = true;130 finishFlag.notify_all();131 132 thread.join();133 assert(secondCallbackCalled);134 }135 136 // adding callbacks on different threads137 {138 std::stop_source ss;139 const std::stop_token st = ss.get_token();140 141 std::vector<std::thread> threads;142 std::atomic<int> callbackCalledTimes = 0;143 std::atomic<bool> done = false;144 for (auto i = 0; i < 10; ++i) {145 threads.emplace_back(support::make_test_thread([&] {146 std::stop_callback sc{st, [&] { callbackCalledTimes.fetch_add(1, std::memory_order_relaxed); }};147 done.wait(false);148 }));149 }150 using namespace std::chrono_literals;151 std::this_thread::sleep_for(1ms);152 ss.request_stop();153 done = true;154 done.notify_all();155 for (auto& thread : threads) {156 thread.join();157 }158 assert(callbackCalledTimes.load(std::memory_order_relaxed) == 10);159 }160 161 // correct overload162 {163 struct CBWithTracking {164 bool& lvalueCalled;165 bool& lvalueConstCalled;166 bool& rvalueCalled;167 bool& rvalueConstCalled;168 169 void operator()() & { lvalueCalled = true; }170 void operator()() const& { lvalueConstCalled = true; }171 void operator()() && { rvalueCalled = true; }172 void operator()() const&& { rvalueConstCalled = true; }173 };174 175 // RValue176 {177 bool lvalueCalled = false;178 bool lvalueConstCalled = false;179 bool rvalueCalled = false;180 bool rvalueConstCalled = false;181 std::stop_source ss;182 const std::stop_token st = ss.get_token();183 ss.request_stop();184 185 std::stop_callback<CBWithTracking> sc(186 st, CBWithTracking{lvalueCalled, lvalueConstCalled, rvalueCalled, rvalueConstCalled});187 assert(rvalueCalled);188 }189 190 // RValue191 {192 bool lvalueCalled = false;193 bool lvalueConstCalled = false;194 bool rvalueCalled = false;195 bool rvalueConstCalled = false;196 std::stop_source ss;197 const std::stop_token st = ss.get_token();198 ss.request_stop();199 200 std::stop_callback<const CBWithTracking> sc(201 st, CBWithTracking{lvalueCalled, lvalueConstCalled, rvalueCalled, rvalueConstCalled});202 assert(rvalueConstCalled);203 }204 205 // LValue206 {207 bool lvalueCalled = false;208 bool lvalueConstCalled = false;209 bool rvalueCalled = false;210 bool rvalueConstCalled = false;211 std::stop_source ss;212 const std::stop_token st = ss.get_token();213 ss.request_stop();214 CBWithTracking cb{lvalueCalled, lvalueConstCalled, rvalueCalled, rvalueConstCalled};215 std::stop_callback<CBWithTracking&> sc(st, cb);216 assert(lvalueCalled);217 }218 219 // const LValue220 {221 bool lvalueCalled = false;222 bool lvalueConstCalled = false;223 bool rvalueCalled = false;224 bool rvalueConstCalled = false;225 std::stop_source ss;226 const std::stop_token st = ss.get_token();227 ss.request_stop();228 CBWithTracking cb{lvalueCalled, lvalueConstCalled, rvalueCalled, rvalueConstCalled};229 std::stop_callback<const CBWithTracking&> sc(st, cb);230 assert(lvalueConstCalled);231 }232 }233 234 return 0;235}236