CompletableFuture.get() swallows thread interrupt flag on Java 11

classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

CompletableFuture.get() swallows thread interrupt flag on Java 11

JSR166 Concurrency mailing list
Hello everyone,

finally I am using more of the Java 8 concurrency code (yay!) and found
something strange.

Here is the test code:

     Executor executor = Executors.newCachedThreadPool();
     Thread t = new Thread(() -> {
       while (!Thread.interrupted()) {
         CompletableFuture<Void> future = new CompletableFuture<>();
         executor.execute(() -> future.complete(null));
         try {
           future.get();
         } catch (InterruptedException e) {
           return;
         } catch (ExecutionException e) {
           e.printStackTrace();
         }
       }
     });
     t.start();
     Thread.sleep(1000);
     t.interrupt();
     t.join();

This is supposed to terminate after 1s.

Java version which does not terminate:
openjdk version "11.0.8" 2020-07-14
OpenJDK Runtime Environment (build 11.0.8+10-post-Ubuntu-0ubuntu120.04)
OpenJDK 64-Bit Server VM (build 11.0.8+10-post-Ubuntu-0ubuntu120.04,
mixed mode, sharing)

Java version which does terminate:
java version "1.8.0_162"
Java(TM) SE Runtime Environment (build 1.8.0_162-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.162-b12, mixed mode)

Debugging into the CompletableFuture.get() in Java 11 I can find a code
path that resets the interrupt flag but neither restores it or throws
the InterruptedException.

Can "someone" give that a closer look?

Warm regards,

Jens
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: CompletableFuture.get() swallows thread interrupt flag on Java 11

JSR166 Concurrency mailing list
Does it reproduce if you remove the line that completes the future? 

On Thu, 15 Oct 2020, 04:36 Jens Wilke via Concurrency-interest, <[hidden email]> wrote:
Hello everyone,

finally I am using more of the Java 8 concurrency code (yay!) and found
something strange.

Here is the test code:

     Executor executor = Executors.newCachedThreadPool();
     Thread t = new Thread(() -> {
       while (!Thread.interrupted()) {
         CompletableFuture<Void> future = new CompletableFuture<>();
         executor.execute(() -> future.complete(null));
         try {
           future.get();
         } catch (InterruptedException e) {
           return;
         } catch (ExecutionException e) {
           e.printStackTrace();
         }
       }
     });
     t.start();
     Thread.sleep(1000);
     t.interrupt();
     t.join();

This is supposed to terminate after 1s.

Java version which does not terminate:
openjdk version "11.0.8" 2020-07-14
OpenJDK Runtime Environment (build 11.0.8+10-post-Ubuntu-0ubuntu120.04)
OpenJDK 64-Bit Server VM (build 11.0.8+10-post-Ubuntu-0ubuntu120.04,
mixed mode, sharing)

Java version which does terminate:
java version "1.8.0_162"
Java(TM) SE Runtime Environment (build 1.8.0_162-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.162-b12, mixed mode)

Debugging into the CompletableFuture.get() in Java 11 I can find a code
path that resets the interrupt flag but neither restores it or throws
the InterruptedException.

Can "someone" give that a closer look?

Warm regards,

Jens
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: CompletableFuture.get() swallows thread interrupt flag on Java 11

JSR166 Concurrency mailing list
No. Interruption works.

The problem is triggered when interruption and completion happen at the
same time.

On 15/10/20 4:23 pm, Alex Otenko wrote:
> Does it reproduce if you remove the line that completes the future?
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: CompletableFuture.get() swallows thread interrupt flag on Java 11

JSR166 Concurrency mailing list
I think I can see how this happens. At the end of waitingGet we may have been interrupted and already caught the IE, but because we are interruptible we don't re-assert the interrupt status. If we return null that indicates reportGet should throw IE. But before we return from waitingGet we reload from "result" which may now be non-null, so we return non-null and hence never rethrow the IE.

There needs to be a further check of q.interrupted and a re-assertion of the interrupt state, but I'm not certain of the exact state that needs to be checked. Possibly:

If (r != null || (r = result) != null) {
  postComplete();
 + if (q != null && q.interrupted && interruptible)
 +   Thread.currentThread().interrupt();
}

Cheers,
David

-----Original Message-----
From: Concurrency-interest <[hidden email]> On Behalf Of Jens Wilke via Concurrency-interest
Sent: Thursday, 15 October 2020 5:02 PM
To: Alex Otenko <[hidden email]>
Cc: concurrency-interest <[hidden email]>
Subject: Re: [concurrency-interest] CompletableFuture.get() swallows thread interrupt flag on Java 11

No. Interruption works.

The problem is triggered when interruption and completion happen at the
same time.

On 15/10/20 4:23 pm, Alex Otenko wrote:
> Does it reproduce if you remove the line that completes the future?
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest

_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest
Reply | Threaded
Open this post in threaded view
|

Re: CompletableFuture.get() swallows thread interrupt flag on Java 11

JSR166 Concurrency mailing list
In reply to this post by JSR166 Concurrency mailing list
Thanks for reporting this; sorry for problems. As David noticed, there
was a path that did not re-establish interrupt status. Now updated to
place this check along common path.

(While I'm at it: sorry that teaching and CS dept chairing during
pandemic make for  slow response to just about everything.)



@@ -1855,25 +1855,25 @@
              }
              else if (!queued)
                  queued = tryPushStack(q);
+            else if (interruptible && q.interrupted) {
+                q.thread = null;
+                cleanStack();
+                return null;
+            }
              else {
                  try {
                      ForkJoinPool.managedBlock(q);
                  } catch (InterruptedException ie) { // currently
cannot happen
                      q.interrupted = true;
                  }
-                if (q.interrupted && interruptible)
-                    break;
              }
          }
-        if (q != null && queued) {
+        if (q != null) {
              q.thread = null;
-            if (!interruptible && q.interrupted)
+            if (q.interrupted)
                  Thread.currentThread().interrupt();
-            if (r == null)
-                cleanStack();
          }
-        if (r != null || (r = result) != null)
-            postComplete();
+        postComplete();
          return r;
      }



On 10/14/20 11:33 PM, Jens Wilke via Concurrency-interest wrote:

> Hello everyone,
>
> finally I am using more of the Java 8 concurrency code (yay!) and
> found something strange.
>
> Here is the test code:
>
>     Executor executor = Executors.newCachedThreadPool();
>     Thread t = new Thread(() -> {
>       while (!Thread.interrupted()) {
>         CompletableFuture<Void> future = new CompletableFuture<>();
>         executor.execute(() -> future.complete(null));
>         try {
>           future.get();
>         } catch (InterruptedException e) {
>           return;
>         } catch (ExecutionException e) {
>           e.printStackTrace();
>         }
>       }
>     });
>     t.start();
>     Thread.sleep(1000);
>     t.interrupt();
>     t.join();
>
> This is supposed to terminate after 1s.
>
> Java version which does not terminate:
> openjdk version "11.0.8" 2020-07-14
> OpenJDK Runtime Environment (build 11.0.8+10-post-Ubuntu-0ubuntu120.04)
> OpenJDK 64-Bit Server VM (build 11.0.8+10-post-Ubuntu-0ubuntu120.04,
> mixed mode, sharing)
>
> Java version which does terminate:
> java version "1.8.0_162"
> Java(TM) SE Runtime Environment (build 1.8.0_162-b12)
> Java HotSpot(TM) 64-Bit Server VM (build 25.162-b12, mixed mode)
>
> Debugging into the CompletableFuture.get() in Java 11 I can find a
> code path that resets the interrupt flag but neither restores it or
> throws the InterruptedException.
>
> Can "someone" give that a closer look?
>
> Warm regards,
>
> Jens
> _______________________________________________
> Concurrency-interest mailing list
> [hidden email]
> http://cs.oswego.edu/mailman/listinfo/concurrency-interest
_______________________________________________
Concurrency-interest mailing list
[hidden email]
http://cs.oswego.edu/mailman/listinfo/concurrency-interest