Feedback on Structured Concurrency (JEP 525, 6th Preview)

Remi Forax forax at univ-mlv.fr
Sun Oct 12 13:27:23 UTC 2025


> From: "Jige Yu" <yujige at gmail.com>
> To: "loom-dev" <loom-dev at openjdk.org>
> Sent: Sunday, October 12, 2025 7:32:33 AM
> Subject: Feedback on Structured Concurrency (JEP 525, 6th Preview)

> Hi Project Loom.

Hello, 

> First and foremost, I want to express my gratitude for the effort that has gone
> into structured concurrency. API design in this space is notoriously difficult,
> and this feedback is offered with the greatest respect for the team's work and
> in the spirit of collaborative refinement.

> My perspective is that of a developer looking to use Structured Concurrency for
> common, IO-intensive fan-out operations. My focus is to replace everyday async
> callback hell, or reactive chains with something simpler and more readable.

> It will lack depth in the highly specialized concurrent programming area. And I
> acknowledge this viewpoint may bias my feedback.
> High-Level Impression

> From this perspective, the current API feels imperative and more complex for the
> common intended use cases than necessary. It introduces significant cognitive
> load through its stateful nature and manual lifecycle management.
> Specific Points of Concern

>     1.

> Stateful and Imperative API: The API imposes quite some "don't do this at time
> X" rules. Attempting to fork() after join() leads to a runtime error;
> forgetting to call join() is another error; and the imperative fork / join
> sequence is more cumbersome than a declarative approach would be. None of these
> are unmanageable though.

I had a similar feeling the first time I used the API, but once you play with it, it kind of make sense. 
The API can be used when all tasks are different (concurrent tasks) or when all task are the same (parallel tasks), a more functional API will only work with the latter. 

>     1.

> Challenging Exception Handling: The exception handling model is tricky:
>         *

> Loss of Checked Exception Compile-Time Safety: FailedException is effectively an
> unchecked wrapper that erases checked exception information at compile time.
> Migrating from sequential, structured code to concurrent code now means losing
> valuable compiler guarantees.

You can propagate the exceptions but it makes the API clunkier (one more type variable everywhere) and do not solve the fundamental problem that you do not want to merge the control flow of an exception that comes from a callable with one that comes from STS.join(). By example, distinguishing if an InterruptedException is raised because the main thread is interrupted or if one of the callable is interrupted (and this is the same will all runtime exceptions). 

>     1.
>         *

> No Help For Exception Handling: For code that wants to catch and handle these
> exceptions, it's the same story of using instanceof on the getCause(), again,
> losing all compile-time safety that was available in equivalent sequential
> code.

see above 

>     1.
>         *

> Burdensome InterruptedException Handling: The requirement for the caller to
> handle or propagate InterruptedException from join() will add room for error as
> handling InterruptedException is easy to get wrong: one can forget to call
> currentThread().interrupt(). Or, if the caller decides to declare throws
> InterruptedException , the signature propagation becomes viral.

Having InterruptedException not being runtime exception is a pain. But this is a pain for all blocking methods. 
And BTW, you can also wrap it into a runtime exception (usually UncheckedIOException/IOError) which works better than currentThread().interrupt() because you do not loose the context (the stack trace) and avoid the problem of the signature propagation. 

Perhaps at some point in the future, all exceptions will be runtime exceptions (like in Kotlin or C#) but this is a Java problem not a problem of the STS API. 

>     1.
>         *

> Default Exception Swallowing: The AnySuccessOrThrow policy swallows all
> exceptions by default, including critical ones like NullPointerException ,
> IllegalArgumentException , or even an Error . This makes it dangerously easy to
> mask bugs that should be highly visible. There is no straightforward mechanism
> to inspect these suppressed exceptions or fail on specific, unexpected types.

The straightforward mechanism is to inspect the Subtasks that keep that information (if available). 

>     1.

> Conflated API Semantics: The StructuredTaskScope API unifies two very different
> concurrency patterns—"gather all" ( allSuccessfulOrThrow ) and "race to first
> success" ( anySuccessfulResultOrThrow )—under a single class but with different
> interaction models for the same method.
>         *

> In the "gather all" pattern ( allSuccessfulOrThrow ), join() returns void . The
> callsite should use subtask.get() to retrieve results.
>         *

> In the "race" pattern ( anySuccessfulResultOrThrow ), join() returns the result
> ( R ) of the first successful subtask directly. The developer should not call
> get() on individual subtasks. Having the join()+subtask.get() method spec'ed
> conditionally (which method to use and how depends on the actual policy) feels
> like a minor violation of LSP and is a source of confusion. It may be an
> indication of premature abstraction.

I kind agree on this one, i.e. i would like the semantics of when to stop the STS and the semantics of getting all subtaks or not to be separated given there are separated concern. 

>     1.

> Overly Complex Customization: The StructuredTaskScope.Policy API, while
> powerful, feels like a potential footgun. The powerful lifecycle callback
> methods like onFork(), onComplete(), onTimeout() may lower the barrier to
> creating intricate, framework-like abstractions that are difficult to reason
> about and debug.

yes, especially if you try to do reduce to a value (like a Collector) inside the Joiner but this is called out by the documentation. 

I will answer the rest of the mail, in a new message. 

Rémi 
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/loom-dev/attachments/20251012/6feda33b/attachment-0001.htm>


More information about the loom-dev mailing list