Thanks Viktor! I didn't realize I wasn't explicit about the "leave the interruption bit set" part.
So no, if mapConcurrent() throws unchecked upon interruption, the interruption bit must be set. There is no question about that, from my part. :) With that, do you still think throwing unchecked (with interruption bit set) is unsatisfactory? On Mon, Jun 2, 2025 at 9:37 AM Viktor Klang <viktor.kl...@oracle.com> wrote: > I talked to Doug about this today, and it doesn't seem tractable to throw > a non InterruptedException on interrupt (at least not without leaving the > interruption flag set). > > While it should be possible to broadcast stream-evaluation-thread's > interruption to tasks (*without* cancelling them) so that tasks can check > Thread.interrupted() and throw some application-specific exception, it is > still unsatisfactory for most use-cases. > > Cheers, > √ > > > *Viktor Klang* > Software Architect, Java Platform Group > Oracle > ------------------------------ > *From:* core-libs-dev <core-libs-dev-r...@openjdk.org> on behalf of Jige > Yu <yuj...@gmail.com> > *Sent:* Friday, 30 May 2025 00:44 > *To:* David Holmes <david.hol...@oracle.com> > *Cc:* core-libs-dev@openjdk.org <core-libs-dev@openjdk.org> > *Subject:* Re: [External] : Re: mapConcurrent() with InterruptedException > > Yeah. I can see these distinctions and guidelines being useful for callers > of the low-level concurrency primitive methods (semaphore etc.) > > But at a higher level, in the application code, does anyone know of a > plausible scenario where being forced to catch IE is useful? It seems like > you either always propagate it up, or you have to catch it and risk > forgetting to re-interrupt the thread. And, *there is not much useful > thing you can do* that's different from other types of exceptions. > > That's why I think it may be reasonable for mapConcurrent() to: > > - absorb cancellation into the API contract and catch the IE > and throw a more semantically specific exception (an unchecked exception > indicating the mapConcurrent() has been interrupted). > > Thinking from two typical ways of IE scenarios: > > - mapConcurrent() is wrapped in a Future, and the future is canceled - > if you've canceled it, the unchecked exception shouldn't bother you. > - mapConcurrent() is inside an outer mapConcurrent(), or inside an > outer structured concurrency scope - in either case, interruption happens > because a sibling task has failed to trigger the fail-fast cancellation. > Checked exceptions work fine too. > > I can't see harm being done for either case, compared to letting it keep > running through all elements (what if the stream is infinite or very very > long?) > > On Wed, May 28, 2025 at 10:19 PM David Holmes <david.hol...@oracle.com> > wrote: > > On 29/05/2025 3:45 am, Jige Yu wrote: > > 17.2.4 is specifically about the Object wait()/notify() API, yeah? > > > > I've always had this confusion in my mind that I couldn't find an > > authoritative source for: what was the point of checked > > InterruptedException? > > Interruption and IE provide a limited capability for task cancellation. > As such any method that calls a method that can throw IE has to be aware > that the method can do that, and by having IE be checked, they are > forced to confront the question: how do I respond to cancellation? As > Doug Lea describes in his book there are a few ways to respond to this > cancellation notice depending on the semantics of the method involved: > > - make cancellation part of your own API, declare and rethrow any IE > should encounter (and your caller then has to make the choice) > - absorb cancellation into your general API contract and catch the IE > and throw a more semantically specific exception > - handle low-level cancellation by catching the IE and doing whatever > logic is appropriate > > David > ----- > > > My naive observation has been two fold: > > > > 1. In most cases, when possible, applications declare "throws IE" and > > let it propagate all the way up the call stack. This works exactly > > the same as if IE were unchecked. > > 2. When the application is cornered into a Runnable, Function and > > cannot "throws IE", it has to catch(IE), remember to re-interrupt > > the thread, and wrap in an unchecked. Except *it often forgets to > > re-interrupt*. > > > > At the lowest level concurrent primitives (wait(), notify(), Semaphore), > > I presume IE being an integral part of the API contract. > > > > But at mid-level APIs (Future, mapConcurrent()), or high-level > > applications, I've always been at a loss on why we needed the checked IE > > in the first place. > > > > IE used to happen when someone calls Future.cancel(true). Now with > > structured concurrency, it can also be caused by a sibling task failing > > and fail-fast. > > > > But aside from the extra ceremony and the extra room for error > > (forgetting to reinterrupt), what do we get from it? I was asking > > ChatGPT about real-life scenarios where specifically catching and > > handling IE is necessary but all ChatGPT gave me was cases where the > > code has to re-interrupt, which would have been unnecessary if IE were > > unchecked and didn't clear the interruption bit. > > > > Would appreciate a pointer to code or authoritative discussion about > > this matter. > > > > Sorry for the tangent! > > > > > > On Wed, May 28, 2025 at 1:03 AM Viktor Klang <viktor.kl...@oracle.com > > <mailto:viktor.kl...@oracle.com>> wrote: > > > > It would seem to require a change to the Java Language Specification > > section 17.2.4 > > > > Cheers, > > √ > > > > * > > * > > *Viktor Klang* > > Software Architect, Java Platform Group > > Oracle > > > ------------------------------------------------------------------------ > > *From:* Jige Yu <yuj...@gmail.com <mailto:yuj...@gmail.com>> > > *Sent:* Wednesday, 28 May 2025 00:35 > > *To:* Viktor Klang <viktor.kl...@oracle.com > > <mailto:viktor.kl...@oracle.com>> > > *Cc:* core-libs-dev@openjdk.org <mailto:core-libs-dev@openjdk.org> > > <core-libs-dev@openjdk.org <mailto:core-libs-dev@openjdk.org>> > > *Subject:* Re: [External] : Re: mapConcurrent() with > > InterruptedException > > That said, I'm still curious. Have you considered propagating > > cancellation, re-interrupting the current thread, and then throwing > > an unchecked exception upon interruption? It's going to be the most > > fail-fast-compatible. > > > > catch (IE interrupted) { > > cancel all pending futures > > Thread.currentThread().interrupt(); > > throw new UncheckedInterruptedException(); > > } > > > > What are the concerns against such practice? > > > > > > > > > > > > On Mon, May 26, 2025 at 8:31 AM Jige Yu <yuj...@gmail.com > > <mailto:yuj...@gmail.com>> wrote: > > > > Yeah, I think I missed the task cancellation and the joining > part. > > > > When the mapConcurrent() thread is interrupted, it propagates > > the cancellation to the child threads. > > > > And if any of the children threads (running the user-provided > > Function) receives that cancellation, it will still need to > > catch it and handle interruption. > > > > That means the interruption isn't swallowed. If the user code > > wants to abort, they can always just throw an unchecked > > exception and catch it from the caller of mapConcurrent(). > > > > That should do for most of the IO type work. > > > > This makes sense. > > > > > > > > On Mon, May 26, 2025 at 12:30 AM Viktor Klang > > <viktor.kl...@oracle.com <mailto:viktor.kl...@oracle.com>> > wrote: > > > > Yes, the updated Gatherers.mapConcurrent will continue under > > interruption until done, and then restore the interrupt > > status of the calling thread. > > > > It cannot (reasonably) do anything else—if it were to throw > > InterruptedException, it couldn't since that is a checked > > exception and there's no tracking of checked exceptions > > throughout a Stream, and that is the specified behavior to > > handle thread interrupts (i.e. clear flag and throw > > InterruptedException, which is not possible in this case; > > or, make sure that interrupt status is maintained, which is > > possible in this case. > > > > I have given it some more thought in the meantime, but I > > haven't arrived in something more satisfactory than that, > yet. > > > > Cheers, > > √ > > > > * > > * > > *Viktor Klang* > > Software Architect, Java Platform Group > > Oracle > > > ------------------------------------------------------------------------ > > *From:* Jige Yu <yuj...@gmail.com <mailto:yuj...@gmail.com>> > > *Sent:* Monday, 26 May 2025 03:07 > > *To:* Viktor Klang <viktor.kl...@oracle.com > > <mailto:viktor.kl...@oracle.com>> > > *Cc:* core-libs-dev@openjdk.org <mailto:core-libs- > > d...@openjdk.org> <core-libs-dev@openjdk.org <mailto:core- > > libs-...@openjdk.org>> > > *Subject:* Re: [External] : Re: mapConcurrent() with > > InterruptedException > > Thanks Viktor! > > > > Sorry, I was giving myself more time to read the code, but > > then I lost track. > > > > If I'm reading the code right, the current behavior is that > > if the current thread is interrupted, the mapConcurrent() > > would suppress the interruption until all elements are > > processed? (It will then restore the interruption bit) > > > > Doesn't this break fail fast? > > > > For example, I may have a mapConcurrent() called from within > > structured concurrency, and that fans out to two operations: > > † > > scope.fork(() -> doA()); > > scope.fork(() -> doB()); // calls mapConcurrent() internally > > scope.join().throwIfFailed(); > > > > If doA() fails, doB() will be cancelled (thread interrupted). > > > > If mapConcurrent() ignores the interruption, and if doB has > > a long list to process, it'll continue to consume system > > resources even when the caller no longer needs the results, > no? > > > > > > On Fri, Feb 7, 2025 at 2:16 AM Viktor Klang > > <viktor.kl...@oracle.com <mailto:viktor.kl...@oracle.com>> > > wrote: > > > > >Sorry, did the PR stop using Semaphore? > > > > No, not that PR. See: https://github.com/openjdk/jdk/ > > commit/450636ae28b84ded083b6861c6cba85fbf87e16e > > <https://urldefense.com/v3/__https://github.com/openjdk/ > > jdk/commit/450636ae28b84ded083b6861c6cba85fbf87e16e__;!! > > ACWV5N9M2RV99hQ! > > OKfWrvJEDCkiHcT5b4a6gfeomZzZeq3jv6ir2vf2I5WWIhujtb- > > by_VuQFaAPDIuAA0qAwpTUjllXMPF$> > > > > The problem with interruption under parallel evaluation > > is that there is no general support for propagation of > > interruption in CountedCompleters. Adding support for > > such in (at least) GathererOp needs further study before > > contemplating making any changes to mapConcurrent()'s > > interruption policy. > > > > Cheers, > > √ > > > > * > > * > > *Viktor Klang* > > Software Architect, Java Platform Group > > Oracle > > > > > ------------------------------------------------------------------------ > > *From:* Jige Yu <yuj...@gmail.com <mailto: > yuj...@gmail.com>> > > *Sent:* Thursday, 6 February 2025 17:04 > > *To:* Viktor Klang <viktor.kl...@oracle.com > > <mailto:viktor.kl...@oracle.com>> > > *Cc:* core-libs-dev@openjdk.org <mailto:core-libs- > > d...@openjdk.org> <core-libs-dev@openjdk.org > > <mailto:core-libs-dev@openjdk.org>> > > *Subject:* Re: [External] : Re: mapConcurrent() with > > InterruptedException > > Sorry, did the PR stop using Semaphore? > > > > I had naively thought that mapConcurrent() will keep a > > buffer of Future of all currently-running concurrent > > tasks (it can be a ConcurrentMap<TaskKey, Future> if we > > don't have to ensure FIFO). > > > > Upon interruption, the main thread can > > call .cancel(true) on all pending Futures; optionally > > join with the VTs (if we need to block until all VTs > > exit); then propagate exception. > > > > Upon completion, each task just removes itself from the > > ConcurrentMap. > > > > Just in case it adds anything. > > > > > > > > On Thu, Feb 6, 2025 at 6:47 AM Viktor Klang > > <viktor.kl...@oracle.com > > <mailto:viktor.kl...@oracle.com>> wrote: > > > > After some more investigation it seems tractable to > > propagate interruption of the caller in sequential > > mode, but parallel mode will require much bigger > > considerations. > > > > I made a comment to that effect on the JBS issue: > > https://bugs.openjdk.org/browse/JDK-8349462? > > > > focusedId=14750017&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-14750017 > < > https://bugs.openjdk.org/browse/JDK-8349462?focusedId=14750017&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-14750017 > > > > > > > > Cheers, > > √ > > > > * > > * > > *Viktor Klang* > > Software Architect, Java Platform Group > > Oracle > > > ------------------------------------------------------------------------ > > *From:* Viktor Klang <viktor.kl...@oracle.com > > <mailto:viktor.kl...@oracle.com>> > > *Sent:* Thursday, 6 February 2025 11:51 > > *To:* Jige Yu <yuj...@gmail.com > > <mailto:yuj...@gmail.com>> > > *Cc:* core-libs-dev@openjdk.org <mailto:core-libs- > > d...@openjdk.org> <core-libs-dev@openjdk.org > > <mailto:core-libs-dev@openjdk.org>> > > *Subject:* Re: [External] : Re: mapConcurrent() with > > InterruptedException > > I think alignment in behavior between parallel > > Stream and mapConcurrent in terms of how > > interruptions are handled is a possible path forward. > > > > I decided to close the PR for now as I realized my > > parallel Stream example had misled me regarding its > > exception throwing, so I'll need to go back and > > refine the solution. > > > > It still seems solvable though. > > > > Cheers, > > √ > > > > * > > * > > *Viktor Klang* > > Software Architect, Java Platform Group > > Oracle > > > ------------------------------------------------------------------------ > > *From:* Jige Yu <yuj...@gmail.com > > <mailto:yuj...@gmail.com>> > > *Sent:* Wednesday, 5 February 2025 19:20 > > *To:* Viktor Klang <viktor.kl...@oracle.com > > <mailto:viktor.kl...@oracle.com>> > > *Cc:* core-libs-dev@openjdk.org <mailto:core-libs- > > d...@openjdk.org> <core-libs-dev@openjdk.org > > <mailto:core-libs-dev@openjdk.org>> > > *Subject:* Re: [External] : Re: mapConcurrent() with > > InterruptedException > > Oh good call! > > > > I forgot to check what parallel streams do upon > > interruption (didn't think they do any blocking > > calls, but at least the main thread must block). > > > > On Wed, Feb 5, 2025 at 8:18 AM Viktor Klang > > <viktor.kl...@oracle.com > > <mailto:viktor.kl...@oracle.com>> wrote: > > > > Hi Jige, > > > > I opened an issue to track the concern, and I > > have proposed a change which seems to align well > > with how parallel streams behave under caller > > thread interruption. > > > > I've opened the following PR for review: > > https://github.com/openjdk/jdk/pull/23467 > > <https://urldefense.com/v3/__https://github.com/ > > openjdk/jdk/pull/23467__;!!ACWV5N9M2RV99hQ! > > > > M4nB0WxHU_1jcbZoNGy3DD81Oefr7BuObJ-7FHb_mw51HqMBI0BjFy97H6GnjZaX402UQZK89E_mLvMu$> > > > > If you are able to make a local OpenJDK build > > with that solution you could check if it > > addresses your use-cases (or not). > > <https://urldefense.com/v3/__https://github.com/ > > openjdk/jdk/pull/23467__;!!ACWV5N9M2RV99hQ! > > > > M4nB0WxHU_1jcbZoNGy3DD81Oefr7BuObJ-7FHb_mw51HqMBI0BjFy97H6GnjZaX402UQZK89E_mLvMu$> > > > > 8349462: Gatherers.mapConcurrent could support > > async interrupts by viktorklang-ora · Pull > > Request #23467 · openjdk/jdk <https:// > > urldefense.com/v3/__https://github.com/openjdk/ > > jdk/pull/23467__;!!ACWV5N9M2RV99hQ! > > > > M4nB0WxHU_1jcbZoNGy3DD81Oefr7BuObJ-7FHb_mw51HqMBI0BjFy97H6GnjZaX402UQZK89E_mLvMu$> > > This change is likely going to need some extra > > verbiage in the spec for mapConcurrent, and thus > > a CSR. This behavior aligns mapConcurrent with > > how parallel streams work in conjunction with > > interrup... > > github.com <https://urldefense.com/v3/__http:// > > github.com__;!!ACWV5N9M2RV99hQ! > > > > M4nB0WxHU_1jcbZoNGy3DD81Oefr7BuObJ-7FHb_mw51HqMBI0BjFy97H6GnjZaX402UQZK89DBkMefT$> > > > > > > > > Cheers, > > √ > > > > * > > * > > *Viktor Klang* > > Software Architect, Java Platform Group > > Oracle > > > ------------------------------------------------------------------------ > > *From:* Jige Yu <yuj...@gmail.com > > <mailto:yuj...@gmail.com>> > > *Sent:* Wednesday, 5 February 2025 16:24 > > *To:* Viktor Klang <viktor.kl...@oracle.com > > <mailto:viktor.kl...@oracle.com>> > > *Cc:* core-libs-dev@openjdk.org <mailto:core- > > libs-...@openjdk.org> <core-libs-dev@openjdk.org > > <mailto:core-libs-dev@openjdk.org>> > > *Subject:* Re: [External] : Re: mapConcurrent() > > with InterruptedException > > Thanks Viktor! > > > > I understand the problem. > > > > The main reason I asked is because I want to > > understand how the core Java team thinks of > > throwing an unchecked exception. > > > > As explained above, I consider losing > > cancellability a big deal, a deal breaker even. > > And I thought throwing unchecked is more > > acceptable. Because the most common reason the > > mapConcurrent() VT can be interrupted is due to > > cancellation from a parent mapConcurrent(), or a > > parent Structured Concurrency scope. The > > cancellation could be either from an organic > > exception, or from the downstream not needing > > more elements, like maybe due to findFirst() > > already getting an element. > > > > In both cases, since the concurrent operation is > > already cancelled (result ignored), what > > exception pops up to the top level isn't that > > big of a deal (perhaps only a log record will be > > seen?) > > > > But if the core Java team considers it a bad > > idea, I would love to learn and adjust. > > > > On Tue, Feb 4, 2025 at 4:41 AM Viktor Klang > > <viktor.kl...@oracle.com > > <mailto:viktor.kl...@oracle.com>> wrote: > > > > Hi, > > > > The problem is that mapConcurrent cannot > > throw InterruptedException because that is a > > checked exception, so we cannot clear the > > interrupted flag and throw that exception. > > > > So the updated semantics is to not cut the > > stream short but instead run to completion, > > restoring the interruption flag. > > > > There exists a couple of alternatives to > > this approach which I am contemplating, but > > they need to be further explored before I > > consider moving forward with any of them. > > > > Cheers, > > √ > > > > * > > * > > *Viktor Klang* > > Software Architect, Java Platform Group > > Oracle > > > ------------------------------------------------------------------------ > > *From:* Jige Yu <yuj...@gmail.com > > <mailto:yuj...@gmail.com>> > > *Sent:* Monday, 27 January 2025 17:00 > > *To:* Viktor Klang <viktor.kl...@oracle.com > > <mailto:viktor.kl...@oracle.com>> > > *Cc:* core-libs-dev@openjdk.org > > <mailto:core-libs-dev@openjdk.org> <core- > > libs-...@openjdk.org <mailto:core-libs- > > d...@openjdk.org>> > > *Subject:* Re: [External] : Re: > > mapConcurrent() with InterruptedException > > Thanks Viktor! > > > > It looks like the current fix ignores > > interruption. > > > > I want to make sure my concern of it > > defeating cancellation is heard and > understood. > > > > The scenarios I worry about is for a > > mapConcurrent() that fans out to another > > method call, which internally calls > > mapConcurrent() as implementation detail. > > > > An example: > > > > List<RefundResponse> > > refundHelper(transaction) > > { transaction.creditCardAccounts.stream() > > .gather(mapConcurrent(acct -> > > service.refund(acct)) .toList(); } > > transactions.stream() > > .gather(mapConcurrent(transaction -> > > refundHelper(transaction)); > > > > > > It seems undesirable that in such a case all > > the service.refund() calls become non > > cancellable, because the only way the outer > > mapConcurrent() cancels the refundHelper() > > calls is through Thread.interrupt() the > > virtual threads that call refundHelper(), > > which would then be disabled by the inner > > mapConcurrent(). > > > > Does this example make sense to you? I can > > further explain if anything isn't clear. > > > > But I want to make sure the decision to > > disable interruption is deliberate judgement > > call that such nested mapConcurrent() is > > unlikely,or not important. > > > > Cheers, > > > > > > > > On Mon, Jan 27, 2025 at 6:11 AM Viktor Klang > > <viktor.kl...@oracle.com > > <mailto:viktor.kl...@oracle.com>> wrote: > > > > Hi! > > > > Please see: https://github.com/openjdk/ > > jdk/pull/23100 <https://urldefense.com/ > > v3/__https://github.com/openjdk/jdk/ > > pull/23100__;!!ACWV5N9M2RV99hQ! > > > > IzQs0G26M7ZGPwJ3YJpCcS0gxi6BjqoBux2T5u0cHud_zb_mHLfiIrASSZiP0ynNgnaAuwuOh__WinK8$> > > > > Cheers, > > √ > > > > * > > * > > *Viktor Klang* > > Software Architect, Java Platform Group > > Oracle > > > ------------------------------------------------------------------------ > > *From:* Jige Yu <yuj...@gmail.com > > <mailto:yuj...@gmail.com>> > > *Sent:* Sunday, 26 January 2025 23:03 > > *To:* Viktor Klang > > <viktor.kl...@oracle.com > > <mailto:viktor.kl...@oracle.com>> > > *Cc:* core-libs-dev@openjdk.org > > <mailto:core-libs- > > d...@openjdk.org> <core-libs- > > d...@openjdk.org <mailto:core-libs- > > d...@openjdk.org>> > > *Subject:* [External] : Re: > > mapConcurrent() with InterruptedException > > Checking in on what you've found out, > > Viktor. > > > > From where we left off, I understand > > that you were looking at alternatives > > instead of silent truncation? > > > > Have you reached any conclusion? > > > > We touched on disallowing interruption > > during mapConcurrent(). I still have > > concerns with disabling cancellation, > > because it basically undoes this API > > note from the javadoc <https:// > > cr.openjdk.org/~alanb/sc-20240503/ > > java.base/java/util/stream/ > > > Gatherers.html#mapConcurrent(int,java.util.function.Function)>: > > > > API Note: > > > > In progress tasks will be attempted > > to be cancelled, on a best-effort > > basis, in situations where the > > downstream no longer wants to > > receive any more elements. > > > > In reality, people will use > > mapConcurrent() to fan out rpcs. > > Sometimes these rpcs are just a single > > blocking call; yet sometimes they may > > themselves be a Structured Concurrency > > scope, with 2 or 3 rpcs that constitute > > a single logical operation. Under two > > conditions, cancellation is imho > > important semantic: > > > > 1. The downstream code uses > > filter().findFirst(), and when it > > sees an element, it will return and > > no longer needs the other pending > > rpcs to complete. If cancellation is > > disabled, these unnecessary rpcs > > will waste system resources. > > 2. One of the rpc throws and the Stream > > pipeline needs to propagate the > > exception. Again, if the other rpcs > > cannot be cancelled, we'll have many > > zombie rpcs. > > > > Zombie rpcs may or may not be a deal > > breaker, depending on the specific use > > case. But for a JDK library, losing > > cancellation would have a negative > > impact on usability. > > > > My 2c, > > > > > > On Fri, Jan 3, 2025 at 9:18 AM Viktor > > Klang <viktor.kl...@oracle.com > > <mailto:viktor.kl...@oracle.com>> wrote: > > > > Hi Ben, > > > > Thanks for raising these questions— > > getting feedback is crucial in the > > Preview stage of features. > > > > I wrote a reply to the Reddit thread > > so I'll just summarize here: > > > > It is important to note that / > > mapConcurrent()/ is not a part of > > the Structured Concurrency JEPs, so > > it is not designed to join SC scopes. > > > > I'm currently experimenting with > > ignoring-but-restoring interrupts on > > the "calling thread" for / > > mapConcurrent()/, as well as capping > > work-in-progress to / > > maxConcurrency/ (not only capping > > the concurrency but also the amount > > of completed-but-yet-to-be-pushed > > work). Both of these adjustments > > should increase predictability of > > behavior in the face of blocking > > operations with variable delays. > > > > Another adjustment I'm looking at > > right now is to harden/improve the > > cleanup to wait for concurrent tasks > > to acknowledge cancellation, so that > > once the finisher is done executing > > the VTs are known to have terminated. > > > > As for not preserving the encounter > > order, that would be a completely > > different thing, and I'd encourage > > you to experiment with that if that > > functionality would be interesting > > for your use-case(s). > > > > Again, thanks for your feedback! > > > > 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:* Friday, 3 January 2025 17:53 > > *To:* core-libs-dev@openjdk.org > > <mailto:core-libs- > > d...@openjdk.org> <core-libs- > > d...@openjdk.org <mailto:core-libs- > > d...@openjdk.org>> > > *Subject:* mapConcurrent() with > > InterruptedException > > Hi Java Experts, > > > > I sent this email incorrectly to > > loom-dev@ and was told on Reddit > > that core-libs-dev is the right list. > > > > The question is about the behavior > > of mapConcurrent() when the thread > > is interrupted. > > > > Currently mapConcurrent()'s finisher > > phase will re-interrupt the thread, > > then stop at whatever element that > > has already been processed and > return. > > > > This strikes me as a surprising > > behavior, because for example if I'm > > running: > > > > Stream.of(1, 2, 3) > > .gather(mapConcurrent(i -> > > i * 2)) > > .toList() > > > > and the thread is being interrupted, > > the result could be any of [2], [2, > > 4] or [2, 4, 6]. > > > > Since thread interruption is > > cooperative, there is no guarantee > > that the thread being interrupted > > will just abort. It's quite possible > > that it'll keep going and then will > > use for example [2] as the result of > > doubling the list of [1, 2, 3], > > which is imho incorrect. > > > > In the Reddit <https:// > > urldefense.com/v3/__https:// > > www.reddit.com/r/java/ > > comments/1hr8xyu/ > > > > observations_of_gatherersmapconcurrent/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button__;!!ACWV5N9M2RV99hQ!L1LHRE2pnYPg43nM0J0dCoV4agscV_rybIV9jY97xn9XJi9d7VoPma4jhx4J4GBeoeQmVud8M4PjPb7L$> > thread, > someone argued that interruption rarely happens so it's more of a > theoretical issue. But interruption can easily happen in Structured > Concurrency or in mapConcurrent() itself if any subtask has failed in order > to cancel/interrupt the other ongoing tasks. > > > > There had been discussion about > > alternative strategies: > > > > 1. Don't respond to interruption > > and just keep running to > completion. > > 2. Re-interrupt thread and wrap the > > InterruptedException in a > > standard unchecked exception > > > (StructuredConcurrencyInterruptedException?). > > > > > > I have concerns with option 1 > > because it disables cancellation > > propagation when mapConcurrent() > > itself is used in a subtask of a > > parent mapConcurrent() or in a > > StructuredConcurrencyScope. > > > > Both equivalent Future-composition > > async code, or C++'s fiber trees > > support cancellation propagation and > > imho it's a critical feature or else > > it's possible that a zombie thread > > is still sending RPCs long after the > > main thread has exited (failed, or > > falled back to some default action). > > > > My arguments for option 2: > > > > 1. InterruptedException is more > > error prone than traditional > > checked exceptions for > > *users* to catch and handle. > > They can forget to re-interrupt > > the thread. It's so confusing > > that even seasoned programmers > > may not know they are *supposed > > to* re-interrupt the thread. > > 2. With Stream API using functional > > interfaces like Supplier, > > Function, the option of just > > tacking on "throws IE" isn't > > available to many users. > > 3. With Virtual Threads, it will be > > more acceptable, or even become > > common to do blocking calls from > > a stream operation (including > > but exclusive to > > mapConcurrent()). So the chance > > users are forced to deal with IE > > will become substantially higher. > > 4. Other APIs such as the > > Structured Concurrency API have > > already started wrapping system > > checked exceptions like > > ExecutionException, > > TimeoutException in unchecked > > exceptions ( join() <https:// > > urldefense.com/v3/__https:// > > download.java.net/java/ > > early_access/loom/docs/api/ > > java.base/java/util/concurrent/ > > > > StructuredTaskScope.html*join()__;Iw!!ACWV5N9M2RV99hQ!L1LHRE2pnYPg43nM0J0dCoV4agscV_rybIV9jY97xn9XJi9d7VoPma4jhx4J4GBeoeQmVud8MxGG4HzA$> > for > example). > > 5. Imho, exceptions that we'd > > rather users not catch and > > handle but instead should mostly > > just propagate up as is, should > > be unchecked. > > > > There is also a side discussion > > <https://urldefense.com/v3/ > > __https://www.reddit.com/r/java/ > > comments/1hr8xyu/comment/m4z4f8c/? > > > > utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button__;!!ACWV5N9M2RV99hQ!L1LHRE2pnYPg43nM0J0dCoV4agscV_rybIV9jY97xn9XJi9d7VoPma4jhx4J4GBeoeQmVud8MyZYl02k$> > about > whether mapConcurrent() is better off preserving input order or push to > downstream as soon as an element is computed. I'd love to discuss that > topic too but maybe it's better to start a separate thread? > > > > Thank you and cheers! > > > > Ben Yu > > > >