Skip to content

Conversation

@RobertLeahy
Copy link
Contributor

@RobertLeahy RobertLeahy commented Dec 28, 2025

The asynchronous loop of exec::repeat_effect_until proceeds until the child operation sends a value which converts to true. Previously this process proceeded as follows:

  1. Accept the child operation's result by value to avoid dangling references into the operation state (see next step)
  2. Destroy the child operation state
  3. Convert the result accepted in step 1 to bool and check if it's true, if so end the operation, otherwise
  4. Connect the child sender
  5. Start the new child operation

Unfortunately step 1 meant that the result of the child operation would be decay-copied to pass it by value. This occurred within a noexcept function and therefore if that decay-copy threw std::terminate would be called.

Moreover the previous implementation did not forward the result in step 3. This meant that if the child's result type was only rvalue convertible to bool compilation would fail.

Additionally the same pass-by-value strategy was used for errors. However when handling an error there's no need to destroy the child operation state due to the fact the operation is ending and therefore doesn't need to reconnect the child sender for the next iteration (note this logic also applies to successful completion).

Fixed all of the above by handling completion of the child operation as follows:

  1. If the child completed with error or stopped simply forward that completion through (note the child operation state is not destroyed and will be cleaned up by the destructor of the operation state for exec::repeat_effect_until) (note that this saves one decay-copy over the previous implementation but requires a branch in the destructor, which was already present), otherwise
  2. Forward the result, convert that forwarded expression to bool, and check if it's true, if so end the operation (note that once again the child operation state is not destroyed and once again a decay-copy is eliminated), otherwise
  3. Destroy the child operation state
  4. Connect the child sender
  5. Start the new child operation

@copy-pr-bot
Copy link

copy-pr-bot bot commented Dec 28, 2025

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.

@ericniebler
Copy link
Collaborator

/ok to test c6f3d4e

@RobertLeahy RobertLeahy force-pushed the repeat_effect_until_20251228 branch 2 times, most recently from 93c3e3f to 1cee425 Compare December 29, 2025 03:01
@ericniebler
Copy link
Collaborator

/ok to test 1cee425

@RobertLeahy RobertLeahy force-pushed the repeat_effect_until_20251228 branch from 1cee425 to 2367857 Compare December 29, 2025 19:37
…rvation

The asynchronous loop of exec::repeat_effect_until proceeds until the
child operation sends a value which converts to true. Previously this
process proceeded as follows:

1. Accept the child operation's result by value to avoid dangling
   references into the operation state (see next step)
2. Destroy the child operation state
3. Convert the result accepted in step 1 to bool and check if it's true,
   if so end the operation, otherwise
4. Connect the child sender
5. Start the new child operation

Unfortunately step 1 meant that the result of the child operation would
be decay-copied to pass it by value. This occurred within a noexcept
function and therefore if that decay-copy threw std::terminate would be
called.

Moreover the previous implementation did not forward the result in step
3. This meant that if the child's result type was only rvalue
convertible to bool compilation would fail.

Additionally the same pass-by-value strategy was used for errors.
However when handling an error there's no need to destroy the child
operation state due to the fact the operation is ending and therefore
doesn't need to reconnect the child sender for the next iteration (note
this logic also applies to successful completion).

Fixed all of the above by handling completion of the child operation as
follows:

1. If the child completed with error or stopped simply forward that
   completion through (note the child operation state is not destroyed
   and will be cleaned up by the destructor of the operation state for
   exec::repeat_effect_until) (note that this saves one decay-copy over
   the previous implementation but requires a branch in the destructor,
   which was already present), otherwise
2. Forward the result, convert that forwarded expression to bool, and
   check if it's true, if so end the operation (note that once again the
   child operation state is not destroyed and once again a decay-copy is
   eliminated), otherwise
3. Destroy the child operation state
4. Connect the child sender
5. Start the new child operation
@RobertLeahy RobertLeahy force-pushed the repeat_effect_until_20251228 branch from 2367857 to 657e4d2 Compare December 29, 2025 20:11
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.

2 participants