Timeouts in structured concurrency

Holo The Sage Wolf holo3146 at gmail.com
Thu Dec 18 21:32:01 UTC 2025


join does not return void in general.

join return the value of "successful run" where "successful run" is defined
in the Joiner.

One some joiners join() will return a stream of all tasks, on others it
will return the result of the first successful task, and on other it will
return nothing because successful run produce no output.

“success = null, failure = throw” is a fundamental misunderstanding of the
API.




On Thu, 18 Dec 2025, 23:25 Eric Kolotyluk, <eric at kolotyluk.net> wrote:

> My $0.02
>
> Why are we still relying so heavily on exceptions as a control-flow
> mechanism?
>
> Consider the current StructuredTaskScope design:
>
> The join() method waits for all subtasks to succeed or any subtask to fail.
> The join() method returns null if all subtasks complete successfully.
> It throws StructuredTaskScope.FailedException if any subtask fails, with
> the exception from the first subtask to fail as the cause.
>
> This design encodes normal outcomes as null and expected failure modes as
> exceptions. That choice forces callers into the least informative and least
> composable error-handling model Java has.
>
> Returning null for success is especially problematic. null conveys no
> semantic information, cannot carry context, and pushes correctness checks
> to runtime. It remains one of Java’s most damaging design decisions, and
> Loom should not be perpetuating it.
>
> Optional<T> exists, but it is only a partial solution and does not address
> error information. In this context, even Optional<Void> would be an
> improvement over null, but it still leaves failure modeled exclusively as
> exceptional control flow.
>
> I also want to be clear that I am not confusing try-with-resources with
> exceptions. StructuredTaskScope being AutoCloseable is the right design
> choice for lifetime management and cancellation, and try blocks are the
> correct mechanism for that. However, scope lifetime and outcome reporting
> are separable concerns. The use of try does not require that task outcomes
> be surfaced exclusively via thrown exceptions.
>
> As a recent Rust convert, the contrast is stark. Rust’s Result<T, E>
> treats failure as a first-class, explicit outcome, enforced by the type
> system. Java doesn’t need to abandon exceptions—but it does need to support
> alternate paradigms where failure is expected, structured, and composable.
>
> APIs like join() should envision a future beyond “success = null, failure
> = throw”. Even a simple structured outcome type—success or failure—would be
> a step forward. Exceptions could remain available for truly exceptional
> conditions, not routine concurrency outcomes.
>
> Loom is a rare opportunity to modernize not just how Java runs concurrent
> code, but how Java models correctness and failure. Re-entrenching null and
> exception-only outcomes misses that opportunity.
>
> I’ll stop bloviating now.
>
> Sincerely,
> Eric Kolotyluk
>
>
> On 2025-12-18 1:00 PM, David Alayachew wrote:
>
> For 1, the javadoc absolutely does help you. Please read for open.
>
>
> https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/concurrent/StructuredTaskScope.html#open()
>
> As for verbose, can you go into more detail? This is a traditional builder
> pattern addition, so it is literally 1 static method call.
>
> That said, if you dislike a 0 parameter call being forced into being a 2
> paramefer call when you need to add timeout, then sure, I think adding an
> overload for that static method that takes in the configFunction is
> reasonable. I'd support that.
>
>
> On Thu, Dec 18, 2025, 3:46 PM Holo The Sage Wolf <holo3146 at gmail.com>
> wrote:
>
>> Hello Loom devs,
>> Few years ago I experimented in a personal PoC project with
>> StructuredConcurrency in Java 19 and I had to stop working on it for
>> personal reasons.
>>
>> Recently I came back to the project and updated it to Java 25 and had to
>> change my code to the new way the API is built and while doing that I
>> noticed a couple of stuff I want to point out:
>>
>> 1. The default Joiner method can't receive timeout
>> Obviously that is wrong, but the API and JavaDoc don't actually help you.
>> Say you start with:
>>  ```java
>> try (var scope =  StructuredTaskScope.open()) {
>>     ...
>> }
>> ```
>> And I want to evolve the code to add timeout, I look at
>> the StructuredTaskScope static methods, and won't see any way to do that.
>> After reading a bit what StructuredTaskScope.open(Joiner, configFunction)
>> does, I will realise that I can set the timeout using the configFunction.
>> But then I will encounter the problem that I need to provide a Joiner,
>> currently the only way to actually get the "no args method"-joiner is to
>> look at the source code of the method, see which Joiner it uses and copy
>> that into my method to get:
>>  ```java
>> try (var scope =
>> StructuredTaskScope.open(Joiner.awaitAllSuccessfulOrThrow(), (conf) ->
>> ...)) {
>>     ...
>> }
>> ```
>> Not only is this a lot of work to do something very simple, there is a
>> high chance that people who start learning concurrency will want to use
>> timeout before they even know what the Joiner object is.
>>
>> 2. Changing only the timeout is "verbose".
>> I can only talk from my experience, so I may have the wrong impression,
>> but I feel like setting timeout is orders of magnitude more common than
>> changing the default ThreadFactory (especially when using virtual threads)
>> or setting a name.
>> I feel like adding a couple of overloads of the open method that takes
>> only an extra parameter of duration will be convenient:
>> > StructuredTaskScope.open()
>> > StructuredTaskScope.open(Duration timeout)
>> > StructuredTaskScope.open(Joiner joiner)
>> > StructuredTaskScope.open(Joiner joiner, Duration timeout)
>> > StructuredTaskScope.open(Joiner joiner, Function<Configuration,
>> Configuration> configFunction)
>>
>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/loom-dev/attachments/20251218/38f61f2e/attachment-0001.htm>


More information about the loom-dev mailing list