brintos

brintos / llvm-project-archived public Read only

0
0
Text · 6.8 KiB · 25e88e1 Raw
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