最終更新日時(UTC):
が更新

履歴 編集

function
<stop_token>

std::stop_callback::デストラクタ(C++20)

~stop_callback();

効果

もし所有している停止状態があれば、それに対して登録しているコールバックを解除する。

もし自身と同じ停止状態を共有しているほかのstop_callbackが存在してそのコールバックが実行中だったとしても、自身のデストラクタの実行はブロックされない。

もしデストラクタの呼び出し中に自身のコールバックが別のスレッドで実行中だった場合は、そのコールバックの呼び出しから処理が戻るまでは、stop_callbackのコンストラクタで受け取った関数オブジェクトが破棄されないようにデストラクタの実行がブロックされる。
もしデストラクタの呼び出し中に自身のコールバックが同じスレッドで実行中だった場合は、デストラクタの実行はブロックされない。

ほかに停止状態を共有しているオブジェクトがいない場合は、停止状態を扱うために確保したリソースを解放する。

#include <cassert>
#include <chrono>
#include <functional>
#include <memory>
#include <stop_token>
#include <thread>

// CallbackFunc の状態を表す構造体
struct CallbackFuncStatus
{
  std::atomic<bool> started { false };
  std::atomic<bool> finished { false };
  std::atomic<bool> destructed { false };
};

// 停止要求に応じて呼び出される関数オブジェクト
struct CallbackFunc {
  CallbackFunc(CallbackFuncStatus* cfs) : cfs_(cfs) {}
  CallbackFunc(CallbackFunc&& rhs) {
    cfs_ = rhs.cfs_;
    rhs.cfs_ = nullptr;
  }

  CallbackFunc & operator=(CallbackFunc&& rhs) {
    cfs_ = rhs.cfs_;
    rhs.cfs_ = nullptr;
    return *this;
  }

  ~CallbackFunc() {
    if(cfs_) { cfs_->destructed = true; }
  }

  void operator()() {
    cfs_->started = true;
    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    cfs_->finished = true;
  }

private:
  CallbackFuncStatus* cfs_ { nullptr };
};

int main()
{
  // デストラクタ呼び出しのときに別のスレッドで自身のコールバックが実行中な場合のサンプル
  {
    std::stop_source ss;
    std::thread t;
    CallbackFuncStatus cfs;
    {
      std::stop_callback cb { ss.get_token(), CallbackFunc(&cfs) };

      // 別スレッドで cb のコールバックを呼び出す
      t = std::thread([&] { ss.request_stop(); });
      std::this_thread::sleep_for(std::chrono::milliseconds(100));

      assert(cfs.started == true);
      assert(cfs.destructed == false);

    } // ここで cb のデストラクタが呼び出されるが、
      // 別のスレッドで自身のコールバックを実行中なので、
      // それが完了するまでデストラクタの実行がブロックされ、
      // stop_callback のコンストラクタに渡した関数オブジェクトは破棄されない。

    // デストラクタの呼び出しが完了したならば、
    // それに先立って関数オブジェクトも破棄されているはず。
    assert(cfs.finished == true);
    assert(cfs.destructed == true);

    t.join();
  }

  // デストラクタ呼び出しのときに同じスレッドで自身のコールバックが実行中な場合のサンプル
  {
    std::stop_source ss;
    std::function<void()> reset_cb;

    auto on_callback = [&] { reset_cb(); }; // 停止要求に応じて呼び出される関数オブジェクト。
    auto cb = std::make_unique<std::stop_callback<decltype(on_callback)>>(ss.get_token(), on_callback);

    reset_cb = [&] { cb.reset(); }; // on_callback の中から呼び出す処理。cb を破棄する。

    // この中で reset_cb() が呼び出され、 cb のデストラクタが呼び出される。
    // デストラクタとコールバックが同じスレッド上で呼び出されている場合は、
    // デストラクタの実行はブロックされない。
    ss.request_stop();

    assert(cb == nullptr);
  }
}

出力

バージョン

言語

  • C++20

処理系