Skip to content

Conversation

@RobertLeahy
Copy link
Contributor

Please do not squash.

Implementation of P3955 - It's Scopes All the Way Down.

@copy-pr-bot
Copy link

copy-pr-bot bot commented Jan 25, 2026

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

// sending the completion signal may end our lifetime, which means
// we shouldn't send references into ourselves, therefore we move
// all the non-references onto the stack
std::remove_cvref_t<decltype(tuple_or_monostate)>(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can throw.

Certain algorithms, for example when_all, must store the completions of
child operations and then later examine or forward them. The typical way
of doing this is via decay-copy into a tuple, but this erases the
reference-ness of values and errors.

The class template added by this commit, exec::
storage_for_completion_signatures, makes it convenient to store,
examine, and forward the completions of some asynchronous operation,
while also being reference-aware.
Type alias for the type returned by std::forward_like.
stdexec::let_value performs two functions:

- Persisting the values sent by the predecessor, and
- Predicating the successor on those persisted values

Which leaves space for a lower level primitive: One which simply
predicates a successor sender on the values sent by a predecessor. That
lower level primitive is exec::invoke.
Adds concepts which deal with exit scope senders. An exit scope sender
performs the clean up/rollback operations necessary to leave a "scope."
As such an exit scope sender must (in addition to being a sender):

- Send exactly stdexec::set_value_t()
- Be nothrow connectable
- Be nothrow movable
- Be nothrow decay-copyable

Note that in some sense an exit scope sender is analogous to a
destructor.
Adds concepts which deal with enter scope senders. Whereas an exit scope
sender performs the operations necessary to leave a scope, an enter
scope sender performs the operations necessary to enter a scope. Just
entering a scope is insufficient, however, as if a scope is entered it
must be exited. Therefore enter scope senders not only perform the
actions which must be taken to enter a scope, but also complete with a
sender which undoes those actions, i.e. all enter scope senders are a
higher-order sender.
An algorithm which combines N enter scope senders into a single enter
scope sender which enters the scopes represented by each of its children
in parallel, i.e. the scopes are entered in no particular order, as if
by stdexec::when_all.
Algorithm which accepts an enter scope sender, and a sender to run
within the scope represented by the enter scope sender, respectively.
The resulting operation:

1. Enters the scope represented by the enter scope sender
2. Runs the operation represented by the other sender
3. Stores the completion of the above
4. Exits the scope using the exit scope sender yielded by the operation
   in step 1
5. Yields the completion stored in step 3
Adds the concepts which deal with asynchronous objects. An asynchronous
object is an object whose constructor and destructor are asynchronous
operations (as opposed to regular, synchronous object which have
constructors and destructors which are regular, synchronous functions).

Whereas senders are a fully-curried asynchronous function the concept of
an asynchronous object embodied by the concepts in this commit is a
fully-curried asynchronous constructor. The particular form of the
aforementioned is a unary invocable whose:

- Sole argument is a pointer to storage whereat the asynchronous object
  shall be placed
- Returned value is an enter scope sender which constructs the object
  (asynchronously) when the scope is entered, and destroys the object
  (asynchronously) when the scope is exited
Enables consumption of asynchronous objects. Accepts an invocable, and N
asynchronous objects. Forms an operation which, when connected:

1. Provides the asynchronous objects with storage in its operation state
2. Passes all enter scope senders obtained in step 1 to exec::
   enter_scopes to combine them
3. Connects the resulting enter scope sender

And when started:

1. Starts the enter scope sender which was connected in step 3 above
2. If the operation started in step 1 sends error or stopped, completes
   immediately with that completion, otherwise passes a reference to all
   of the newly-constructed objects to the invocable, obtaining a sender
3. Connects and starts the sender obtained in step 2
4. Upon the completion of that operation, stores the completion thereof
5. Connects and starts the exit scope sender which destroys the objects
   constructed in step 1
6. Upon the completion of that operation, ends the overall operation
   with the completion stored in step 4
Adaptor which transforms a regular, synchronous object into an
asynchronous object.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant