> 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

Reply via email to