cplusplus / sender-receiver

Issues list for P2300
Apache License 2.0
20 stars 3 forks source link

`bulk` does not specify what customizations need to do with exceptions thrown from invocations of `f` #276

Open lewissbaker opened 3 months ago

lewissbaker commented 3 months ago

The current wording of bulk() in P2300R10 has a default implementation that invokes set_error(rcvr, current_exception()) if any of the invocations of f exit with an exception.

However, this strategy only works for sequential execution implementation strategies.

We need to specify what the semantics should be if f exits with an exception during implementations of bulk that execute f concurrently.

Several options come to mind:

  1. do not execute f concurrently if f is potentially throwing
  2. require that f is noexcept
  3. pick an arbitrary exception thrown by one of the invocations of f
  4. pick the first exception thrown by an invocation of f - e.g. decided by some atomic-rmw operation.
  5. reduce the exceptions into a single error value (perhaps by some user-provided reduction operation)
  6. terminate on error (this would be inconsistent with the well-defined behaviour of the default sequential implementation)
  7. encapsulate thrown exceptions into an exception_list but only guarantee that one exception exists in it - implementations may gather multiple of the exceptions

Also, what should the behaviour be for subsequent invocations of f? Should we try to cancel subsequent invocations of f after an invocation of f has exited with an exception? Or should it still try to call all subsequent invocations of f (this might be a reasonable strategy if we are reducing the errors)?

What if a stop-request is issued after the f operation exits with an exception but before the executions on other threads can stop executing? Should the operation as a whole complete with set_error or should it complete with set_stopped?

dietmarkuehl commented 3 months ago

I recall that the issue of how to deal with exceptions in concurrent algorithms was discussed at the Oulu meeting: there was a heated debate which couldn’t be settled. It may be worth to look at that discussion. For example http://wg21.link/p0323 (exception_list) and http://wg21.link/p0333 (improving parallel algorithm exception handling) are in that space. The current parallel algorithms may also have an approach.On 12 Jul 2024, at 07:13, Lewis Baker @.***> wrote: The current wording of bulk() in P2300R10 has a default implementation that invokes set_error(rcvr, current_exception()) if any of the invocations of f exit with an exception. However, this strategy only works for sequential execution implementation strategies. We need to specify what the semantics should be if f exits with an exception during implementations of bulk that execute f concurrently. Several options come to mind:

do not execute f concurrently if f is potentially throwing pick an arbitrary exception thrown by one of the invocations of f pick the first exception thrown by an invocation of f - e.g. decided by some atomic-rmw operation. reduce the exceptions into a single error value (perhaps by some user-provided reduction operation) terminate on error (this would be inconsistent with the well-defined behaviour of the default sequential implementation)

Also, what should the behaviour be for subsequent invocations of f? Should we try to cancel subsequent invocations of f after an invocation of f has exited with an exception? Or should it still try to call all subsequent invocations of f (this might be a reasonable strategy if we are reducing the errors)? What if a stop-request is issued after the f operation exits with an exception but before the executions on other threads can stop executing? Should the operation as a whole complete with set_error or should it complete with set_stopped?

—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you are subscribed to this thread.Message ID: @.***>

lewissbaker commented 3 months ago

The current parallel algorithms are specified to terminate if any of the element-access functions or the per-element operation to be performed exits with an exception - there is no attempt to try to combine the errors together.

That would be one way to approach this - terminate if anything fails within a bulk() operation. However, that would be inconsistent with the current specification of the default bulk implementation, which completes with an exception_ptr of the first exception to be thrown.

lucteo commented 2 months ago

I would add another option:

  1. It's implementation defined which error will be transmitted, but an error needs to be sent to the completion handler.

This would allow the implementation to pick an arbitrary exception from what the function has thrown, group some of the exceptions into another exception (i.e., exception_list) or throw an implementation-defined exception (e.g., cannot_serialize_user_exception). For me, there 3 strategies are the only practically viable strategies that the implementations can use.

inbal2l commented 1 week ago

Issue is addressed in: "P3481: Summarizing std::execution::bulk() issues" (Lewis Baker, Lucian Radu Teodorescu, Ruslan Arutyunyan)