Skip to content

Change split algorithm to be an algorithm over an async-scope #263

Open
@lewissbaker

Description

@lewissbaker

The current (P2300R10) cancellation semantics of the split algorithm are that if any of the consumers sends a stop-request then the underlying operation is cancelled for all consumers (see #200).

We should revise the semantics so that it instead only cancels the underlying operation when the last consumer goes away. However, to do this in a way that avoids the possibility of detached work, it needs to be an algorithm over an async-scope.

We should replace the current semantic of split() with something with the following shape:

sender auto split(sender auto&& source, async_scope_token auto scope, queryable env = empty_env{});

And with the following semantics:

  • Calling split() allocates shared state for holding the child operation state and the result of the operation.
  • The shared state is allocated using get_allocator(env)
  • Calling split() nests the input sender in the async-scope using scope.nest(source) and eagerly connects the result to an internal receiver, storing the operation-state in the allocated shared state.
  • The internal receiver's get_env() returns env.
  • The returned sender has shared ownership of the shared state and is said to be "associated" with the underlying operation.
  • The returned sender type is copy-constructible and copying the sender copies the ownership of the shared state.
  • The first sender to connect/start a sender associated with the underlying operation which does not have an outstanding stop-request upon invocation of start() starts the underlying operation.
  • Destroying the split-sender before connecting/starting it releases shared ownership of the shared state.
  • The operation-state created by connecting the sender also has shared ownership of the shared-state and destroying the operation-state releases the shared ownership.
  • If the operation-state is started and then receives a stop-request before the underlying operation completes, the consumer detaches from the underlying operation, completing immediately with set_stopped().
  • If the underlying operation completes before a consumer detaches, then completes the consumer with an lvalue reference to the result datums (should this be const or non-const?)
  • When the last owner of the shared-state is released, then if the operation has either not started, or has been started and completed, then destroys the shared-state. Otherwise, if the operation has been started and has not yet completed, then defers destruction of the shared-state until the operation completes, discarding the result.

There are similar questions with this algorithm, though, about whether the allocation of the shared state should be guarded by the async-scope. i.e. so that join() completes only after all allocations made by split(), et. al. are released.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P0async-scopeIssues relating to the design of async_scope/counting_scope facilities.designneeds-paperNeeds a paper to be writtenprocessedprocessed in a meeting

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions