brintos

brintos / llvm-project-archived public Read only

0
0
Text · 6.5 KiB · f587b64 Raw
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