C++2c(C++26)標準ライブラリに追加される実行制御ライブラリ(execution control library)についてメモ。別名:std::execution, Senders/Receivers(S/R)
2025年5月現在、ベースライン提案文書P2300R10までを採択済み。
2025-07-20追記:2025年6月会合にて関連する一連の提案文書P2079R10,
P3149R11, P3284R4, (PDF)P3433R1, P3481R5, P3552R3, P3557R3, P3570R2, (PDF)P3682R0が採択された。
- 汎用的な非同期処理フレームワークを構成するコンセプト(concept)や、基本的な操作CPO(Customization Point Object)(→id:yohhoy:20190403)を定義する。*1
- 非同期処理ビルディングブロックとしてのSenderアルゴリズムを定義する。→ id:yohhoy:20250612
- C++コルーチンとSenderの相互運用をサポートする。→ id:yohhoy:20250613
// C++2c #include <execution> #include <print> namespace exec = std::execution; // タスクチェインを構成 exec::sender auto work = exec::just(2, 3) | exec::then([](int a, int b) { return a * b * 7; }); // 呼び出しスレッド上で同期的にタスク実行 auto result = std::this_thread::sync_wait(work); // 結果値を取り出す auto [value] = result.value(); // value == int型の値42
CPU並列処理を実現するスレッドプール機構や、コルーチン非同期タスク型といった応用機能は検討進行中。GPU/CUDA並列処理のようなベンダ拡張機能では、非同期処理フレームワークに則ったサードパーティ実装が提供される。*2
- 2025-07-20追記:システムスレッドプールのSchedulerを返す
execution::get_parallel_scheduler関数[P2079R10]と連携してCPU並列処理を行うSenderアダプタexecution::bulkファミリ[P3481R5]、Sender互換コルーチン戻り値型として利用する非同期タスクexecution::task<T>クラス[P3552R3]が採択済み。
実行制御ライブラリ
C++2c実行制御ライブラリでは、非同期(asynchronous)・並行(concurrent)/並列(parallel)処理を統合的に扱うフレームワークを規定する。ライブラリ仕様は非同期処理を実現する枠組みとして記述され、並行/並列処理は具体的なScheduler実装によって実現される。(例:CPUスレッドプール、GPGPU/CUDA処理をサポートするScheduler)
<execution>ヘッダ(C++17追加ヘッダを拡張)- 基本コンセプト
execution::senderexecution::receiverexecution::operation_stateexecution::scheduler
- 基本操作(CPOとして定義)
execution::connect:(sender, receiver) -> operation_stateexecution::start:(operation_state) -> voidexecution::schedule:(scheduler) -> senderexecution::set_value:(receiver, values...) -> voidexecution::set_error:(receiver, err) -> voidexecution::set_stopped:(receiver) -> void
- プロパティ問い合わせ(queries)
- 標準クエリオブジェクト(CPOとして定義)
- クエリ可能オブジェクト == Sender属性, Receiver環境, Scheduler
- クエリ可能オブジェクトの実装ヘルパ型
execution::env,execution::prop
- Senderアルゴリズム(CPOとして定義)→ id:yohhoy:20250612
- C++コルーチンサポート → id:yohhoy:20250613
- 任意のコルーチンAwaitable型をSenderとして扱える(
execution::connectCPO内部動作) - Senderをコルーチン内で利用可能にする実装ヘルパ型
execution::with_awaitable_senders
- 任意のコルーチンAwaitable型をSenderとして扱える(
基本コンセプト: Sender, Receiver, Operation State
- 非同期操作(asynchronous operation)
- 明示的に作成され、1回だけ明示的に開始(start)でき、最終的に3種類いずれかの完了(completion)に到達する。
- 値完了(value completion):非同期操作の “成功”。0個以上の任意型の値。
- エラー完了(error completion):非同期操作の “失敗”。1個の任意型のエラー値。
- 停止完了(stopped completion):非同期操作の “キャンセル”。付帯情報なし。
- Operation State:
execution::operation_stateconcept - Sender:
execution::senderconcept - Receiver:
execution::senderconcept- 非同期操作の値完了/エラー完了/停止完了を受け取るハンドラの集合体。
- メンバ型
receiver_conceptでタグ型receiver_tを宣言。 - 完了操作
execution::set_{value,error,stopped}CPOから呼ばれる完了ハンドラset_{value,error,stopped}メンバ関数を定義。
- 完了シグネチャ(completion signature) == 完了ハンドラの型情報*4
- 値完了:
execution::set_value_t(Values...) - エラー完了:
execution::set_error_t(Err) - 停止完了:
execution::set_stopped_t() - 完了シグネチャ集合を
execution::completion_signatures<Sigs...>で表現。 - Sender/Receiverは少なくとも1個の完了シグネチャに対応する。Sender完了シグネチャ集合 ⊆ Receiver完了シグネチャ集合ならば、両者は接続(connect)可能。*5
- 2025-08-23追記:P3557R3採択後のSender完了シグネチャ集合の宣言方法は id:yohhoy:20250823 を参照。
- 値完了:
exec::sender auto sndr = /* Senderオブジェクト */ exec::receiver auto rcvr = /* Receiverオブジェクト */ // SenderとReceiverを接続(connect) exec::operation_state auto op = exec::connect(sndr, rcvr); // 非同期操作の開始(start) exec::start(op); // 非同期操作完了時にexec::set_{value,error,stopped}を呼び出し、 // rcvrで定義する完了ハンドラのいずれか1つが呼び出される。 // (Receiverが受け取った結果値の取り出しは別機構で実現する。)
基本コンセプト: Scheduler
- 実行エージェント(execution agent):タスクを並列実行する機構(例:CPUスレッド
std::thread、GPU/CUDAスレッド)。 - 実行リソース(execution resource):実行エージェントの集合を管理するエンティティ(例:CPUスレッドプール、CUDAスレッド管理)
- Scheduler:
execution::schedulerconcept - 完了Scheduler(completion scheduler)
- 非同期操作の値完了/エラー完了/停止完了を実行するScheduler。完了種別毎に異なる可能性あり。
- スケジュールSender(schedule sender)
execution::scheduleCPO呼び出しで得られるSender。Scheduler上で空の値を用いて値完了を実行する。
execution::run_loop型- シングルCPUスレッドでのタスクFIFO処理を行う実行リソース。
exec::scheduler auto sch = /* Schedulerオブジェクト */ // スケジュールSenderを作成 exec::sender auto sndr = exec::schedule(sch); exec::receiver auto rcvr = /* Receiverオブジェクト */ exec::operation_state auto op = exec::connect(sndr, rcvr); exec::start(op); // sch上でrcvrに対して値完了rcvr.set_value()が呼ばれる
クエリ可能オブジェクト/クエリオブジェクト
- クエリ可能オブジェクト(queryable object)
- クエリオブジェクト(query object)
- 問い合わせ(query)操作を行うCPO 兼 クエリ可能オブジェクトの Key。
- Sender/Receiver/Schedulerに関連付けられたプロパティ(property)値を取得。
- 標準クエリオブジェクト
forwarding_query:Senderアルゴリズム間のクエリ転送可否を問い合わせ。get_allocator:メモリアロケータを問い合わせ。get_stop_token:非同期キャンセル(→id:yohhoy:20250625)のための停止トークンを問い合わせ。execution::get_domain:Sender動作カスタマイズのための実行ドメインを問い合わせ。execution::get_(delegation_)scheduler:Receiverへ関連付けられたSchedulerを問い合わせ。execution::get_forward_progress_guarantee:Schedulerへ前方進行保証を問い合わせ。execution::get_completion_scheduler<completion-tag>:Senderへ完了Schedulerを問い合わせ。execution::get_await_completion_adaptor:Senderへco_awaitオペランド指定時の型変換を問い合わせ。[P3570R2]
execution::env:複数のクエリ可能オブジェクトを合成。[P3325R5]execution::prop:単一Key−Valueに対応した最小のクエリ可能オブジェクト。[P3325R5]
exec::receiver auto rcvr = /* Receiverオブジェクト */ // Receiver環境を取得 auto env = exec::get_env(rcvr); // メモリアロケータを問い合わせ auto alloc = std::get_allocator(env);
関連URL
- P2300R10 std::execution
- P3325R5 A Utility for Creating Execution Environments
- (PDF) P2470R0 Async Execution in Standard C++
- (PDF) P3090R0 std::execution Introduction
- https://github.com/NVIDIA/stdexec
- https://github.com/intel/cpp-baremetal-senders-and-receivers
- https://github.com/bemanproject/execution
- Using Sender Receiver to Implement Control Flow for Async Processing, CppNow2023
- What are Senders Good For, Anyway? – Eric Niebler
- Senders/receivers in C++ | CompuTruthing
- cppreference: Execution control library
- cpprefjp: std::execution
- https://zenn.dev/yohhoy/scraps/5c500ed9096792
*1:サードパーティ・ライブラリの拡張自由度を高めるためにCPOが多用されており、多数のカスタマイゼーションポイントを提供する。代償としてライブラリ仕様記述が複雑化し、難解な仕様となっているのは否めない。
*2:https://github.com/NVIDIA/stdexec
*3:P2300R10, §5.2: "An operation state is neither movable nor copyable"
*4:Receiverメンバ関数として定義する完了ハンドラの関数シグネチャとは異なり、戻り値型を “完了操作CPOの型” に置換した関数型として表現される。例:値完了シグネチャ execution::set_value_t(int) に対応するRcvrクラスメンバ関数は void Rcvr::set_value(int) となる。
*5:ある種類の完了シグネチャには対応しないケースや、同種の完了シグネチャで異なる引数リストに複数対応するケースもある。停止完了シグネチャは引数0個のため、実質 execution::set_stopped_t() の1種類のみ。
*6:P2300R10, §4.2: "A scheduler is a lightweight handle that represents a strategy for scheduling work onto an execution resource."