> From: "Alan Bateman" <[email protected]> > To: "Adam Warski" <[email protected]>, "loom-dev" <[email protected]> > Sent: Friday, September 26, 2025 11:20:57 AM > Subject: Re: Problem report on the usage of Structured Concurrency (5th > preview)
> On 26/09/2025 07:37, Adam Warski wrote: >> Good morning, >> with the release of Java 25, I’ve attempted to migrate my >> virtual-thread-native, >> reactive-streaming-like library from Java 21 to Java 25 scopes. So far I’ve >> been using my own wrapper on StructuredConcurrencyScope, but with that >> migration I wanted to eliminate it, and just use SCS directly. However, I >> encountered some problems with SCS’s design; I've summarised them in a blog >> ( [ >> https://softwaremill.com/critique-of-jep-505-structured-concurrency-fifth-preview >> | >> https://softwaremill.com/critique-of-jep-505-structured-concurrency-fifth-preview >> ] ), and then prompted by a discussion on Reddit that followed ( [ >> https://www.reddit.com/r/java/comments/1nq25yr/critique_of_jep_505_structured_concurrency_fifth/ >> | >> https://www.reddit.com/r/java/comments/1nq25yr/critique_of_jep_505_structured_concurrency_fifth/ >> ] ), I’m writing here. > Thanks for writing down your experiences. > (I'll reply to your main points 1 and 2 later as they concern advanced use of > the API that isn't straight-forward fan-out.) > Just some quick comments on the two smaller points here: >> 3) The final two problems are more of nitpicks. First, a `timeout` method can >> easily be implemented using the machinery of the SCS, without additional >> configuration parameters. Special-casing for this seems odd, as timeout is >> only >> one example from a family of "resiliency" methods, others including retries, >> repeats etc. These as well, could be implemented on top of virtual threads >> and >> SCS as methods, without special support from the SCS API itself. > If you implement your own Joiner with a side channel to this special timeout > subtask then it would work. However in general, it will be policy (and hence > Joiner) dependent as to whether a failing subtask will cancel the scope. > You'll > see what I mean if you try with it a Joiner created by the > anySuccessfulResultOrThrow factory method. Same thing with any Joiner that has > a policy that doesn't unconditionally cancel when a subtask fails. > Another point on this topic is that the timeout applies to the scope and > configuring it when creating the scope is good. Additionally, having join > throwing TimeoutException is useful to distinguish the timeout case from other > failure outcomes. >> 4) The Subtask.get() method is confusing, as it has the semantics of >> Future.resultNow(), but the nomenclature of Future.get(). Since Future.get() >> is >> quite well established, I think it’s reasonable to assume, without prior >> knowledge of the SCS API, that Subtask.get() is blocking as well. However, it >> works rather differently. I understand that .get() is a fitting name, however >> given the existing functionalities in place, I would consider changing the >> name >> to something without naming or semantical clashes. > Subtask<T> is a Supplier<T>, and maybe more of the examples should use > Supplier<T> to avoid anyone thinking Future::get. If someone does attempt to > use Subtask::get before joining then it will throw of course, so they know to > use it after join. Ron has long argued that fork should just return a > Supplier, > it's only use is when using subtasks return results of different types. If > subtasks all return results of the same type then the outcome from join is > much > more useful, no need for Supplier::get. I think i agree with Ron, fork() should return a Supplier with get() failing if not called from the main thread. In that case, maybe Subtask.get() should not throw an exception when called from the main thread. > -Alan Rémi
