-
Notifications
You must be signed in to change notification settings - Fork 169
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP] Tail sender #678
base: main
Are you sure you want to change the base?
[WIP] Tail sender #678
Conversation
Cool. What impact does this have on algorithm implementations? I assume it's not safe to just drop a tail sender on the floor, or is it? |
Correct. The results of start(), set_value(), set_error(), and set_stopped() must either be propagated or resumed. This affects lifetimes as well. When a tail_sender is returned, the lifetime of the object that returned the tail_sender is extended until all tail_senders are resumed. This is why sync_wait will resume the tail_senders before the operation_state is destructed. |
6cbd664
to
30b788e
Compare
cf59183
to
d05357c
Compare
@kirkshoop there's a |
/ok to test |
This change adds support for TailCalls, in library form, to allow Sender/Receiver algorithms to iterate without blowing the stack or using a scheduler.
Needs examples, documentation and a paper.
The solution here is translated from an original design and implementation by @lewissbaker.
A tail_sender is a constrained sender concept where always_completes_inline_v (a new CPO) is true, all destructors are trivial and all methods are noexcept (copy/move/connect etc..) and the tail_operation_state must support the new unwind() CPO.
A nullable_tail_sender must also have a tail_operation_state that is convertible to bool.
A terminal_tail_sender has a tail_operation_state that returns void from start().
The existing start, set_value, set_error, and set_stopped CPOs now support returning a tail_sender.
A tail_sender would be used extensively in sender/receiver sequences.
A run() that returns a tail_sender is also the way to compose multiple 'drivable' schedulers (like run_loop) on the same thread. This will be important for the system_executor.
The repeat() algorithm would use tail_sender per start() instead of a schedule() per start().
To drive a set of recursive tail_senders without exhausting the stack, use:
TailSender resume_tail_senders_until_one_remaining(TailReceiver, TailSenders...)
An example main() that safely starts and stops async execution:
Other framework loops can be integrated into main as additional 'drivable' contexts that provide run(). Or by inserting:
Running this each iteration of the loop in the framework, will ensure that the schedulers are making forward progress.