> On 26 Sep 2025, at 18:35, Alan Bateman <[email protected]> wrote:
>
>
>
> On 26/09/2025 14:04, Viktor Klang wrote:
>> :
>>
>> >If the main scope body includes any blocking logic, it might end up hanging
>> >indefinitely, while all the other forks have been cancelled.
>>
>> That statement is true by definition—any code which is blocking indefinitely
>> and is not interrupted, is by definition blocking indefinitely.
>>
>> >The main scope’s body awaits for data from either of them (on a queue), and
>> >when an element is produced, sends it downstream. Now, if we’re not careful
>> >with error handling, an exception in one of the substreams will cancel the
>> >scope, but the main scope will indefinitely wait on data, not aware of the
>> >error.
>>
>> This sounds, to me, like another issue with an absent feature—Inter-task
>> communication channels.
>
> I agree, this example would be a good fit for channels.
>
> On interrupting the main task ("Non-uniform cancellation" section in the
> article), it's a good topic to discuss. When the scope is cancelled then the
> outstanding subtasks are interrupted so that they finish up quickly (their
> results aren't needed). It would mostly wrong to interrupt the main task as
> you aren't looking for the main task to finish, instead you want the main
> task to wakeup (from join) to process the outcome. It's important to say that
> cancellation does not mean failure, it just means there is an outcome, e.g.
> anyResultOrThrow cancels after any subtask completes successfully. A
> lengthy/looping forking phase can use isCancelled to avoid doing unnecessary
> work if needed. (Early prototypes did interrupt the main task but this was
> problematic on many levels and adds booking overhead to ensure that the
> processing of that interrupt is restricted to code in the block.)
Yes, I think that interrupting the main task in fact cannot be implemented
properly, for the reasons you describe (an interruption b/c of scope cleanup is
indistinguishable from an interruption coming externally). But you definitely
have a point that cancellation is a result, not necessarily failure. On the
other hand, there are "unexpected" exceptions (something along the original
distinction between checked/unchecked exceptions), which *are* a failure, and
when I think it’d be ideal if we could clean up the whole scope (ensuring that
no threads stay alive), and re-throw for further handling. This distinction
("fatal"/"non-fatal" or "failulre" / "result") would logically belong to the
Joiner, I suppose?
Using the isCancelled flag as part of the scope’s body is an interesting idea,
but then any blocking operations (such as retrieving an element from a queue)
would need a timeout? And when the timeout passes, we’d check the flag?
Adam
--
Adam Warski
https://warski.org