< prev index next >

test/jdk/java/lang/Thread/virtual/ThreadAPI.java

Print this page
@@ -25,19 +25,20 @@
   * @test id=default
   * @bug 8284161 8286788 8321270
   * @summary Test Thread API with virtual threads
   * @modules java.base/java.lang:+open
   * @library /test/lib
-  * @run junit ThreadAPI
+  * @run junit/othervm --enable-native-access=ALL-UNNAMED ThreadAPI
   */
  
  /*
   * @test id=no-vmcontinuations
   * @requires vm.continuations
   * @modules java.base/java.lang:+open
   * @library /test/lib
-  * @run junit/othervm -XX:+UnlockExperimentalVMOptions -XX:-VMContinuations ThreadAPI
+  * @run junit/othervm -XX:+UnlockExperimentalVMOptions -XX:-VMContinuations
+  *     --enable-native-access=ALL-UNNAMED ThreadAPI
   */
  
  import java.time.Duration;
  import java.util.Arrays;
  import java.util.ArrayList;

@@ -59,10 +60,11 @@
  import java.util.concurrent.locks.ReentrantLock;
  import java.util.stream.Stream;
  import java.nio.channels.Selector;
  
  import jdk.test.lib.thread.VThreadRunner;
+ import jdk.test.lib.thread.VThreadPinner;
  import org.junit.jupiter.api.Test;
  import org.junit.jupiter.api.BeforeAll;
  import org.junit.jupiter.api.AfterAll;
  import org.junit.jupiter.params.ParameterizedTest;
  import org.junit.jupiter.params.provider.MethodSource;

@@ -753,15 +755,15 @@
       */
      @Test
      void testJoin33() throws Exception {
          AtomicBoolean done = new AtomicBoolean();
          Thread thread = Thread.ofVirtual().start(() -> {
-             synchronized (lock) {
+             VThreadPinner.runPinned(() -> {
                  while (!done.get()) {
                      LockSupport.parkNanos(Duration.ofMillis(20).toNanos());
                  }
-             }
+             });
          });
          try {
              assertFalse(thread.join(Duration.ofMillis(100)));
          } finally {
              done.set(true);

@@ -1177,22 +1179,23 @@
                  list.add("A");
                  var child = factory.newThread(() -> {
                      list.add("B");
                  });
                  child.start();
-                 synchronized (lock) {
+                 VThreadPinner.runPinned(() -> {
                      Thread.yield();   // pinned so will be a no-op
                      list.add("A");
-                 }
+                 });
                  try { child.join(); } catch (InterruptedException e) { }
              });
              thread.start();
              thread.join();
          }
          assertEquals(List.of("A", "A", "B"), list);
      }
  
+ 
      /**
       * Test that Thread.yield does not consume the thread's parking permit.
       */
      @Test
      void testYield3() throws Exception {

@@ -1406,13 +1409,13 @@
       */
      @Test
      void testSleep8() throws Exception {
          VThreadRunner.run(() -> {
              long start = millisTime();
-             synchronized (lock) {
+             VThreadPinner.runPinned(() -> {
                  Thread.sleep(1000);
-             }
+             });
              expectDuration(start, /*min*/900, /*max*/20_000);
          });
      }
  
      /**

@@ -1422,13 +1425,13 @@
      void testSleep9() throws Exception {
          VThreadRunner.run(() -> {
              Thread me = Thread.currentThread();
              me.interrupt();
              try {
-                 synchronized (lock) {
+                 VThreadPinner.runPinned(() -> {
                      Thread.sleep(2000);
-                 }
+                 });
                  fail("sleep not interrupted");
              } catch (InterruptedException e) {
                  // expected
                  assertFalse(me.isInterrupted());
              }

@@ -1442,13 +1445,13 @@
      void testSleep10() throws Exception {
          VThreadRunner.run(() -> {
              Thread t = Thread.currentThread();
              scheduleInterrupt(t, 100);
              try {
-                 synchronized (lock) {
+                 VThreadPinner.runPinned(() -> {
                      Thread.sleep(20 * 1000);
-                 }
+                 });
                  fail("sleep not interrupted");
              } catch (InterruptedException e) {
                  // interrupt status should be cleared
                  assertFalse(t.isInterrupted());
              }

@@ -1577,61 +1580,96 @@
       * Test Thread.setUncaughtExceptionHandler.
       */
      @Test
      void testUncaughtExceptionHandler1() throws Exception {
          class FooException extends RuntimeException { }
-         var exception = new AtomicReference<Throwable>();
-         Thread.UncaughtExceptionHandler handler = (thread, exc) -> exception.set(exc);
+         var handler = new CapturingUHE();
          Thread thread = Thread.ofVirtual().start(() -> {
              Thread me = Thread.currentThread();
              assertTrue(me.getUncaughtExceptionHandler() == me.getThreadGroup());
              me.setUncaughtExceptionHandler(handler);
              assertTrue(me.getUncaughtExceptionHandler() == handler);
              throw new FooException();
          });
          thread.join();
-         assertTrue(exception.get() instanceof FooException);
+         assertInstanceOf(FooException.class, handler.exception());
+         assertEquals(thread, handler.thread());
          assertNull(thread.getUncaughtExceptionHandler());
      }
  
      /**
       * Test default UncaughtExceptionHandler.
       */
      @Test
      void testUncaughtExceptionHandler2() throws Exception {
          class FooException extends RuntimeException { }
-         var exception = new AtomicReference<Throwable>();
-         Thread.UncaughtExceptionHandler handler = (thread, exc) -> exception.set(exc);
+         var handler = new CapturingUHE();
          Thread.UncaughtExceptionHandler savedHandler = Thread.getDefaultUncaughtExceptionHandler();
          Thread.setDefaultUncaughtExceptionHandler(handler);
          Thread thread;
          try {
              thread = Thread.ofVirtual().start(() -> {
                  Thread me = Thread.currentThread();
                  throw new FooException();
              });
              thread.join();
          } finally {
-             Thread.setDefaultUncaughtExceptionHandler(savedHandler);
+             Thread.setDefaultUncaughtExceptionHandler(savedHandler);  // restore
          }
-         assertTrue(exception.get() instanceof FooException);
+         assertInstanceOf(FooException.class, handler.exception());
+         assertEquals(thread, handler.thread());
          assertNull(thread.getUncaughtExceptionHandler());
      }
  
      /**
-      * Test no UncaughtExceptionHandler set.
+      * Test Thread and default UncaughtExceptionHandler set.
       */
      @Test
      void testUncaughtExceptionHandler3() throws Exception {
          class FooException extends RuntimeException { }
-         Thread thread = Thread.ofVirtual().start(() -> {
-             throw new FooException();
-         });
-         thread.join();
+         var defaultHandler = new CapturingUHE();
+         var threadHandler = new CapturingUHE();
+         Thread.UncaughtExceptionHandler savedHandler = Thread.getDefaultUncaughtExceptionHandler();
+         Thread.setDefaultUncaughtExceptionHandler(defaultHandler);
+         Thread thread;
+         try {
+             thread = Thread.ofVirtual().start(() -> {
+                 Thread me = Thread.currentThread();
+                 assertTrue(me.getUncaughtExceptionHandler() == me.getThreadGroup());
+                 me.setUncaughtExceptionHandler(threadHandler);
+                 assertTrue(me.getUncaughtExceptionHandler() == threadHandler);
+                 throw new FooException();
+             });
+             thread.join();
+         } finally {
+             Thread.setDefaultUncaughtExceptionHandler(savedHandler);  // restore
+         }
+         assertInstanceOf(FooException.class, threadHandler.exception());
+         assertNull(defaultHandler.exception());
+         assertEquals(thread, threadHandler.thread());
          assertNull(thread.getUncaughtExceptionHandler());
      }
  
+     /**
+      * Test no Thread or default UncaughtExceptionHandler set.
+      */
+     @Test
+     void testUncaughtExceptionHandler4() throws Exception {
+         Thread.UncaughtExceptionHandler savedHandler = Thread.getDefaultUncaughtExceptionHandler();
+         Thread.setDefaultUncaughtExceptionHandler(null);
+         try {
+             class FooException extends RuntimeException { }
+             Thread thread = Thread.ofVirtual().start(() -> {
+                 throw new FooException();
+             });
+             thread.join();
+             assertNull(thread.getUncaughtExceptionHandler());
+         } finally {
+             Thread.setDefaultUncaughtExceptionHandler(savedHandler);
+         }
+     }
+ 
      /**
       * Test Thread::threadId and getId.
       */
      @Test
      void testThreadId1() throws Exception {

@@ -2232,11 +2270,11 @@
      void testEnumerate1() throws Exception {
          VThreadRunner.run(() -> {
              ThreadGroup vgroup = Thread.currentThread().getThreadGroup();
              Thread[] threads = new Thread[100];
              int n = vgroup.enumerate(threads, /*recurse*/false);
-             assertTrue(n == 0);
+             assertFalse(Arrays.stream(threads, 0, n).anyMatch(Thread::isVirtual));
          });
      }
  
      /**
       * Test Thread.enumerate(true).

@@ -2345,10 +2383,37 @@
          });
          thread.join();
          assertTrue(thread.toString().contains("fred"));
      }
  
+     /**
+      * Thread.UncaughtExceptionHandler that captures the first exception thrown.
+      */
+     private static class CapturingUHE implements Thread.UncaughtExceptionHandler {
+         Thread thread;
+         Throwable exception;
+         @Override
+         public void uncaughtException(Thread t, Throwable e) {
+             synchronized (this) {
+                 if (thread == null) {
+                     this.thread = t;
+                     this.exception = e;
+                 }
+             }
+         }
+         Thread thread() {
+             synchronized (this) {
+                 return thread;
+             }
+         }
+         Throwable exception() {
+             synchronized (this) {
+                 return exception;
+             }
+         }
+     }
+ 
      /**
       * Waits for the given thread to reach a given state.
       */
      private void await(Thread thread, Thread.State expectedState) throws InterruptedException {
          Thread.State state = thread.getState();
< prev index next >