You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@commons.apache.org by "Gary D. Gregory (Jira)" <ji...@apache.org> on 2026/02/07 20:00:00 UTC

[jira] [Comment Edited] (LANG-1817) UncheckedFutureImpl clears thread interrupt status when wrapping InterruptedException

    [ https://issues.apache.org/jira/browse/LANG-1817?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18057099#comment-18057099 ] 

Gary D. Gregory edited comment on LANG-1817 at 2/7/26 7:59 PM:
---------------------------------------------------------------

Hello [~iponomarev] 

Thank you for your update.

Since the changes to {{BackgroundInitializer}} are not tested, I didn't apply that part of the patch. Otherwise, the rest is in git master and 3.21.0-SNAPSHOT [builds|https://repository.apache.org/content/repositories/snapshots/].


was (Author: garydgregory):
Hello [~iponomarev] 

Thank you for your update.

Since the changes to `BackgroundInitializer` are not tested, I didn't apply that part of the patch. Otherwise, the rest is in git master and 3.21.0-SNAPSHOT [builds](https://repository.apache.org/content/repositories/snapshots/).

> UncheckedFutureImpl clears thread interrupt status when wrapping InterruptedException
> -------------------------------------------------------------------------------------
>
>                 Key: LANG-1817
>                 URL: https://issues.apache.org/jira/browse/LANG-1817
>             Project: Commons Lang
>          Issue Type: Bug
>            Reporter: Ivan Ponomarev
>            Priority: Major
>             Fix For: 3.21.0
>
>
> h3. Problem
> {{UncheckedFutureImpl}} {{.get()}} and {{.get(long, TimeUnit)}} catch {{InterruptedException}} and rethrow it as {{{}UncheckedInterruptedException{}}}, but do not restore the thread’s interrupt status.
> Catching InterruptedException clears the {{interrupt}} flag. As a result, callers observing {{Thread.currentThread().isInterrupted()}} after calling {{UncheckedFuture.get()}} may incorrectly see {{{}false{}}}, even though an interrupt was delivered.
> For example,
> {code:java}
> class Worker implements Runnable {
>     private final UncheckedFuture<?> future;
>     Worker(UncheckedFuture<?> future) {
>         this.future = future;
>     }
>     @Override
>     public void run() {
>         while (!Thread.currentThread().isInterrupted()) {
>             // Block waiting for work or result
>             try {
>                 future.get();   // <-- wrapped Future
>                 doWork();
>           } catch (RuntimeException e) {
>               log.warn("Transient failure", e);
>               // continue loop
>           }
>         }
>     }
> }
> //Shutdown logic
> executor.shutdownNow();  // interrupts worker threads
> {code}
>  * Thread is interrupted.
>  * {{Future.get()}} throws {{{}InterruptedException{}}}.
>  * {{UncheckedFutureImpl.get()}} catches it and clears the {{{}interrupt{}}}. It throws {{{}UncheckedInterruptedException{}}}.
>  * Caller catches or ignores the unchecked exception.
>  * Thread interrupt flag is now false.
>  * {{while (!Thread.currentThread().isInterrupted())}} continues running.
>  * {*}Even if the exception is not caught{*}, and the task returns a thread to the pool as non-interrupted, then any higher-level cancellation logic that relies on the flag won’t see it.
> h3. Proposed fix
> When wrapping {{{}InterruptedException{}}}, the interrupt status should be preserved:
> {code:java}
> catch (InterruptedException e) {
>     Thread.currentThread().interrupt();
>     throw new UncheckedInterruptedException(e);
> }
> {code}
>  
> This matches standard Java concurrency best practices.
> See Section 7.1 of "Java Concurrency in Practice" (Brian Goetz et al):
> {quote}If [the task] is not simply going to propagate
> {{InterruptedException}} to its caller, it should restore the interruption status after
> catching {{InterruptedException}} using {{Thread.currentThread().interrupt();}}
> {quote}
> See also [code-review-checklists/java-concurrency/#IF.1|https://github.com/code-review-checklists/java-concurrency?tab=readme-ov-file#restore-interruption]
> {quote}If some code propagates InterruptedException wrapped into another exception (e. g. RuntimeException), is the interruption status of the current thread restored before the wrapping exception is thrown?
> {quote}
> Proposed fix: https://github.com/apache/commons-lang/pull/1590



--
This message was sent by Atlassian Jira
(v8.20.10#820010)