//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // UNSUPPORTED: no-threads // UNSUPPORTED: libcpp-has-no-experimental-stop_token // UNSUPPORTED: c++03, c++11, c++14, c++17 // XFAIL: availability-synchronization_library-missing // template // explicit stop_callback(const stop_token& st, C&& cb) // noexcept(is_nothrow_constructible_v); #include #include #include #include #include #include #include #include "make_test_thread.h" #include "test_macros.h" struct Cb { void operator()() const; }; // Constraints: Callback and C satisfy constructible_from. static_assert(std::is_constructible_v, const std::stop_token&, void (*)()>); static_assert(!std::is_constructible_v, const std::stop_token&, void (*)(int)>); static_assert(std::is_constructible_v, const std::stop_token&, Cb&>); static_assert(std::is_constructible_v, const std::stop_token&, Cb&>); static_assert(!std::is_constructible_v, const std::stop_token&, int>); // explicit template void conversion_test(T); template concept ImplicitlyConstructible = requires(Args&&... args) { conversion_test({std::forward(args)...}); }; static_assert(ImplicitlyConstructible); static_assert(!ImplicitlyConstructible, const std::stop_token&, Cb>); // noexcept template struct CbNoExcept { CbNoExcept(int) noexcept(NoExceptCtor); void operator()() const; }; static_assert(std::is_nothrow_constructible_v>, const std::stop_token&, int>); static_assert(!std::is_nothrow_constructible_v>, const std::stop_token&, int>); int main(int, char**) { // was requested { std::stop_source ss; const std::stop_token st = ss.get_token(); ss.request_stop(); bool called = false; std::stop_callback sc(st, [&] { called = true; }); assert(called); } // was not requested { std::stop_source ss; const std::stop_token st = ss.get_token(); bool called = false; std::stop_callback sc(st, [&] { called = true; }); assert(!called); ss.request_stop(); assert(called); } // token has no state { std::stop_token st; bool called = false; std::stop_callback sc(st, [&] { called = true; }); assert(!called); } // should not be called multiple times { std::stop_source ss; const std::stop_token st = ss.get_token(); int calledTimes = 0; std::stop_callback sc(st, [&] { ++calledTimes; }); std::vector threads; for (auto i = 0; i < 10; ++i) { threads.emplace_back(support::make_test_thread([&] { ss.request_stop(); })); } for (auto& thread : threads) { thread.join(); } assert(calledTimes == 1); } // adding more callbacks during invoking other callbacks { std::stop_source ss; const std::stop_token st = ss.get_token(); std::atomic startedFlag = false; std::atomic finishFlag = false; std::stop_callback sc(st, [&] { startedFlag = true; startedFlag.notify_all(); finishFlag.wait(false); }); auto thread = support::make_test_thread([&] { ss.request_stop(); }); startedFlag.wait(false); // first callback is still running, adding another one; bool secondCallbackCalled = false; std::stop_callback sc2(st, [&] { secondCallbackCalled = true; }); finishFlag = true; finishFlag.notify_all(); thread.join(); assert(secondCallbackCalled); } // adding callbacks on different threads { std::stop_source ss; const std::stop_token st = ss.get_token(); std::vector threads; std::atomic callbackCalledTimes = 0; std::atomic done = false; for (auto i = 0; i < 10; ++i) { threads.emplace_back(support::make_test_thread([&] { std::stop_callback sc{st, [&] { callbackCalledTimes.fetch_add(1, std::memory_order_relaxed); }}; done.wait(false); })); } using namespace std::chrono_literals; std::this_thread::sleep_for(1ms); ss.request_stop(); done = true; done.notify_all(); for (auto& thread : threads) { thread.join(); } assert(callbackCalledTimes.load(std::memory_order_relaxed) == 10); } // correct overload { struct CBWithTracking { bool& lvalueCalled; bool& lvalueConstCalled; bool& rvalueCalled; bool& rvalueConstCalled; void operator()() & { lvalueCalled = true; } void operator()() const& { lvalueConstCalled = true; } void operator()() && { rvalueCalled = true; } void operator()() const&& { rvalueConstCalled = true; } }; // RValue { bool lvalueCalled = false; bool lvalueConstCalled = false; bool rvalueCalled = false; bool rvalueConstCalled = false; std::stop_source ss; const std::stop_token st = ss.get_token(); ss.request_stop(); std::stop_callback sc( st, CBWithTracking{lvalueCalled, lvalueConstCalled, rvalueCalled, rvalueConstCalled}); assert(rvalueCalled); } // RValue { bool lvalueCalled = false; bool lvalueConstCalled = false; bool rvalueCalled = false; bool rvalueConstCalled = false; std::stop_source ss; const std::stop_token st = ss.get_token(); ss.request_stop(); std::stop_callback sc( st, CBWithTracking{lvalueCalled, lvalueConstCalled, rvalueCalled, rvalueConstCalled}); assert(rvalueConstCalled); } // LValue { bool lvalueCalled = false; bool lvalueConstCalled = false; bool rvalueCalled = false; bool rvalueConstCalled = false; std::stop_source ss; const std::stop_token st = ss.get_token(); ss.request_stop(); CBWithTracking cb{lvalueCalled, lvalueConstCalled, rvalueCalled, rvalueConstCalled}; std::stop_callback sc(st, cb); assert(lvalueCalled); } // const LValue { bool lvalueCalled = false; bool lvalueConstCalled = false; bool rvalueCalled = false; bool rvalueConstCalled = false; std::stop_source ss; const std::stop_token st = ss.get_token(); ss.request_stop(); CBWithTracking cb{lvalueCalled, lvalueConstCalled, rvalueCalled, rvalueConstCalled}; std::stop_callback sc(st, cb); assert(lvalueConstCalled); } } return 0; }