< prev index next >

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

Print this page
@@ -66,23 +66,29 @@
  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;
+ import org.junit.jupiter.params.provider.ValueSource;
  import static org.junit.jupiter.api.Assertions.*;
  import static org.junit.jupiter.api.Assumptions.*;
  
  class ThreadAPI {
      private static final Object lock = new Object();
  
      // used for scheduling thread interrupt
      private static ScheduledExecutorService scheduler;
  
      @BeforeAll
-     static void setup() throws Exception {
+     static void setup() {
          ThreadFactory factory = Executors.defaultThreadFactory();
          scheduler = Executors.newSingleThreadScheduledExecutor(factory);
+ 
+         // need >=2 carriers for testing pinning when main thread is a virtual thread
+         if (Thread.currentThread().isVirtual()) {
+             VThreadRunner.ensureParallelism(2);
+         }
      }
  
      @AfterAll
      static void finish() {
          scheduler.shutdown();

@@ -717,18 +723,11 @@
       * Test virtual thread invoking timed-Thread.join on a thread that is parking
       * and unparking while pinned.
       */
      @Test
      void testJoin34() throws Exception {
-         // need at least two carrier threads due to pinning
-         int previousParallelism = VThreadRunner.ensureParallelism(2);
-         try {
-             VThreadRunner.run(this::testJoin33);
-         } finally {
-             // restore
-             VThreadRunner.setParallelism(previousParallelism);
-         }
+         VThreadRunner.run(this::testJoin33);
      }
  
      /**
       * Test Thread.join(null).
       */

@@ -1106,14 +1105,45 @@
          }
          assertEquals(List.of("A", "B", "A", "B"), list);
      }
  
      /**
-      * Test Thread.yield when thread is pinned by native frame.
+      * Test Thread.yield releases carrier when virtual thread holds a monitor.
       */
      @Test
      void testYield2() throws Exception {
+         assumeTrue(ThreadBuilders.supportsCustomScheduler(), "No support for custom schedulers");
+         var list = new CopyOnWriteArrayList<String>();
+         try (ExecutorService scheduler = Executors.newFixedThreadPool(1)) {
+             Thread.Builder builder = ThreadBuilders.virtualThreadBuilder(scheduler);
+             ThreadFactory factory = builder.factory();
+             var lock = new Object();
+             var thread = factory.newThread(() -> {
+                 list.add("A");
+                 var child = factory.newThread(() -> {
+                     list.add("B");
+                     synchronized (lock) {
+                         Thread.yield();
+                     }
+                     list.add("B");
+                 });
+                 child.start();
+                 Thread.yield();
+                 list.add("A");
+                 try { child.join(); } catch (InterruptedException e) { }
+             });
+             thread.start();
+             thread.join();
+         }
+         assertEquals(List.of("A", "B", "A", "B"), list);
+     }
+ 
+     /**
+      * Test Thread.yield when thread is pinned by native frame.
+      */
+     @Test
+     void testYield3() throws Exception {
          assumeTrue(ThreadBuilders.supportsCustomScheduler(), "No support for custom schedulers");
          var list = new CopyOnWriteArrayList<String>();
          try (ExecutorService scheduler = Executors.newFixedThreadPool(1)) {
              Thread.Builder builder = ThreadBuilders.virtualThreadBuilder(scheduler);
              ThreadFactory factory = builder.factory();

@@ -1137,11 +1167,11 @@
  
      /**
       * Test Thread.yield does not consume the thread's parking permit.
       */
      @Test
-     void testYield3() throws Exception {
+     void testYield4() throws Exception {
          var thread = Thread.ofVirtual().start(() -> {
              LockSupport.unpark(Thread.currentThread());
              Thread.yield();
              LockSupport.park();  // should not park
          });

@@ -1150,11 +1180,11 @@
  
      /**
       * Test Thread.yield does not make available the thread's parking permit.
       */
      @Test
-     void testYield4() throws Exception {
+     void testYield5() throws Exception {
          var thread = Thread.ofVirtual().start(() -> {
              Thread.yield();
              LockSupport.park();  // should park
          });
          try {

@@ -1738,63 +1768,107 @@
          }
          assertTrue(completed.get() == true);
      }
  
      /**
-      * Test Thread::getState when thread is waiting to enter a monitor.
+      * Test Thread::getState when thread is blocked waiting to enter a monitor.
       */
-     @Test
-     void testGetState5() throws Exception {
-         var started = new CountDownLatch(1);
+     @ParameterizedTest
+     @ValueSource(booleans = { true, false })
+     void testGetState5(boolean pinned) throws Exception {
+         var ready = new AtomicBoolean();
          var thread = Thread.ofVirtual().unstarted(() -> {
-             started.countDown();
-             synchronized (lock) { }
+             if (pinned) {
+                 VThreadPinner.runPinned(() -> {
+                     ready.set(true);
+                     synchronized (lock) { }
+                 });
+             } else {
+                 ready.set(true);
+                 synchronized (lock) { }
+             }
          });
          synchronized (lock) {
              thread.start();
-             started.await();
+             awaitTrue(ready);
  
              // wait for thread to block
              await(thread, Thread.State.BLOCKED);
          }
          thread.join();
      }
  
      /**
       * Test Thread::getState when thread is waiting in Object.wait.
       */
-     @Test
-     void testGetState6() throws Exception {
+     @ParameterizedTest
+     @ValueSource(booleans = { true, false })
+     void testGetState6(boolean pinned) throws Exception {
+         var ready = new AtomicBoolean();
          var thread = Thread.ofVirtual().start(() -> {
              synchronized (lock) {
-                 try { lock.wait(); } catch (InterruptedException e) { }
+                 try {
+                     if (pinned) {
+                         VThreadPinner.runPinned(() -> {
+                             ready.set(true);
+                             lock.wait();
+                         });
+                     } else {
+                         ready.set(true);
+                         lock.wait();
+                     }
+                 } catch (InterruptedException e) { }
              }
          });
          try {
              // wait for thread to wait
+             awaitTrue(ready);
              await(thread, Thread.State.WAITING);
+ 
+             // notify, thread should block trying to reenter
+             synchronized (lock) {
+                 lock.notifyAll();
+                 await(thread, Thread.State.BLOCKED);
+             }
          } finally {
              thread.interrupt();
              thread.join();
          }
      }
  
      /**
       * Test Thread::getState when thread is waiting in Object.wait(millis).
       */
-     @Test
-     void testGetState7() throws Exception {
+     @ParameterizedTest
+     @ValueSource(booleans = { true, false })
+     void testGetState7(boolean pinned) throws Exception {
+         var ready = new AtomicBoolean();
          var thread = Thread.ofVirtual().start(() -> {
              synchronized (lock) {
                  try {
-                     lock.wait(Long.MAX_VALUE);
+                     if (pinned) {
+                         VThreadPinner.runPinned(() -> {
+                             ready.set(true);
+                             lock.wait(Long.MAX_VALUE);
+                         });
+                     } else {
+                         ready.set(true);
+                         lock.wait(Long.MAX_VALUE);
+                     }
                  } catch (InterruptedException e) { }
              }
          });
          try {
-             // wait for thread to wait
+             // wait for thread to timed-wait
+             awaitTrue(ready);
              await(thread, Thread.State.TIMED_WAITING);
+ 
+             // notify, thread should block trying to reenter
+             synchronized (lock) {
+                 lock.notifyAll();
+                 await(thread, Thread.State.BLOCKED);
+             }
          } finally {
              thread.interrupt();
              thread.join();
          }
      }

@@ -2011,10 +2085,11 @@
  
              // allow virtual thread to terminate
              synchronized (lock) {
                  lock.notifyAll();
              }
+             vthread.join();
  
              // check carrier thread's stack trace
              assertTrue(contains(carrierStackTrace, "java.util.concurrent.ForkJoinPool.runWorker"));
              assertFalse(contains(carrierStackTrace, "java.lang.Object.wait"));
  

@@ -2166,12 +2241,13 @@
  
              // allow virtual thread to terminate
              synchronized (lock) {
                  lock.notifyAll();
              }
+             vthread.join();
  
-             // get stack trace for the carrier thread
+             // stack trace for the carrier thread
              StackTraceElement[] stackTrace = map.get(carrier);
              assertNotNull(stackTrace);
              assertTrue(contains(stackTrace, "java.util.concurrent.ForkJoinPool"));
              assertFalse(contains(stackTrace, "java.lang.Object.wait"));
  

@@ -2415,10 +2491,19 @@
                  return exception;
              }
          }
      }
  
+     /**
+      * Waits for the boolean value to become true.
+      */
+     private static void awaitTrue(AtomicBoolean ref) throws Exception {
+         while (!ref.get()) {
+             Thread.sleep(20);
+         }
+     }
+ 
      /**
       * 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 >