brintos

brintos / llvm-project-archived public Read only

0
0
Text · 4.7 KiB · 4ae4ae3 Raw
169 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// ~stop_callback();13 14#include <atomic>15#include <cassert>16#include <chrono>17#include <functional>18#include <memory>19#include <optional>20#include <stop_token>21#include <type_traits>22#include <utility>23#include <vector>24 25#include "make_test_thread.h"26#include "test_macros.h"27 28struct CallbackHolder;29 30struct DeleteHolder {31  CallbackHolder& holder_;32  void operator()() const;33};34 35struct CallbackHolder {36  std::unique_ptr<std::stop_callback<DeleteHolder>> callback_;37};38 39void DeleteHolder::operator()() const { holder_.callback_.reset(); }40 41int main(int, char**) {42  // Unregisters the callback from the owned stop state, if any43  {44    std::stop_source ss;45    bool called = false;46 47    {48      std::stop_callback sc(ss.get_token(), [&] { called = true; });49    }50    ss.request_stop();51    assert(!called);52  }53 54  // The destructor does not block waiting for the execution of another55  // callback registered by an associated stop_callback.56  {57    std::stop_source ss;58 59    std::atomic<int> startedIndex    = 0;60    std::atomic<bool> callbackFinish = false;61 62    std::optional<std::stop_callback<std::function<void()>>> sc1(std::in_place, ss.get_token(), [&] {63      startedIndex = 1;64      startedIndex.notify_all();65      callbackFinish.wait(false);66    });67 68    std::optional<std::stop_callback<std::function<void()>>> sc2(std::in_place, ss.get_token(), [&] {69      startedIndex = 2;70      startedIndex.notify_all();71      callbackFinish.wait(false);72    });73 74    auto thread = support::make_test_thread([&] { ss.request_stop(); });75 76    startedIndex.wait(0);77 78    // now one of the callback has started but not finished.79    if (startedIndex == 1) {80      sc2.reset();   // destructor should not block81    } else if (startedIndex == 2) {82      sc1.reset();   // destructor should not block83    } else {84      assert(false); // something is wrong85    }86 87    callbackFinish = true;88    callbackFinish.notify_all();89    thread.join();90  }91 92  // If callback is concurrently executing on another thread, then the93  // return from the invocation of callback strongly happens before ([intro.races])94  // callback is destroyed.95  {96    struct Callback {97      std::atomic<bool>& started_;98      std::atomic<bool>& waitDone_;99      std::atomic<bool>& finished_;100      bool moved = false;101 102      Callback(std::atomic<bool>& started, std::atomic<bool>& waitDone, std::atomic<bool>& finished)103          : started_(started), waitDone_(waitDone), finished_(finished) {}104      Callback(Callback&& other) : started_(other.started_), waitDone_(other.waitDone_), finished_(other.finished_) {105        other.moved = true;106      }107 108      void operator()() const {109        struct ScopedGuard {110          std::atomic<bool>& g_finished_;111          ~ScopedGuard() { g_finished_.store(true, std::memory_order_relaxed); }112        };113 114        started_ = true;115        started_.notify_all();116        waitDone_.wait(false);117        ScopedGuard g{finished_};118      }119 120      ~Callback() {121        if (!moved) {122          // destructor has to be called after operator() returns123          assert(finished_.load(std::memory_order_relaxed));124        }125      }126    };127 128    std::stop_source ss;129 130    std::atomic<bool> started  = false;131    std::atomic<bool> waitDone = false;132    std::atomic<bool> finished = false;133 134    std::optional<std::stop_callback<Callback>> sc{135        std::in_place, ss.get_token(), Callback{started, waitDone, finished}};136 137    auto thread1 = support::make_test_thread([&] { ss.request_stop(); });138    started.wait(false);139 140    auto thread2 = support::make_test_thread([&] {141      using namespace std::chrono_literals;142      std::this_thread::sleep_for(1ms);143      waitDone = true;144      waitDone.notify_all();145    });146 147    sc.reset(); // destructor should block until operator() returns, i.e. waitDone to be true148 149    thread1.join();150    thread2.join();151  }152 153  // If callback is executing on the current thread, then the destructor does not block ([defns.block])154  // waiting for the return from the invocation of callback.155  {156    std::stop_source ss;157 158    CallbackHolder holder;159    holder.callback_ = std::make_unique<std::stop_callback<DeleteHolder>>(ss.get_token(), DeleteHolder{holder});160 161    assert(holder.callback_ != nullptr);162 163    ss.request_stop(); // the callbacks deletes itself. if the destructor blocks, it would be deadlock164    assert(holder.callback_ == nullptr);165  }166 167  return 0;168}169