Skip to content

Latest commit

 

History

History
318 lines (260 loc) · 13.1 KB

File metadata and controls

318 lines (260 loc) · 13.1 KB

on

  • execution[meta header]
  • cpo[meta id-type]
  • std::execution[meta namespace]
  • cpp26[meta cpp]
namespace std::execution {
  struct on_t { unspecified };
  inline constexpr on_t on{};
}
  • unspecified[italic]

概要

onは、2種類の呼び出し形式サポートするSenderアダプタである。

  • on(sch, sndr) : SendersndrSchedulerschに関連付けられた実行リソースに属する実行エージェント上で開始し、完了後にonSenderが開始された実行リソースへと実行制御を戻す。
  • on(sndr, sch, closure) : Sendersndrの完了後に、Schedulerschに関連付けられた実行リソースに属する実行エージェントへ実行を移し、sndrの完了結果をもってSenderアダプタクロージャclosureを実行し、Sendersndrが完了された実行リソースへと実行制御を戻す。

onパイプ可能Senderアダプタオブジェクトであり、パイプライン記法をサポートする。

効果

呼び出し式 on(sch, sndr)

説明用の式schsndrに対して、下記いずれかがtrueとなるとき呼び出し式on(sch, sndr)は不適格となる。

そうでなければ、呼び出し式on(sch, sndr)schが1回だけ評価されることを除いて、下記と等価。

transform_sender(
  query-with-default(get_domain, sch, default_domain()),
  make-sender(on, sch, sndr))
  • transform_sender[link transform_sender.md]
  • query-with-default[link query-with-default.md]
  • get_domain[link get_domain.md]
  • default_domain()[link default_domain.md]
  • make-sender[link make-sender.md]

呼び出し式 on(sndr, sch, closure)

説明用の式sndr, sch, closureに対して、下記いずれかがtrueとなるとき呼び出し式on(sndr, sch, closure)は不適格となる。

そうでなければ、呼び出し式on(sndr, sch, closure)sndrが1回だけ評価されることを除いて、下記と等価。

transform_sender(
  get-domain-early(sndr),
  make-sender(on, product-type{sch, closure}, sndr))
  • transform_sender[link transform_sender.md]
  • get-domain-early[link get-domain-early.md]
  • make-sender[link make-sender.md]
  • product-type[link product-type.md]

Senderアルゴリズムタグ on

説明用の式out_sndrenvに対して、型OutSndrdecltype((out_sndr))とし、型Envdecltype((env))とする。sender-for<OutSndr, on_t> == falseのとき、式on.transform_env(out_sndr, env)および式on.transform_sender(out_sndr, env)はいずれも不適格となる。

そうでなければ、下記の通り。

  • on.transform_env(out_sndr, env)は下記と等価。

    auto&& [_, data, _] = out_sndr;
    if constexpr (scheduler<decltype(data)>) {
      return JOIN-ENV(SCHED-ENV(std::forward_like<OutSndr>(data)), FWD-ENV(std::forward<Env>(env)));
    } else {
      return std::forward<Env>(env);
    }
    • scheduler[link scheduler.md]
    • JOIN-ENV[link ../queryable.md]
    • SCHED-ENV[link scheduler.md]
    • FWD-ENV[link ../forwarding_query.md]
  • on.transform_sender(out_sndr, env)は下記と等価。

    auto&& [_, data, child] = out_sndr;
    if constexpr (scheduler<decltype(data)>) {
      auto orig_sch =
        query-with-default(get_scheduler, env, not-a-scheduler());
    
      if constexpr (same_as<decltype(orig_sch), not-a-scheduler>) {
        return not-a-sender{};
      } else {
        return continues_on(
          starts_on(std::forward_like<OutSndr>(data), std::forward_like<OutSndr>(child)),
          std::move(orig_sch));
      }
    } else {
      auto& [sch, closure] = data;
      auto orig_sch = query-with-default(
        get_completion_scheduler<set_value_t>,
        get_env(child),
        query-with-default(get_scheduler, env, not-a-scheduler()));
    
      if constexpr (same_as<decltype(orig_sch), not-a-scheduler>) {
        return not-a-sender{};
      } else {
        return write_env(
          continues_on(
            std::forward_like<OutSndr>(closure)(
              continues_on(
                write_env(std::forward_like<OutSndr>(child), SCHED-ENV(orig_sch)),
                sch)),
            orig_sch),
          SCHED-ENV(sch));
      }
    }
    • scheduler[link scheduler.md]
    • get_scheduler[link get_scheduler.md]
    • continues_on[link continues_on.md]
    • starts_on[link starts_on.md]
    • get_completion_scheduler[link get_completion_scheduler.md]
    • set_value_t[link set_value.md]
    • get_env[link get_env.md]
    • query-with-default[link query-with-default.md]
    • not-a-sender[link not-a-sender.md]
    • write_env[link write_env.md]
    • SCHED-ENV[link scheduler.md]
    • std::move[link /reference/utility/move.md]

説明専用のクラスnot-a-schedulerを未規定な空のクラス型とする。

カスタマイゼーションポイント

Senderアルゴリズム構築時に、関連付けられた実行ドメインに対してexecution::transform_sender経由でSender変換が行われる。 デフォルト実行ドメインでは無変換。

Receiverとの接続(connect)時に、関連付けられた実行ドメインに対してexecution::transform_sender経由でSender変換が行われる。 デフォルト実行ドメインではon.transform_sender(out_sndr, env)が呼ばれ、前述仕様通りのSenderへと変換される。

呼び出し式 on(sch, sndr)

説明用の式out_sndron(sch, sndr)の戻り値Senderとし、型OutSndrdecltype((out_sndr))とする。式out_rcvrsender_in<OutSndr, Env> == trueとなる環境Envに関連付けられたReceiverとする。out_sndrout_rcvrとの接続(connect)結果Operation Stateへの左辺値参照をopとしたとき、start(op)呼び出しは下記を満たすべき。

  • 現在のSchedulerget_scheduler(get_env(rcvr))を記憶する。
  • Schedulerschに関連付けられた実行リソースに属する実行エージェント上でsndrを開始する。
  • sndrの完了後に、手順1で記憶したSchedulerに関連付けられた実行リソースに実行制御を戻す。
  • sndrの非同期操作の結果をout_rcvrへ転送する。

いずれかのスケジューリングが失敗した場合、未規定の実行エージェント上でout_rcvrエラー完了が行われるべき。

呼び出し式 on(sndr, sch, closure)

説明用の式out_sndron(sndr, sch, closure)の戻り値Senderとし、型OutSndrdecltype((out_sndr))とする。式out_rcvrsender_in<OutSndr, Env> == trueとなる環境Envに関連付けられたReceiverとする。out_sndrout_rcvrとの接続(connect)結果Operation Stateへの左辺値参照をopとしたとき、start(op)呼び出しは下記を満たすべき。

  • 下記のうち最初に適格となるものを、現在のSchedulerとして記憶する。
  • sndrを現在の実行エージェント上で開始する。
  • sndrの完了後に、Schedulerschに関連付けられた実行リソースの実行エージェントに実行制御を移す。
  • sndrの非同期操作の結果を、Senderclosure(S)と接続して開始するかのように転送する。ここでSsndrの非同期操作の結果と同期的に完了するSenderとする。
  • 前手順で開始した非同期操作が完了したら、手順1で記憶したSchedulerに関連付けられた実行リソースに実行制御を戻し、非同期操作の結果をout_rcvrへ転送する。

いずれかのスケジューリングが失敗した場合、未規定の実行エージェント上でout_rcvrエラー完了が行われるべき。

例1: on(sch, sndr)

#include <thread>
#include <print>
#include <execution>
namespace ex = std::execution;

int main()
{
  std::println("main#{}", std::this_thread::get_id());

  ex::run_loop loop;
  std::jthread worker{[&]{
    std::println("start worker#{}", std::this_thread::get_id());
    loop.run();
  }};
  ex::scheduler auto sch = loop.get_scheduler();

  ex::sender auto snd0 =
    ex::then(ex::just(), []() {
      std::println("on worker#{}", std::this_thread::get_id());
      return 6;
    });
  ex::sender auto snd1 = ex::on(sch, snd0);
  ex::sender auto snd2 =
    ex::then(snd1, [](int n) {
      std::println("on main#{}", std::this_thread::get_id());
      return n * 7;
    });

  auto [val] = std::this_thread::sync_wait(std::move(snd2)).value();
  std::println("val={}", val);

  loop.finish();
}
  • ex::on[color ff0000]
  • ex::run_loop[link run_loop.md]
  • ex::sender[link sender.md]
  • ex::scheduler[link scheduler.md]
  • ex::just[link just.md]
  • ex::then[link then.md]
  • get_scheduler()[link run_loop/get_scheduler.md]
  • run()[link run_loop/run.md]
  • finish()[link run_loop/finish.md]
  • std::this_thread::sync_wait[link ../this_thread/sync_wait.md]
  • value()[link /reference/optional/optional/value.md]
  • std::move[link /reference/utility/move.md]
  • std::this_thread::get_id()[link /reference/thread/this_thread/get_id.md]

出力例

main#133519926773568
start worker#133519921645120
on worker#133519921645120
on main#133519926773568
val=42

例2: on(sndr, sch, closure)

#include <thread>
#include <print>
#include <execution>
namespace ex = std::execution;

int main()
{
  std::println("main#{}", std::this_thread::get_id());

  ex::run_loop loop;
  std::jthread worker{[&]{
    std::println("start worker#{}", std::this_thread::get_id());
    loop.run();
  }};
  ex::scheduler auto sch = loop.get_scheduler();

  ex::sender auto sndr =
    ex::just()
    | ex::then([]() {
        std::println("on main#{}", std::this_thread::get_id());
        return 2;
      })
    | ex::on(sch, ex::then([](int n) {
        std::println("on worker#{}", std::this_thread::get_id());
        return n * 3;
      }))
    | ex::then([](int n) {
        std::println("on main#{}", std::this_thread::get_id());
        return n * 7;
      });

  auto [val] = std::this_thread::sync_wait(std::move(sndr)).value();
  std::println("val={}", val);

  loop.finish();
}
  • ex::on[color ff0000]
  • ex::run_loop[link run_loop.md]
  • ex::sender[link sender.md]
  • ex::scheduler[link scheduler.md]
  • ex::just[link just.md]
  • ex::then[link then.md]
  • get_scheduler()[link run_loop/get_scheduler.md]
  • run()[link run_loop/run.md]
  • finish()[link run_loop/finish.md]
  • std::this_thread::sync_wait[link ../this_thread/sync_wait.md]
  • value()[link /reference/optional/optional/value.md]
  • std::move[link /reference/utility/move.md]
  • std::this_thread::get_id()[link /reference/thread/this_thread/get_id.md]

出力例

main#133519926773568
start worker#133519921645120
on main#133519926773568
on worker#133519921645120
on main#133519926773568
val=42

バージョン

言語

  • C++26

処理系

関連項目

参照