brycelelbach / wg21_p2300_execution

`std::execution`, the proposed C++ framework for asynchronous and parallel programming.
Apache License 2.0
19 stars 6 forks source link

Question on sync_wait return type and cancellation #48

Closed MikailBag closed 11 months ago

MikailBag commented 1 year ago

As written in https://www.open-std.org/JTC1/SC22/WG21/docs/papers/2023/p2300r7.html#design-sender-consumer-sync_wait (I refer to R7, but AFAIK this place was not modified here in repository), sync_wait(snd) returns std::optional<value_t>, where std::nullopt means that task, described by snd, was stopped.

But according to the specification, sync_wait in fact never tries to stop its operation (and it makes perrect sense, since there is no general thread cancellation machinery like Java's Thread.interrupt).

So sync_wait may only receive stopped result if sender decided to stop itself. But if receiver started some operation, it did not lose interest in it, and this operation suddenly stopped, then it should be treated as failure, not as an normal outcome.

Also, please consider following (pseudo)code

// for the simplicity, assume that channel does not support close() operation 
void do_work(channel<Task> tasks, channel<Task> also_tasks) {
    while (true) {
        optional<Task> task = sync_wait(when_any(tasks.recv(), also_tasks().recv()));
        if (!task) {
            // ???
        }
        process_task(*task);
    }
}

Here, task has to be declared optional, because when_any returns optional. However it is clear that is never empty in fact - although when_any may stop its child operations internally, it will not stop both. But since this invariant is not encoded in type system, user has following options:

  1. Fill ??? with explanation of the situation
  2. Replace // ??? with terminate()-like call.

Neither option is perfect. It is desirable that this invariant ("task always contains some task") is encoded in type system instead, which avoids both reader confusion and having dead code.

Therefore it seems to me that 1) it should be illegal (*) for sender to invoke set_stopped if operation was not requested to stop; in particular, if receiver passes unstoppable token, it's set_stopped function should never be called. 1) sync_wait should not return std::optional.

P.S. sorry if this repository is wrong place for questions or if this question was already discussed.

ericniebler commented 11 months ago

Any sender may end with stopped for any reason it chooses. It is not a decision made by the receiver. sync_wait must be able to handle the case when a sender completes with stopped.