[External] : Re: My experience with Structured Concurrency

David Alayachew davidalayachew at gmail.com
Sun Aug 17 20:50:49 UTC 2025


Hey, I've been trying out your idea, but I can't find a single place where
doing it would apply.

I understand the idea of nesting scopes well enough. For example, I could
put a innerScope inside of a outerscope.fork() call. Alternatively, I could
have another scope after the outerScope.join() call.

But doing it the first way would mean that I am creating an entire scope
for each outerScope subtask. Is that what you are proposing? That makes
sense if there are multiple tasks worth of processing that I want. For
example, if the outerscope subtask would return a list of things, each of
which, I could do further processing on. But that doesn't make sense if
each outerScope subtask has only one "thing" that needs further downstream
processing. The entire point of a scope is to do multi-threading. Having
one task defeats the purpose of that.

And doing it the second way means I would have to wait for all of my tasks
to complete from the outerScope before the innerScope could do anything.

In both cases, I wouldn't really be able to gain anything for my composed
joiners.

And to be clear, most of my Joiner compositions were simply nesting and
adding to an existing implementation. For example, I had a joiner that
closed the scope after 3 counts of tasks failing with SomeException. Well,
I then used composition to make it cancel AND return the tasks up to and
including the 3 failed. In that case, I don't see how I would gain anything
by adding another scope.

Could you help me see what you had in mind? Maybe I am just lacking
creativity.




On Fri, Aug 15, 2025 at 8:04 PM David Alayachew <davidalayachew at gmail.com>
wrote:

> Oh, I'm already doing that. Lol, I love STS BECAUSE nesting scopes is so
> easy to do.
>
> But that's an interesting idea. I kind of see what you mean -- maybe I am
> stuffing too much logic into a single Joiner. I can't tell yet, so I guess
> I'll just have to try it out and get back to you.
>
> Thanks for the tip.
>
>
> On Fri, Aug 15, 2025, 5:09 PM Viktor Klang <viktor.klang at oracle.com>
> wrote:
>
>> Hi David,
>>
>> Thanks for the added detail, that really helps my understanding of your
>> situation.
>>
>> Did you try/consider/evaluate nested scopes (each with different Joiner
>> strategies) over composing Joiners themselves?
>> And if you did, what were your findings when comparing those two
>> different approaches?
>>
>> Cheers,
>>>>
>>
>> *Viktor Klang*
>> Software Architect, Java Platform Group
>> Oracle
>> ------------------------------
>> *From:* David Alayachew <davidalayachew at gmail.com>
>> *Sent:* Friday, 15 August 2025 20:53
>> *To:* Viktor Klang <viktor.klang at oracle.com>
>> *Cc:* loom-dev <loom-dev at openjdk.org>
>> *Subject:* [External] : Re: My experience with Structured Concurrency
>>
>> One other detail I'd like to highlight.
>>
>> Much like Collectors and Gatherers, there are a handful of super useful
>> ones that you use everywhere, and then the rest are ad-hoc, inline ones
>> where you sort of just make your own to handle a custom scenario. If you
>> use streams often, you will run into those frequently, and that's why those
>> factory methods are fantastic.
>>
>> Well, I have kind of found myself in the same position for Joiners.
>> Joiners aren't as complex as Collectors and Gatherers, so there has
>> certainly been less need for it. But I am also only a few weeks into using
>> Joiners (though, I used STS for over a year). If I feel this strain now,
>> then I feel like this experience is definitely worth sharing.
>>
>> On Fri, Aug 15, 2025, 2:44 PM David Alayachew <davidalayachew at gmail.com>
>> wrote:
>>
>> Sure.
>>
>> Long story short, the biggest reason why STS is so useful for me is
>> because it allows me to fire off a bunch of requests, and handle their
>> failures and outcomes centrally. That is the single most useful feature of
>> this library for me. It's also why Future.status was not so useful for me
>> -- it calls get under the hood, and therefore might fail! Handling that was
>> too much scaffolding.
>>
>> So, when someone recently challenged me to use Joiners (rather than the
>> old STS preview versions I was used to), I started creating Joiners to
>> handle all sorts of failure and outcomes. At first, a lot of them could be
>> handled by the Joiner.awaitUntil(), where I would just check and see if the
>> task failed, then handle the error. But as I got further and further along,
>> I started needing to add state to my Joiners in order to get the failure
>> handling that I wanted. For example, if a certain number of timeouts occur,
>> cancel the scope. Well, that necessitates an AtomicNumber.
>>
>> Then, as the error-handling got more and more complex, I started finding
>> myself making a whole bunch of copy paste, minor variations of similar
>> Joiners. Which isn't bad or wrong, but started to feel some strain. Now, I
>> need to jump through an inheritance chain just to see what my Joiner is
>> really doing. It wasn't so bad, but I did start to feel a little uneasy.
>> Bad memories.
>>
>> So, the solution to a problem like this is to create a Joiner factory.
>> Which is essentially what I started to write before I started remembering
>> how Collectors and Gatherers worked. At that point, I kind of realized that
>> this is worth suggesting, which prompted me to write my original email.
>>
>> Like I said, not a big deal if you don't give it to me -- I can just make
>> my own.
>>
>> But yes, that is the surrounding context behind that quote. Let me know
>> if you need more details.
>>
>>
>> On Fri, Aug 15, 2025, 9:25 AM Viktor Klang <viktor.klang at oracle.com>
>> wrote:
>>
>> Hi David,
>>
>> First of all—thank you for your feedback!
>>
>> I'm curious to learn more about why you ended up in the situation you
>> describe below, specifically about what use-cases led you into wishing for
>> an augmentation to Joiner to facilitate composition.
>>
>> Are you able to share more details?
>>
>> >Which, funnily enough, led to a slightly different problem -- I found
>> myself wanting an easier way to create Joiners. Since I was leaning on
>> Joiners so much more heavily than I was for STS, I ended up creating many
>> Joiners that do almost the same thing, with just minor variations. And
>> inheritance wasn't always the right answer, as I can't inherit from
>> multiple classes. Plus, most of my joiners were stateful, but I only wanted
>> the non-stateful parts of it. I could do composition, but it sort of felt
>> weird to delegate to multiple other Joiners.
>>
>> Cheers,
>>>>
>>
>> *Viktor Klang*
>> Software Architect, Java Platform Group
>> Oracle
>> ------------------------------
>> *From:* loom-dev <loom-dev-retn at openjdk.org> on behalf of David
>> Alayachew <davidalayachew at gmail.com>
>> *Sent:* Friday, 15 August 2025 11:52
>> *To:* loom-dev <loom-dev at openjdk.org>
>> *Subject:* My experience with Structured Concurrency
>>
>> Hello @loom-dev <loom-dev at openjdk.org>,
>>
>> I just wanted to share my experience with Structured Concurrency. I had
>> actually been using it for a while now, but only recently got experience
>> with the new Joiner. After trying it out, my previously stated opinion has
>> changed.
>>
>> Overall, Structured Concurrency has been a pleasure. I'll avoid repeating
>> ALL my old thoughts and just highlight the KEY details.
>>
>> * Structured Concurrency is excellent for complex error-handling.
>> Receiving exceptions via the subtask makes all the error-handling less
>> painful.
>> * Structured Concurrency makes nesting scopes a breeze, a task I
>> historically found very painful to do.
>> * Inheritance allows me to take an existing Scope (now Joiner), and
>> modify only what I need to in order to modify it for my use case. Great for
>> reusing old strategies in new ways.
>>
>> Now for the new stuff -- having Joiner be the point of extension
>> definitely proved to be the right move imo. I didn't mention this in my
>> original message, but while it was easy to get a scope set up using
>> inheritance, it wasn't always clear what invariants needed to be
>> maintained. For example, the ensureOwnerAndJoined method. Was that
>> something we needed to call when inheriting? On which methods? Just join()?
>>
>> The Joiner solution is comparatively simpler, which actually meant that I
>> ended up creating way more Joiners, rather than only several STS'. Joiners
>> invariants are obvious, and there is no ambiguity on what is expected from
>> the implementor.
>>
>> Which, funnily enough, led to a slightly different problem -- I found
>> myself wanting an easier way to create Joiners. Since I was leaning on
>> Joiners so much more heavily than I was for STS, I ended up creating many
>> Joiners that do almost the same thing, with just minor variations. And
>> inheritance wasn't always the right answer, as I can't inherit from
>> multiple classes. Plus, most of my joiners were stateful, but I only wanted
>> the non-stateful parts of it. I could do composition, but it sort of felt
>> weird to delegate to multiple other Joiners.
>>
>> Part of me kept wondering how well a factory method, similar to the ones
>> for Collectors and Gatherers, might fare for Joiners.
>>
>> Regardless, even if we don't get that factory method, this library has
>> been a pleasure, and I can't wait to properly implement this once it goes
>> live.
>>
>> Thank you for your time and consideration.
>> David Alayachew
>>
>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/loom-dev/attachments/20250817/a1d7623e/attachment-0001.htm>


More information about the loom-dev mailing list