Add alternative design with just 2 operations on async_scope#16
Conversation
Put the new alternative is a separate part of the document, making it easier to read and compare with the base proposal.
|
This is just the first draft; I didn't go over it to check for errors. Please excuse me for this. |
|
|
||
| If `scope` is an object of type `async_scope`, then `scope.spawn(snd)` is equivalent with: | ||
| ```c++ | ||
| start_detached(scope.nest(snd)); |
There was a problem hiding this comment.
Can't say I'm a fan of this. For this particular case, those things may well be equivalent, but anywhere that I see a start_detached, alarm bells will ring. Whereas when I see a spawn on an async scope, alarm bells won't ring, that's the bread-and-butter operation and I know that it's likely going to be correct.
There was a problem hiding this comment.
Yes, I think this bothers me as well. Suggesting that we use start_detached to achieve this implies that start_detached is itself a good algorithm. Yet if it is used for anything that doesn't hand ownership anywhere (like chaining anything after that nest call) it probably isn't. So we end up in a situation where we recommend to not use start_detached except where its input is a nest-sender. In which case it's an async_scope algorithm and it might as well be explicitly tied to the async_scope.
There was a problem hiding this comment.
It makes a lot of sense. I would also want us to ensure that we don't use start_detached without some clear lifetime safety nets.
I see two good ways out of this:
- completely remove
start_detached - make
start_detachedwork by default only withasync_scopesenders; i.e., disallow general senders, unless somebody makes an explicit specialization
The alternative of keeping start_detached and saying that it should never be used, seems less ideal.
I feel this is the main question we need to answer before choosing how asnyc_scope should be used.
Do you think it's worth writing a quick paper on this, to get a general direction from LEWG? Or should we just put this question in this paper?
| However, this can be added on top. | ||
| If `scope` is an object of type `async_scope`, and `stoken` is a stop token, then we can build cancellation in with the following: | ||
| ```c++ | ||
| scope.nest(stop_when(snd, stoken)) |
There was a problem hiding this comment.
I think we would want to see the cancellation path here too.
If we are going to wait for the scope to complete we need to decide what happens - do we stop accepting work (which cancellation tied to the scope can allow) and cancel in-flight work so we can guarantee safe shutdown?
Kirk's recent work on a phased async_scope makes this a bit more explicit.
There was a problem hiding this comment.
In this alternative, you loose the ability co cancel any future work (unless everything is spawn using the construct above). This may be an inconvenience, but not all flows might have cases in which work is spawned when we are trying to end the scope. It provides a bit more granular access to the user to decide whether global cancellation is needed, or not.
If global cancellation is needed, then the user can always make a small abstraction on top of async_scope that always packs the stop token with the work needing to be spawned.
I guess, in an ideal world, we would propose two abstractions:
- a low level abstraction, similar to the one in this alternative
- a friendlier version of
async_scope, as described by the main paper
Put the new alternative is a separate part of the document, making it easier to read and compare with the base proposal.