>I understand that explicit API contracts are what matters. My concern, 
>however, is that even if the API contract explicitly states no happens-before 
>guarantee upon an unchecked exception, this behavior would still be a 
>significant deviation from established visibility standards in other JDK APIs.

Would you mind clariying exactly what you mean here—what happens-before 
completion/exception?

Cheers,
√


Viktor Klang
Software Architect, Java Platform Group
Oracle

________________________________
From: Jige Yu <yuj...@gmail.com>
Sent: Tuesday, 8 July 2025 04:26
To: Viktor Klang <viktor.kl...@oracle.com>
Cc: core-libs-dev@openjdk.org <core-libs-dev@openjdk.org>
Subject: [External] : Re: Question about mapConcurrent() Behavior and 
Happens-Before Guarantees

Thanks for the quick reply, Viktor!

On Mon, Jul 7, 2025 at 2:35 AM Viktor Klang 
<viktor.kl...@oracle.com<mailto:viktor.kl...@oracle.com>> wrote:
Hi Jige,

>Initially, I thought this design choice might provide a strong happens-before 
>guarantee. My assumption was that an application catching a RuntimeException 
>would be able to observe all side effects from the virtual threads, even 
>though this practice is generally discouraged. This seemed like a potentially 
>significant advantage, outweighing the risk of a virtual thread failing to 
>respond to interruption or responding slowly.

Unless explicitly stated in the API contract, no such guarantees should be 
presumed to exist.


I understand that explicit API contracts are what matters. My concern, however, 
is that even if the API contract explicitly states no happens-before guarantee 
upon an unchecked exception, this behavior would still be a significant 
deviation from established visibility standards in other JDK APIs.

For instance, both parallel streams and Future.get() provide a happens-before 
guarantee upon completion (or exceptional completion in the case of 
Future.get()). So users will most likely take it for granted. If 
mapConcurrent() were to not offer this, it would potentially be the first 
blocking JDK API that doesn't honor happens-before in such a scenario. This 
inconsistency would likely be surprising and potentially confusing to users who 
have come to expect this behavior in concurrent programming constructs within 
the JDK.


As for general resource-management in Stream, I have contemplated designs for 
Gatherer (and Collector) to be able to participate in the onClose actions, but 
there's a lot of ground to cover to ensure correct ordering and 
sufficiently-encompassing of cleanup action execution.


Yeah. I agree that hooking into onClose() could provide a more reliable 
mechanism for cleanup.

My primary concern though, is the change it imposes on the call-site contract. 
Requiring all users of mapConcurrent() to adopt a try-with-resources syntax, 
while ideal for correctness, introduces a burden and is more subject to users 
forgetting to do so, potentially leading to resource leaks.

My previously proposed collectingAndThen(toList(), list -> 
list.stream().gather(mapConcurrent())) idea, on the other hand, avoids this 
call-site contract change. Being a collector, it needs to first consume the 
input, similar to how most Collectors operate. So it might be a less intrusive 
path to ensure proper resource handling without altering usage patterns.

Cheers,
√


Viktor Klang
Software Architect, Java Platform Group
Oracle
________________________________
From: core-libs-dev 
<core-libs-dev-r...@openjdk.org<mailto:core-libs-dev-r...@openjdk.org>> on 
behalf of Jige Yu <yuj...@gmail.com<mailto:yuj...@gmail.com>>
Sent: Thursday, 3 July 2025 16:36
To: core-libs-dev@openjdk.org<mailto:core-libs-dev@openjdk.org> 
<core-libs-dev@openjdk.org<mailto:core-libs-dev@openjdk.org>>
Subject: Question about mapConcurrent() Behavior and Happens-Before Guarantees


Hi JDK Core Devs,

I'm writing to you today with a question about the behavior of mapConcurrent() 
and its interaction with unchecked exceptions. I've been experimenting with the 
API and observed that mapConcurrent() blocks and joins all virtual threads upon 
an unchecked exception before propagating it.

Initially, I thought this design choice might provide a strong happens-before 
guarantee. My assumption was that an application catching a RuntimeException 
would be able to observe all side effects from the virtual threads, even though 
this practice is generally discouraged. This seemed like a potentially 
significant advantage, outweighing the risk of a virtual thread failing to 
respond to interruption or responding slowly.

However, I've since realized that mapConcurrent() cannot fully guarantee a 
strong happens-before relationship when an unchecked exception occurs somewhere 
in the stream pipeline. While it can block and wait for exceptions thrown by 
the mapper function or downstream operations, it appears unable to intercept 
unchecked exceptions thrown by an upstream source.

Consider a scenario with two input elements: if the first element starts a 
virtual thread, and then the second element causes an unchecked exception from 
the upstream before reaching the gather() call, the virtual thread initiated by 
the first element would not be interrupted. This makes the "happens-before" 
guarantee quite nuanced in practice.

This brings me to my core questions:

  1.  Is providing a happens-before guarantee upon an unchecked exception a 
design goal for mapConcurrent()?

  2.  If not, would it be more desirable to not join on virtual threads when 
unchecked exceptions occur? This would allow the application code to catch the 
exception sooner and avoid the risk of being blocked indefinitely.

Thank you for your time and insights.

Best regards,

Ben Yu

Reply via email to