- 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): SendersndrをSchedulerschに関連付けられた実行リソースに属する実行エージェント上で開始し、完了後にonSenderが開始された実行リソースへと実行制御を戻す。on(sndr, sch, closure): Sendersndrの完了後に、Schedulerschに関連付けられた実行リソースに属する実行エージェントへ実行を移し、sndrの完了結果をもってSenderアダプタクロージャclosureを実行し、Sendersndrが完了された実行リソースへと実行制御を戻す。
onはパイプ可能Senderアダプタオブジェクトであり、パイプライン記法をサポートする。
説明用の式schとsndrに対して、下記いずれかがtrueとなるとき呼び出し式on(sch, sndr)は不適格となる。
decltype((sch))がschedulerを満たさない、もしくはdecltype((sndr))がsenderを満たさず、かつパイプ可能Senderアダプタクロージャオブジェクトではない、もしくはdecltype((sndr))がsenderを満たし、かつパイプ可能Senderアダプタクロージャオブジェクトである。
そうでなければ、呼び出し式on(sch, sndr)は下記と等価。
make-sender(on, sch, sndr)- make-sender[link make-sender.md]
説明用の式sndr, sch, closureに対して、下記いずれかがtrueとなるとき呼び出し式on(sndr, sch, closure)は不適格となる。
decltype((sch))がschedulerを満たさない、もしくはdecltype((sndr))がsenderを満たさない、もしくはclosureがパイプ可能Senderアダプタクロージャオブジェクトではない。
そうでなければ、呼び出し式on(sndr, sch, closure)は下記と等価。
make-sender(on, product-type{sch, closure}, sndr)- make-sender[link make-sender.md]
- product-type[link product-type.md]
説明用の式out_sndrとenvに対して、型OutSndrをdecltype((out_sndr))とし、型Envをdecltype((env))とする。sender-for<OutSndr, on_t> == falseのとき、式on.transform_sender(set_value, out_sndr, env)は不適格となる。
そうでなければ、式on.transform_sender(set_value, out_sndr, env)は下記と等価。
auto&& [_, data, child] = out_sndr;
if constexpr (scheduler<decltype(data)>) {
auto orig_sch =
call-with-default(get_start_scheduler, not-a-scheduler(), env);
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 = call-with-default(
get_completion_scheduler<set_value_t>, not-a-scheduler(), get_env(child), env);
return continues_on(
std::forward_like<OutSndr>(closure)(continues_on(std::forward_like<OutSndr>(child), SCHED-ENV(orig_sch)),
orig_sch);
}- scheduler[link scheduler.md]
- call-with-default[link call-with-default.md]
- get_start_scheduler[link get_start_scheduler.md]
- not-a-scheduler()[link not-a-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]
- SCHED-ENV[link scheduler.md]
- std::move[link /reference/utility/move.md]
Receiverとの接続(connect)時に、関連付けられた実行ドメインに対してexecution::transform_sender経由でSender変換が行われる。
デフォルト実行ドメインではon.transform_sender(set_value, out_sndr, env)が呼ばれ、前述仕様通りのSenderへと変換される。
説明用の式out_sndrをon(sch, sndr)の戻り値Senderとし、型OutSndrをdecltype((out_sndr))とする。式out_rcvrをsender_in<OutSndr, Env> == trueとなる環境Envに関連付けられたReceiverとする。out_sndrとout_rcvrとの接続(connect)結果Operation Stateへの左辺値参照をopとしたとき、start(op)呼び出しは下記を満たすべき。
get_start_scheduler(get_env(rcvr))で取得した現在のSchedulerを記憶する。- Scheduler
schに関連付けられた実行リソースに属する実行エージェント上でsndrを開始する。 sndrの完了後に、手順1で記憶したSchedulerに関連付けられた実行リソースに実行制御を戻す。sndrの非同期操作の結果をout_rcvrへ転送する。
いずれかのスケジューリングが失敗した場合、未規定の実行エージェント上でout_rcvrのエラー完了が行われるべき。
説明用の式out_sndrをon(sndr, sch, closure)の戻り値Senderとし、型OutSndrをdecltype((out_sndr))とする。式out_rcvrをsender_in<OutSndr, Env> == trueとなる環境Envに関連付けられたReceiverとする。out_sndrとout_rcvrとの接続(connect)結果Operation Stateへの左辺値参照をopとしたとき、start(op)呼び出しは下記を満たすべき。
- 現在のSchedulerとして、
get_completion_scheduler<set_value_t>(get_env(rcvr))を記憶する。 sndrを現在の実行エージェント上で開始する。sndrの完了後に、Schedulerschに関連付けられた実行リソースの実行エージェントに実行制御を移す。sndrの非同期操作の結果を、Senderclosure(S)と接続して開始するかのように転送する。ここでSはsndrの非同期操作の結果と同期的に完了するSenderとする。- 前手順で開始した非同期操作が完了したら、手順1で記憶したSchedulerに関連付けられた実行リソースに実行制御を戻し、非同期操作の結果を
out_rcvrへ転送する。
いずれかのスケジューリングが失敗した場合、未規定の実行エージェント上でout_rcvrのエラー完了が行われるべき。
#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
#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
- Clang: ??
- GCC: ??
- ICC: ??
- Visual C++: ??