<div dir="ltr">Yes. <div><br></div><div>I got two tests that show the behavior difference between downstream exception and upstream exception:</div><div><br></div><div><div style="padding:0px 0px 0px 2px"><div style="color:rgb(0,0,0);font-family:Menlo;font-size:12pt;white-space:pre"><p style="margin:0px"><br></p><p style="margin:0px"> <span style="color:rgb(100,100,100)">@Test</span> <span style="color:rgb(127,0,85);font-weight:bold">public</span> <span style="color:rgb(127,0,85);font-weight:bold">void</span> mapConcurrently_upstreamFailureDoesNotInterrupt() {</p><p style="margin:0px"> ConcurrentLinkedQueue<Integer> <span style="color:rgb(106,62,62)">started</span> = <span style="color:rgb(127,0,85);font-weight:bold">new</span> ConcurrentLinkedQueue<>();</p><p style="margin:0px"> ConcurrentLinkedQueue<Integer> <span style="color:rgb(106,62,62)">interrupted</span> = <span style="color:rgb(127,0,85);font-weight:bold">new</span> ConcurrentLinkedQueue<>();</p><p style="margin:0px"> RuntimeException <span style="color:rgb(106,62,62)">thrown</span> = <span style="font-style:italic">assertThrows</span>(</p><p style="margin:0px"> RuntimeException.<span style="color:rgb(127,0,85);font-weight:bold">class</span>,</p><p style="margin:0px"> () -> Stream.<span style="font-style:italic">of</span>(1, 10, 3, 0)</p><p style="margin:0px"> .peek(<span style="color:rgb(106,62,62)">n</span> -> {</p><p style="margin:0px"> <span style="color:rgb(127,0,85);font-weight:bold">if</span> (<span style="color:rgb(106,62,62)">n</span> == 3) {</p><p style="margin:0px"> <span style="color:rgb(127,0,85);font-weight:bold">try</span> { <span style="color:rgb(63,127,95)">// give 1 and 3 some time to have at least started</span></p><p style="margin:0px"> Thread.<span style="font-style:italic">sleep</span>(100);</p><p style="margin:0px"> } <span style="color:rgb(127,0,85);font-weight:bold">catch</span> (InterruptedException <span style="color:rgb(106,62,62)">e</span>) {</p><p style="margin:0px"> <span style="color:rgb(106,62,62)">interrupted</span>.add(<span style="color:rgb(106,62,62)">n</span>);</p><p style="margin:0px"> }</p><p style="margin:0px"> <span style="color:rgb(127,0,85);font-weight:bold">throw</span> <span style="color:rgb(127,0,85);font-weight:bold">new</span> IllegalArgumentException(String.<span style="font-style:italic">valueOf</span>(<span style="color:rgb(106,62,62)">n</span>));</p><p style="margin:0px"> }</p><p style="margin:0px"> })</p><p style="margin:0px"> .gather(<span style="font-style:italic">mapConcurrently</span>(3, <span style="color:rgb(106,62,62)">n</span> -> {</p><p style="margin:0px"> <span style="color:rgb(106,62,62)">started</span>.add(<span style="color:rgb(106,62,62)">n</span>);</p><p style="margin:0px"> <span style="color:rgb(127,0,85);font-weight:bold">try</span> {</p><p style="margin:0px"> Thread.<span style="font-style:italic">sleep</span>(<span style="color:rgb(106,62,62)">n</span> * 10000);</p><p style="margin:0px"> } <span style="color:rgb(127,0,85);font-weight:bold">catch</span> (InterruptedException <span style="color:rgb(106,62,62)">e</span>) {</p><p style="margin:0px"> <span style="color:rgb(106,62,62)">interrupted</span>.add(<span style="color:rgb(106,62,62)">n</span>);</p><p style="margin:0px"> }</p><p style="margin:0px"> <span style="color:rgb(127,0,85);font-weight:bold">return</span> <span style="color:rgb(106,62,62)">n</span>;</p><p style="margin:0px"> }))</p><p style="margin:0px"> .findAny());</p><p style="margin:0px"> <span style="font-style:italic">assertThat</span>(<span style="color:rgb(106,62,62)">started</span>).containsExactly(10, 1);</p><p style="margin:0px"> <span style="font-style:italic">assertThat</span>(<span style="color:rgb(106,62,62)">interrupted</span>).isEmpty();</p><p style="margin:0px"> <span style="font-style:italic">assertThat</span>(<span style="color:rgb(106,62,62)">thrown</span>).hasMessageThat().isEqualTo(<span style="color:rgb(42,0,255)">"3"</span>);</p><p style="margin:0px"> }</p><p style="margin:0px"><br></p><p style="margin:0px"> <span style="color:rgb(100,100,100)">@Test</span> <span style="color:rgb(127,0,85);font-weight:bold">public</span> <span style="color:rgb(127,0,85);font-weight:bold">void</span> mapConcurrently_downstreamFailurePropagated() {</p><p style="margin:0px"> ConcurrentLinkedQueue<Integer> <span style="color:rgb(106,62,62)">started</span> = <span style="color:rgb(127,0,85);font-weight:bold">new</span> ConcurrentLinkedQueue<>();</p><p style="margin:0px"> ConcurrentLinkedQueue<Integer> <span style="color:rgb(106,62,62)">interrupted</span> = <span style="color:rgb(127,0,85);font-weight:bold">new</span> ConcurrentLinkedQueue<>();</p><p style="margin:0px"> RuntimeException <span style="color:rgb(106,62,62)">thrown</span> = <span style="font-style:italic">assertThrows</span>(</p><p style="margin:0px"> RuntimeException.<span style="color:rgb(127,0,85);font-weight:bold">class</span>,</p><p style="margin:0px"> () -> Stream.<span style="font-style:italic">of</span>(10, 1, 3, 0)</p><p style="margin:0px"> .gather(<span style="font-style:italic">mapConcurrently</span>(3, <span style="color:rgb(106,62,62)">n</span> -> {</p><p style="margin:0px"> <span style="color:rgb(106,62,62)">started</span>.add(<span style="color:rgb(106,62,62)">n</span>);</p><p style="margin:0px"> <span style="color:rgb(127,0,85);font-weight:bold">try</span> {</p><p style="margin:0px"> Thread.<span style="font-style:italic">sleep</span>(<span style="color:rgb(106,62,62)">n</span> * 1000);</p><p style="margin:0px"> } <span style="color:rgb(127,0,85);font-weight:bold">catch</span> (InterruptedException <span style="color:rgb(106,62,62)">e</span>) {</p><p style="margin:0px"> <span style="color:rgb(106,62,62)">interrupted</span>.add(<span style="color:rgb(106,62,62)">n</span>);</p><p style="margin:0px"> }</p><p style="margin:0px"> <span style="color:rgb(127,0,85);font-weight:bold">return</span> <span style="color:rgb(106,62,62)">n</span>;</p><p style="margin:0px"> }))</p><p style="margin:0px"> .peek(<span style="color:rgb(106,62,62)">n</span> -> {</p><p style="margin:0px"> <span style="color:rgb(127,0,85);font-weight:bold">throw</span> <span style="color:rgb(127,0,85);font-weight:bold">new</span> IllegalArgumentException(String.<span style="font-style:italic">valueOf</span>(<span style="color:rgb(106,62,62)">n</span>));</p><p style="margin:0px"> })</p><p style="margin:0px"> .findAny());</p><p style="margin:0px"> <span style="font-style:italic">assertThat</span>(<span style="color:rgb(106,62,62)">started</span>).containsExactly(10, 1, 3);</p><p style="margin:0px"> <span style="font-style:italic">assertThat</span>(<span style="color:rgb(106,62,62)">interrupted</span>).containsExactly(3, 10);</p><p style="margin:0px"> <span style="font-style:italic">assertThat</span>(<span style="text-decoration-line:underline;text-decoration-style:solid;text-decoration-color:rgb(0,102,204);color:rgb(0,102,204)">thrown</span>).hasMessageThat().isEqualTo(<span style="color:rgb(42,0,255)">"1"</span>);</p><p style="margin:0px"> }</p></div></div></div><div><br></div><div><br></div><div>Both with maxConcurrenc=3.</div><div><br></div><div>When the downstream peek() throws on the first element "1" it gets, it will interrupt (and join) the other two pending threads (3 and 10).</div><div>The interrupted ConcurrentLinkedQueue is guaranteed to see [3, 10] because of the happens-before guarantee.</div><div><br></div><div>When the upstream peek() throws on 3, [1, 10] are also already running concurrently. But no thread is interrupted.</div><div><br></div><div><br></div></div><br><div class="gmail_quote gmail_quote_container"><div dir="ltr" class="gmail_attr">On Thu, Jul 3, 2025 at 10:29 AM David Alayachew <<a href="mailto:davidalayachew@gmail.com">davidalayachew@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="auto">These questions necessitate runnable examples. Do you have any</div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Thu, Jul 3, 2025, 10:37 AM Jige Yu <<a href="mailto:yujige@gmail.com" rel="noreferrer" target="_blank">yujige@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr"><p>Hi JDK Core Devs,</p><p>I'm writing to you today with a question about the behavior of <code>mapConcurrent()</code> and its interaction with unchecked exceptions. I've been experimenting with the API and observed that <code>mapConcurrent()</code> blocks and joins all virtual threads upon an unchecked exception before propagating it.</p><p>Initially, I thought this design choice might provide a strong happens-before guarantee. My assumption was that an application catching a <code>RuntimeException</code> would be able to <b>observe all side effects</b> 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.</p><p>However, I've since realized that <code>mapConcurrent()</code> cannot fully guarantee a strong happens-before relationship when an unchecked exception occurs <i>somewhere</i> 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 <b>thrown by an upstream</b> source.</p><p>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 <i>before</i> reaching the <code>gather()</code> call, the virtual thread initiated by the first element would not be interrupted. This makes the "happens-before" guarantee quite nuanced in practice.</p><p>This brings me to my core questions:</p><ol start="1"><li><p>Is providing a happens-before guarantee upon an unchecked exception a design goal for <code>mapConcurrent()</code>?</p></li><li><p>If not, would it be more desirable to <i>not</i> 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.</p></li></ol><p>Thank you for your time and insights.</p><p>Best regards,</p><p>Ben Yu</p></div>
</blockquote></div>
</blockquote></div>