Problem report on the usage of Structured Concurrency (5th preview)
Adam Warski
adam at warski.org
Mon Sep 29 10:28:26 UTC 2025
> On 26 Sep 2025, at 18:35, Alan Bateman <Alan.Bateman at oracle.com> 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
More information about the loom-dev
mailing list