You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
`affine_on(sndr, sch)`adapts`sndr`to complete on the scheduler `sch`, skipping the transition if the sender already completes there.
434
+
`affine(sndr)`ensures`sndr`completes on the receiver's start scheduler, skipping the transition if the sender already completes there. The scheduler is obtained from `get_start_scheduler(get_env(rcvr))` at `connect` time.
432
435
433
436
```cpp
434
437
auto sndr = some_operation()
435
-
| affine_on(pool.get_scheduler());
438
+
| affine;
436
439
```
437
440
438
-
The optimization the reader would hope for - if the sender already completes on the target scheduler, the transition is skipped entirely. No wasted work.
441
+
The optimization the reader would hope for - if the sender already completes on the start scheduler, the transition is skipped entirely. No wasted work.
439
442
440
-
`affine_on` was introduced by [P3552R3](https://wg21.link/p3552r3)<sup>[2]</sup> as the scheduler affinity primitive. It behaves like `continues_on` but avoids the scheduling overhead when the predecessor already completes on the target scheduler. This is what makes scheduler affinity practical for coroutine `task` - the `await_transform` injects `affine_on` around every `co_await`ed sender. We will return to this in Section 11.
443
+
`affine` is the scheduler affinity primitive, redesigned by [P3941R4](https://wg21.link/p3941r4) and renamed from `affine_on` by [P4151R1](https://wg21.link/p4151r1). It behaves like `continues_on` but avoids the scheduling overhead when the predecessor already completes on the start scheduler. The scheduler is not passed as an argument - it comes from the receiver's environment. This is what makes scheduler affinity practical for coroutine `task` - the `await_transform` injects `affine` around every `co_await`ed sender. We will return to this in Section 11.
441
444
442
445
The equivalent program:
443
446
@@ -890,7 +893,7 @@ When the sender completes with `set_value(args...)`, the `co_await` expression p
890
893
891
894
### 11.3 `task_scheduler`
892
895
893
-
`task_scheduler` is a type-erased scheduler used by `task` for scheduler affinity. When a `task` is connected to a receiver, the scheduler is obtained from the receiver's environment via `get_scheduler(get_env(rcvr))` and stored in the `task_scheduler`.
896
+
`task_scheduler` is a type-erased scheduler used by `task` for scheduler affinity. When a `task` is connected to a receiver, the scheduler is obtained from the receiver's environment via `get_start_scheduler(get_env(rcvr))` and stored in the `task_scheduler`.
894
897
895
898
```cpp
896
899
task<void> work() {
@@ -912,7 +915,7 @@ The `task_scheduler` uses small-object optimization to avoid allocation for comm
912
915
913
916
```cpp
914
917
struct no_affinity {
915
-
using scheduler_type = inline_scheduler;
918
+
using start_scheduler_type = inline_scheduler;
916
919
};
917
920
918
921
task<void, no_affinity> fast_path() {
@@ -926,7 +929,7 @@ Disabling affinity removes the rescheduling overhead at the cost of the guarante
926
929
927
930
### 11.5 Scheduler Affinity
928
931
929
-
The `task`'s promise type injects `affine_on` around every `co_await`ed sender via `await_transform`. The effect: after each `co_await`, execution resumes on the task's scheduler regardless of where the sender completed.
932
+
The `task`'s promise type injects `affine` around every `co_await`ed sender via `await_transform`. The effect: after each `co_await`, execution resumes on the task's scheduler regardless of where the sender completed.
930
933
931
934
```cpp
932
935
task<void> affine_demo() {
@@ -939,11 +942,11 @@ task<void> affine_demo() {
939
942
}
940
943
```
941
944
942
-
Scheduler affinity means the programmer can reason about execution context the same way they reason about synchronous code - each line runs on the same context as the line before it. The `affine_on` insertion is invisible. The rescheduling happens only when necessary - if the sender already completes on the correct scheduler, `affine_on` skips the transition.
945
+
Scheduler affinity means the programmer can reason about execution context the same way they reason about synchronous code - each line runs on the same context as the line before it. The `affine` insertion is invisible. The rescheduling happens only when necessary - if the sender already completes on the correct scheduler, `affine` skips the transition.
943
946
944
947
### 11.6 Allocator Support
945
948
946
-
The context parameter `C`configures allocator support. The allocator type is declared via `C::allocator_type`and is used for the coroutine frame allocation.
949
+
The context parameter `C`declares the allocator type via `C::allocator_type`. Two allocator paths exist post-[P3980R1](https://wg21.link/p3980r1): the frame allocator is specified at the call site via `allocator_arg` (which must be the first parameter), and the environment allocator is obtained from `get_allocator(get_env(rcvr))` at `connect` time.
947
950
948
951
```cpp
949
952
structalloc_ctx {
@@ -965,7 +968,7 @@ auto sndr = allocated_work(
965
968
21);
966
969
```
967
970
968
-
The allocator is available to child operations through the receiver's environment via `get_allocator`. The design supports environments where heap allocation is prohibited - the same context parameter that controls the scheduler type controls the allocator type.
971
+
The frame allocator controls coroutine frame allocation at the call site. The environment allocator - obtained from the receiver at `connect` time - is available to child operations via `get_allocator` and propagates through the receiver chain. The design supports environments where heap allocation is prohibited.
The following examples are drawn from the [stdexec](https://github.com/NVIDIA/stdexec)<sup>[8]</sup> reference implementation (whose algorithm customization model is addressed by [P3826R3](https://wg21.link/p3826r3) ("Fix Sender Algorithm Customization")<sup>[19]</sup>) and the [sender-examples](https://github.com/steve-downey/sender-examples)<sup>[21]</sup> repository. They demonstrate patterns that combine the algorithms from this tutorial into working code at a scale the reader may encounter in practice.
1265
+
The following examples are drawn from the [stdexec](https://github.com/NVIDIA/stdexec)<sup>[8]</sup> reference implementation (whose algorithm customization model is addressed by [P3826R5](https://wg21.link/p3826r5) ("Fix Sender Algorithm Customization")<sup>[19]</sup>) and the [sender-examples](https://github.com/steve-downey/sender-examples)<sup>[21]</sup> repository. They demonstrate patterns that combine the algorithms from this tutorial into working code at a scale the reader may encounter in practice.
1263
1266
1264
1267
### 13.1 The Backtracker
1265
1268
@@ -1528,7 +1531,7 @@ The authors thank the P2300 authors - Michał Dominiak, Georgy Evtushenko,
19.[P3826R3](https://wg21.link/p3826r3). Eric Niebler. "Fix Sender Algorithm Customization." 2026. https://wg21.link/p3826r3
1534
+
19.[P3826R5](https://wg21.link/p3826r5). Eric Niebler. "Fix Sender Algorithm Customization." 2026. https://wg21.link/p3826r5
1532
1535
1533
1536
20.[P2079R10](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p2079r10.html). Lucian Radu Teodorescu, Ruslan Arutyunyan, Lee Howes, Michael Voss. "Parallel Scheduler." 2025. https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p2079r10.html
0 commit comments