< prev index next >

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

Print this page

  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /*
  25  * @test id=default
  26  * @bug 8284161 8286788
  27  * @summary Test Thread API with virtual threads
  28  * @modules java.base/java.lang:+open
  29  * @library /test/lib
  30  * @run junit ThreadAPI
  31  */
  32 
  33 /*
  34  * @test id=no-vmcontinuations
  35  * @requires vm.continuations
  36  * @modules java.base/java.lang:+open
  37  * @library /test/lib
  38  * @run junit/othervm -XX:+UnlockExperimentalVMOptions -XX:-VMContinuations ThreadAPI

  39  */
  40 
  41 import java.time.Duration;
  42 import java.util.Arrays;
  43 import java.util.ArrayList;
  44 import java.util.List;
  45 import java.util.Map;
  46 import java.util.Set;
  47 import java.util.concurrent.CopyOnWriteArrayList;
  48 import java.util.concurrent.CountDownLatch;
  49 import java.util.concurrent.ExecutorService;
  50 import java.util.concurrent.Executor;
  51 import java.util.concurrent.Executors;
  52 import java.util.concurrent.ForkJoinPool;
  53 import java.util.concurrent.ScheduledExecutorService;
  54 import java.util.concurrent.ThreadFactory;
  55 import java.util.concurrent.TimeUnit;
  56 import java.util.concurrent.atomic.AtomicBoolean;
  57 import java.util.concurrent.atomic.AtomicReference;
  58 import java.util.concurrent.locks.LockSupport;
  59 import java.util.concurrent.locks.ReentrantLock;
  60 import java.util.stream.Stream;
  61 import java.nio.channels.Selector;
  62 
  63 import jdk.test.lib.thread.VThreadRunner;

  64 import org.junit.jupiter.api.Test;
  65 import org.junit.jupiter.api.BeforeAll;
  66 import org.junit.jupiter.api.AfterAll;
  67 import org.junit.jupiter.params.ParameterizedTest;
  68 import org.junit.jupiter.params.provider.MethodSource;
  69 import static org.junit.jupiter.api.Assertions.*;
  70 import static org.junit.jupiter.api.Assumptions.*;
  71 
  72 class ThreadAPI {
  73     private static final Object lock = new Object();
  74 
  75     // used for scheduling thread interrupt
  76     private static ScheduledExecutorService scheduler;
  77 
  78     @BeforeAll
  79     static void setup() throws Exception {
  80         ThreadFactory factory = Executors.defaultThreadFactory();
  81         scheduler = Executors.newSingleThreadScheduledExecutor(factory);
  82     }
  83 

 738         assertFalse(thread.isAlive());
 739     }
 740 
 741     /**
 742      * Test virtual thread invoking Thread.join on a thread that is parking
 743      * and unparking.
 744      */
 745     @Test
 746     void testJoin32() throws Exception {
 747         VThreadRunner.run(this::testJoin31);
 748     }
 749 
 750     /**
 751      * Test platform thread invoking timed-Thread.join on a thread that is parking
 752      * and unparking while pinned.
 753      */
 754     @Test
 755     void testJoin33() throws Exception {
 756         AtomicBoolean done = new AtomicBoolean();
 757         Thread thread = Thread.ofVirtual().start(() -> {
 758             synchronized (lock) {
 759                 while (!done.get()) {
 760                     LockSupport.parkNanos(Duration.ofMillis(20).toNanos());
 761                 }
 762             }
 763         });
 764         try {
 765             assertFalse(thread.join(Duration.ofMillis(100)));
 766         } finally {
 767             done.set(true);
 768             thread.join();
 769         }
 770     }
 771 
 772     /**
 773      * Test virtual thread invoking timed-Thread.join on a thread that is parking
 774      * and unparking while pinned.
 775      */
 776     @Test
 777     void testJoin34() throws Exception {
 778         // need at least two carrier threads due to pinning
 779         int previousParallelism = VThreadRunner.ensureParallelism(2);
 780         try {
 781             VThreadRunner.run(this::testJoin33);
 782         } finally {

1162         }
1163         assertEquals(List.of("A", "B", "A", "B"), list);
1164     }
1165 
1166     /**
1167      * Test Thread.yield when thread is pinned.
1168      */
1169     @Test
1170     void testYield2() throws Exception {
1171         assumeTrue(ThreadBuilders.supportsCustomScheduler(), "No support for custom schedulers");
1172         var list = new CopyOnWriteArrayList<String>();
1173         try (ExecutorService scheduler = Executors.newFixedThreadPool(1)) {
1174             Thread.Builder builder = ThreadBuilders.virtualThreadBuilder(scheduler);
1175             ThreadFactory factory = builder.factory();
1176             var thread = factory.newThread(() -> {
1177                 list.add("A");
1178                 var child = factory.newThread(() -> {
1179                     list.add("B");
1180                 });
1181                 child.start();
1182                 synchronized (lock) {
1183                     Thread.yield();   // pinned so will be a no-op
1184                     list.add("A");
1185                 }
1186                 try { child.join(); } catch (InterruptedException e) { }
1187             });
1188             thread.start();
1189             thread.join();
1190         }
1191         assertEquals(List.of("A", "A", "B"), list);
1192     }
1193 
1194     /**
1195      * Test Thread.onSpinWait.
1196      */
1197     @Test
1198     void testOnSpinWait() throws Exception {
1199         VThreadRunner.run(() -> {
1200             Thread me = Thread.currentThread();
1201             Thread.onSpinWait();
1202             assertTrue(Thread.currentThread() == me);
1203         });
1204     }
1205 

1361         });
1362         // attempt to disrupt sleep
1363         for (int i = 0; i < 5; i++) {
1364             Thread.sleep(20);
1365             LockSupport.unpark(thread);
1366         }
1367         thread.join();
1368         Exception e = exc.get();
1369         if (e != null) {
1370             throw e;
1371         }
1372     }
1373 
1374     /**
1375      * Test Thread.sleep when pinned.
1376      */
1377     @Test
1378     void testSleep8() throws Exception {
1379         VThreadRunner.run(() -> {
1380             long start = millisTime();
1381             synchronized (lock) {
1382                 Thread.sleep(1000);
1383             }
1384             expectDuration(start, /*min*/900, /*max*/20_000);
1385         });
1386     }
1387 
1388     /**
1389      * Test Thread.sleep when pinned and with interrupt status set.
1390      */
1391     @Test
1392     void testSleep9() throws Exception {
1393         VThreadRunner.run(() -> {
1394             Thread me = Thread.currentThread();
1395             me.interrupt();
1396             try {
1397                 synchronized (lock) {
1398                     Thread.sleep(2000);
1399                 }
1400                 fail("sleep not interrupted");
1401             } catch (InterruptedException e) {
1402                 // expected
1403                 assertFalse(me.isInterrupted());
1404             }
1405         });
1406     }
1407 
1408     /**
1409      * Test interrupting Thread.sleep when pinned.
1410      */
1411     @Test
1412     void testSleep10() throws Exception {
1413         VThreadRunner.run(() -> {
1414             Thread t = Thread.currentThread();
1415             scheduleInterrupt(t, 100);
1416             try {
1417                 synchronized (lock) {
1418                     Thread.sleep(20 * 1000);
1419                 }
1420                 fail("sleep not interrupted");
1421             } catch (InterruptedException e) {
1422                 // interrupt status should be cleared
1423                 assertFalse(t.isInterrupted());
1424             }
1425         });
1426     }
1427 
1428     /**
1429      * Test Thread.sleep(null).
1430      */
1431     @Test
1432     void testSleep11() throws Exception {
1433         assertThrows(NullPointerException.class, () -> Thread.sleep(null));
1434         VThreadRunner.run(() -> {
1435             assertThrows(NullPointerException.class, () -> Thread.sleep(null));
1436         });
1437     }
1438 
1439     /**

1532     void testContextClassLoader5() throws Exception {
1533         VThreadRunner.run(() -> {
1534             ClassLoader loader = new ClassLoader() { };
1535             Thread.currentThread().setContextClassLoader(loader);
1536             int characteristics = VThreadRunner.NO_INHERIT_THREAD_LOCALS;
1537             VThreadRunner.run(characteristics, () -> {
1538                 Thread t = Thread.currentThread();
1539                 assertTrue(t.getContextClassLoader() == ClassLoader.getSystemClassLoader());
1540                 t.setContextClassLoader(loader);
1541                 assertTrue(t.getContextClassLoader() == loader);
1542             });
1543         });
1544     }
1545 
1546     /**
1547      * Test Thread.setUncaughtExceptionHandler.
1548      */
1549     @Test
1550     void testUncaughtExceptionHandler1() throws Exception {
1551         class FooException extends RuntimeException { }
1552         var exception = new AtomicReference<Throwable>();
1553         Thread.UncaughtExceptionHandler handler = (thread, exc) -> exception.set(exc);
1554         Thread thread = Thread.ofVirtual().start(() -> {
1555             Thread me = Thread.currentThread();
1556             assertTrue(me.getUncaughtExceptionHandler() == me.getThreadGroup());
1557             me.setUncaughtExceptionHandler(handler);
1558             assertTrue(me.getUncaughtExceptionHandler() == handler);
1559             throw new FooException();
1560         });
1561         thread.join();
1562         assertTrue(exception.get() instanceof FooException);

1563         assertNull(thread.getUncaughtExceptionHandler());
1564     }
1565 
1566     /**
1567      * Test default UncaughtExceptionHandler.
1568      */
1569     @Test
1570     void testUncaughtExceptionHandler2() throws Exception {
1571         class FooException extends RuntimeException { }
1572         var exception = new AtomicReference<Throwable>();
1573         Thread.UncaughtExceptionHandler handler = (thread, exc) -> exception.set(exc);
1574         Thread.UncaughtExceptionHandler savedHandler = Thread.getDefaultUncaughtExceptionHandler();
1575         Thread.setDefaultUncaughtExceptionHandler(handler);
1576         Thread thread;
1577         try {
1578             thread = Thread.ofVirtual().start(() -> {
1579                 Thread me = Thread.currentThread();
1580                 throw new FooException();
1581             });
1582             thread.join();
1583         } finally {
1584             Thread.setDefaultUncaughtExceptionHandler(savedHandler);
1585         }
1586         assertTrue(exception.get() instanceof FooException);

1587         assertNull(thread.getUncaughtExceptionHandler());
1588     }
1589 
1590     /**
1591      * Test no UncaughtExceptionHandler set.
1592      */
1593     @Test
1594     void testUncaughtExceptionHandler3() throws Exception {
1595         class FooException extends RuntimeException { }
1596         Thread thread = Thread.ofVirtual().start(() -> {
1597             throw new FooException();
1598         });
1599         thread.join();
















1600         assertNull(thread.getUncaughtExceptionHandler());
1601     }
1602 



















1603     /**
1604      * Test Thread::threadId and getId.
1605      */
1606     @Test
1607     void testThreadId1() throws Exception {
1608         record ThreadIds(long threadId, long id) { }
1609         var ref = new AtomicReference<ThreadIds>();
1610 
1611         Thread vthread = Thread.ofVirtual().unstarted(() -> {
1612             Thread thread = Thread.currentThread();
1613             ref.set(new ThreadIds(thread.threadId(), thread.getId()));
1614             LockSupport.park();
1615         });
1616 
1617         // unstarted
1618         long tid = vthread.threadId();
1619 
1620         // running
1621         ThreadIds tids;
1622         vthread.start();

2187 
2188             vgroup.setMaxPriority(Thread.MAX_PRIORITY - 1);
2189             assertEquals(Thread.MAX_PRIORITY, vgroup.getMaxPriority());
2190             assertEquals(Thread.MAX_PRIORITY - 1, group.getMaxPriority());
2191 
2192             vgroup.setMaxPriority(Thread.MIN_PRIORITY);
2193             assertEquals(Thread.MAX_PRIORITY, vgroup.getMaxPriority());
2194             assertEquals(Thread.MIN_PRIORITY, group.getMaxPriority());
2195         });
2196     }
2197 
2198     /**
2199      * Test Thread.enumerate(false).
2200      */
2201     @Test
2202     void testEnumerate1() throws Exception {
2203         VThreadRunner.run(() -> {
2204             ThreadGroup vgroup = Thread.currentThread().getThreadGroup();
2205             Thread[] threads = new Thread[100];
2206             int n = vgroup.enumerate(threads, /*recurse*/false);
2207             assertTrue(n == 0);
2208         });
2209     }
2210 
2211     /**
2212      * Test Thread.enumerate(true).
2213      */
2214     @Test
2215     void testEnumerate2() throws Exception {
2216         VThreadRunner.run(() -> {
2217             ThreadGroup vgroup = Thread.currentThread().getThreadGroup();
2218             Thread[] threads = new Thread[100];
2219             int n = vgroup.enumerate(threads, /*recurse*/true);
2220             assertFalse(Arrays.stream(threads, 0, n).anyMatch(Thread::isVirtual));
2221         });
2222     }
2223 
2224     /**
2225      * Test equals and hashCode.
2226      */
2227     @Test

2300             assertTrue(thread.toString().contains("fred"));
2301         } finally {
2302             LockSupport.unpark(thread);
2303             thread.join();
2304         }
2305     }
2306 
2307     /**
2308      * Test toString on terminated thread.
2309      */
2310     @Test
2311     void testToString4() throws Exception {
2312         Thread thread = Thread.ofVirtual().start(() -> {
2313             Thread me = Thread.currentThread();
2314             me.setName("fred");
2315         });
2316         thread.join();
2317         assertTrue(thread.toString().contains("fred"));
2318     }
2319 



























2320     /**
2321      * Waits for the given thread to reach a given state.
2322      */
2323     private void await(Thread thread, Thread.State expectedState) throws InterruptedException {
2324         Thread.State state = thread.getState();
2325         while (state != expectedState) {
2326             assertTrue(state != Thread.State.TERMINATED, "Thread has terminated");
2327             Thread.sleep(10);
2328             state = thread.getState();
2329         }
2330     }
2331 
2332     /**
2333      * Schedule a thread to be interrupted after a delay.
2334      */
2335     private void scheduleInterrupt(Thread thread, long delayInMillis) {
2336         scheduler.schedule(thread::interrupt, delayInMillis, TimeUnit.MILLISECONDS);
2337     }
2338 }

  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /*
  25  * @test id=default
  26  * @bug 8284161 8286788
  27  * @summary Test Thread API with virtual threads
  28  * @modules java.base/java.lang:+open
  29  * @library /test/lib
  30  * @run junit/othervm --enable-native-access=ALL-UNNAMED ThreadAPI
  31  */
  32 
  33 /*
  34  * @test id=no-vmcontinuations
  35  * @requires vm.continuations
  36  * @modules java.base/java.lang:+open
  37  * @library /test/lib
  38  * @run junit/othervm -XX:+UnlockExperimentalVMOptions -XX:-VMContinuations
  39  *     --enable-native-access=ALL-UNNAMED ThreadAPI
  40  */
  41 
  42 import java.time.Duration;
  43 import java.util.Arrays;
  44 import java.util.ArrayList;
  45 import java.util.List;
  46 import java.util.Map;
  47 import java.util.Set;
  48 import java.util.concurrent.CopyOnWriteArrayList;
  49 import java.util.concurrent.CountDownLatch;
  50 import java.util.concurrent.ExecutorService;
  51 import java.util.concurrent.Executor;
  52 import java.util.concurrent.Executors;
  53 import java.util.concurrent.ForkJoinPool;
  54 import java.util.concurrent.ScheduledExecutorService;
  55 import java.util.concurrent.ThreadFactory;
  56 import java.util.concurrent.TimeUnit;
  57 import java.util.concurrent.atomic.AtomicBoolean;
  58 import java.util.concurrent.atomic.AtomicReference;
  59 import java.util.concurrent.locks.LockSupport;
  60 import java.util.concurrent.locks.ReentrantLock;
  61 import java.util.stream.Stream;
  62 import java.nio.channels.Selector;
  63 
  64 import jdk.test.lib.thread.VThreadRunner;
  65 import jdk.test.lib.thread.VThreadPinner;
  66 import org.junit.jupiter.api.Test;
  67 import org.junit.jupiter.api.BeforeAll;
  68 import org.junit.jupiter.api.AfterAll;
  69 import org.junit.jupiter.params.ParameterizedTest;
  70 import org.junit.jupiter.params.provider.MethodSource;
  71 import static org.junit.jupiter.api.Assertions.*;
  72 import static org.junit.jupiter.api.Assumptions.*;
  73 
  74 class ThreadAPI {
  75     private static final Object lock = new Object();
  76 
  77     // used for scheduling thread interrupt
  78     private static ScheduledExecutorService scheduler;
  79 
  80     @BeforeAll
  81     static void setup() throws Exception {
  82         ThreadFactory factory = Executors.defaultThreadFactory();
  83         scheduler = Executors.newSingleThreadScheduledExecutor(factory);
  84     }
  85 

 740         assertFalse(thread.isAlive());
 741     }
 742 
 743     /**
 744      * Test virtual thread invoking Thread.join on a thread that is parking
 745      * and unparking.
 746      */
 747     @Test
 748     void testJoin32() throws Exception {
 749         VThreadRunner.run(this::testJoin31);
 750     }
 751 
 752     /**
 753      * Test platform thread invoking timed-Thread.join on a thread that is parking
 754      * and unparking while pinned.
 755      */
 756     @Test
 757     void testJoin33() throws Exception {
 758         AtomicBoolean done = new AtomicBoolean();
 759         Thread thread = Thread.ofVirtual().start(() -> {
 760             VThreadPinner.runPinned(() -> {
 761                 while (!done.get()) {
 762                     LockSupport.parkNanos(Duration.ofMillis(20).toNanos());
 763                 }
 764             });
 765         });
 766         try {
 767             assertFalse(thread.join(Duration.ofMillis(100)));
 768         } finally {
 769             done.set(true);
 770             thread.join();
 771         }
 772     }
 773 
 774     /**
 775      * Test virtual thread invoking timed-Thread.join on a thread that is parking
 776      * and unparking while pinned.
 777      */
 778     @Test
 779     void testJoin34() throws Exception {
 780         // need at least two carrier threads due to pinning
 781         int previousParallelism = VThreadRunner.ensureParallelism(2);
 782         try {
 783             VThreadRunner.run(this::testJoin33);
 784         } finally {

1164         }
1165         assertEquals(List.of("A", "B", "A", "B"), list);
1166     }
1167 
1168     /**
1169      * Test Thread.yield when thread is pinned.
1170      */
1171     @Test
1172     void testYield2() throws Exception {
1173         assumeTrue(ThreadBuilders.supportsCustomScheduler(), "No support for custom schedulers");
1174         var list = new CopyOnWriteArrayList<String>();
1175         try (ExecutorService scheduler = Executors.newFixedThreadPool(1)) {
1176             Thread.Builder builder = ThreadBuilders.virtualThreadBuilder(scheduler);
1177             ThreadFactory factory = builder.factory();
1178             var thread = factory.newThread(() -> {
1179                 list.add("A");
1180                 var child = factory.newThread(() -> {
1181                     list.add("B");
1182                 });
1183                 child.start();
1184                 VThreadPinner.runPinned(() -> {
1185                     Thread.yield();   // pinned so will be a no-op
1186                     list.add("A");
1187                 });
1188                 try { child.join(); } catch (InterruptedException e) { }
1189             });
1190             thread.start();
1191             thread.join();
1192         }
1193         assertEquals(List.of("A", "A", "B"), list);
1194     }
1195 
1196     /**
1197      * Test Thread.onSpinWait.
1198      */
1199     @Test
1200     void testOnSpinWait() throws Exception {
1201         VThreadRunner.run(() -> {
1202             Thread me = Thread.currentThread();
1203             Thread.onSpinWait();
1204             assertTrue(Thread.currentThread() == me);
1205         });
1206     }
1207 

1363         });
1364         // attempt to disrupt sleep
1365         for (int i = 0; i < 5; i++) {
1366             Thread.sleep(20);
1367             LockSupport.unpark(thread);
1368         }
1369         thread.join();
1370         Exception e = exc.get();
1371         if (e != null) {
1372             throw e;
1373         }
1374     }
1375 
1376     /**
1377      * Test Thread.sleep when pinned.
1378      */
1379     @Test
1380     void testSleep8() throws Exception {
1381         VThreadRunner.run(() -> {
1382             long start = millisTime();
1383             VThreadPinner.runPinned(() -> {
1384                 Thread.sleep(1000);
1385             });
1386             expectDuration(start, /*min*/900, /*max*/20_000);
1387         });
1388     }
1389 
1390     /**
1391      * Test Thread.sleep when pinned and with interrupt status set.
1392      */
1393     @Test
1394     void testSleep9() throws Exception {
1395         VThreadRunner.run(() -> {
1396             Thread me = Thread.currentThread();
1397             me.interrupt();
1398             try {
1399                 VThreadPinner.runPinned(() -> {
1400                     Thread.sleep(2000);
1401                 });
1402                 fail("sleep not interrupted");
1403             } catch (InterruptedException e) {
1404                 // expected
1405                 assertFalse(me.isInterrupted());
1406             }
1407         });
1408     }
1409 
1410     /**
1411      * Test interrupting Thread.sleep when pinned.
1412      */
1413     @Test
1414     void testSleep10() throws Exception {
1415         VThreadRunner.run(() -> {
1416             Thread t = Thread.currentThread();
1417             scheduleInterrupt(t, 100);
1418             try {
1419                 VThreadPinner.runPinned(() -> {
1420                     Thread.sleep(20 * 1000);
1421                 });
1422                 fail("sleep not interrupted");
1423             } catch (InterruptedException e) {
1424                 // interrupt status should be cleared
1425                 assertFalse(t.isInterrupted());
1426             }
1427         });
1428     }
1429 
1430     /**
1431      * Test Thread.sleep(null).
1432      */
1433     @Test
1434     void testSleep11() throws Exception {
1435         assertThrows(NullPointerException.class, () -> Thread.sleep(null));
1436         VThreadRunner.run(() -> {
1437             assertThrows(NullPointerException.class, () -> Thread.sleep(null));
1438         });
1439     }
1440 
1441     /**

1534     void testContextClassLoader5() throws Exception {
1535         VThreadRunner.run(() -> {
1536             ClassLoader loader = new ClassLoader() { };
1537             Thread.currentThread().setContextClassLoader(loader);
1538             int characteristics = VThreadRunner.NO_INHERIT_THREAD_LOCALS;
1539             VThreadRunner.run(characteristics, () -> {
1540                 Thread t = Thread.currentThread();
1541                 assertTrue(t.getContextClassLoader() == ClassLoader.getSystemClassLoader());
1542                 t.setContextClassLoader(loader);
1543                 assertTrue(t.getContextClassLoader() == loader);
1544             });
1545         });
1546     }
1547 
1548     /**
1549      * Test Thread.setUncaughtExceptionHandler.
1550      */
1551     @Test
1552     void testUncaughtExceptionHandler1() throws Exception {
1553         class FooException extends RuntimeException { }
1554         var handler = new CapturingUHE();

1555         Thread thread = Thread.ofVirtual().start(() -> {
1556             Thread me = Thread.currentThread();
1557             assertTrue(me.getUncaughtExceptionHandler() == me.getThreadGroup());
1558             me.setUncaughtExceptionHandler(handler);
1559             assertTrue(me.getUncaughtExceptionHandler() == handler);
1560             throw new FooException();
1561         });
1562         thread.join();
1563         assertInstanceOf(FooException.class, handler.exception());
1564         assertEquals(thread, handler.thread());
1565         assertNull(thread.getUncaughtExceptionHandler());
1566     }
1567 
1568     /**
1569      * Test default UncaughtExceptionHandler.
1570      */
1571     @Test
1572     void testUncaughtExceptionHandler2() throws Exception {
1573         class FooException extends RuntimeException { }
1574         var handler = new CapturingUHE();

1575         Thread.UncaughtExceptionHandler savedHandler = Thread.getDefaultUncaughtExceptionHandler();
1576         Thread.setDefaultUncaughtExceptionHandler(handler);
1577         Thread thread;
1578         try {
1579             thread = Thread.ofVirtual().start(() -> {
1580                 Thread me = Thread.currentThread();
1581                 throw new FooException();
1582             });
1583             thread.join();
1584         } finally {
1585             Thread.setDefaultUncaughtExceptionHandler(savedHandler);  // restore
1586         }
1587         assertInstanceOf(FooException.class, handler.exception());
1588         assertEquals(thread, handler.thread());
1589         assertNull(thread.getUncaughtExceptionHandler());
1590     }
1591 
1592     /**
1593      * Test Thread and default UncaughtExceptionHandler set.
1594      */
1595     @Test
1596     void testUncaughtExceptionHandler3() throws Exception {
1597         class FooException extends RuntimeException { }
1598         var defaultHandler = new CapturingUHE();
1599         var threadHandler = new CapturingUHE();
1600         Thread.UncaughtExceptionHandler savedHandler = Thread.getDefaultUncaughtExceptionHandler();
1601         Thread.setDefaultUncaughtExceptionHandler(defaultHandler);
1602         Thread thread;
1603         try {
1604             thread = Thread.ofVirtual().start(() -> {
1605                 Thread me = Thread.currentThread();
1606                 assertTrue(me.getUncaughtExceptionHandler() == me.getThreadGroup());
1607                 me.setUncaughtExceptionHandler(threadHandler);
1608                 assertTrue(me.getUncaughtExceptionHandler() == threadHandler);
1609                 throw new FooException();
1610             });
1611             thread.join();
1612         } finally {
1613             Thread.setDefaultUncaughtExceptionHandler(savedHandler);  // restore
1614         }
1615         assertInstanceOf(FooException.class, threadHandler.exception());
1616         assertNull(defaultHandler.exception());
1617         assertEquals(thread, threadHandler.thread());
1618         assertNull(thread.getUncaughtExceptionHandler());
1619     }
1620 
1621     /**
1622      * Test no Thread or default UncaughtExceptionHandler set.
1623      */
1624     @Test
1625     void testUncaughtExceptionHandler4() throws Exception {
1626         Thread.UncaughtExceptionHandler savedHandler = Thread.getDefaultUncaughtExceptionHandler();
1627         Thread.setDefaultUncaughtExceptionHandler(null);
1628         try {
1629             class FooException extends RuntimeException { }
1630             Thread thread = Thread.ofVirtual().start(() -> {
1631                 throw new FooException();
1632             });
1633             thread.join();
1634             assertNull(thread.getUncaughtExceptionHandler());
1635         } finally {
1636             Thread.setDefaultUncaughtExceptionHandler(savedHandler);
1637         }
1638     }
1639 
1640     /**
1641      * Test Thread::threadId and getId.
1642      */
1643     @Test
1644     void testThreadId1() throws Exception {
1645         record ThreadIds(long threadId, long id) { }
1646         var ref = new AtomicReference<ThreadIds>();
1647 
1648         Thread vthread = Thread.ofVirtual().unstarted(() -> {
1649             Thread thread = Thread.currentThread();
1650             ref.set(new ThreadIds(thread.threadId(), thread.getId()));
1651             LockSupport.park();
1652         });
1653 
1654         // unstarted
1655         long tid = vthread.threadId();
1656 
1657         // running
1658         ThreadIds tids;
1659         vthread.start();

2224 
2225             vgroup.setMaxPriority(Thread.MAX_PRIORITY - 1);
2226             assertEquals(Thread.MAX_PRIORITY, vgroup.getMaxPriority());
2227             assertEquals(Thread.MAX_PRIORITY - 1, group.getMaxPriority());
2228 
2229             vgroup.setMaxPriority(Thread.MIN_PRIORITY);
2230             assertEquals(Thread.MAX_PRIORITY, vgroup.getMaxPriority());
2231             assertEquals(Thread.MIN_PRIORITY, group.getMaxPriority());
2232         });
2233     }
2234 
2235     /**
2236      * Test Thread.enumerate(false).
2237      */
2238     @Test
2239     void testEnumerate1() throws Exception {
2240         VThreadRunner.run(() -> {
2241             ThreadGroup vgroup = Thread.currentThread().getThreadGroup();
2242             Thread[] threads = new Thread[100];
2243             int n = vgroup.enumerate(threads, /*recurse*/false);
2244             assertFalse(Arrays.stream(threads, 0, n).anyMatch(Thread::isVirtual));
2245         });
2246     }
2247 
2248     /**
2249      * Test Thread.enumerate(true).
2250      */
2251     @Test
2252     void testEnumerate2() throws Exception {
2253         VThreadRunner.run(() -> {
2254             ThreadGroup vgroup = Thread.currentThread().getThreadGroup();
2255             Thread[] threads = new Thread[100];
2256             int n = vgroup.enumerate(threads, /*recurse*/true);
2257             assertFalse(Arrays.stream(threads, 0, n).anyMatch(Thread::isVirtual));
2258         });
2259     }
2260 
2261     /**
2262      * Test equals and hashCode.
2263      */
2264     @Test

2337             assertTrue(thread.toString().contains("fred"));
2338         } finally {
2339             LockSupport.unpark(thread);
2340             thread.join();
2341         }
2342     }
2343 
2344     /**
2345      * Test toString on terminated thread.
2346      */
2347     @Test
2348     void testToString4() throws Exception {
2349         Thread thread = Thread.ofVirtual().start(() -> {
2350             Thread me = Thread.currentThread();
2351             me.setName("fred");
2352         });
2353         thread.join();
2354         assertTrue(thread.toString().contains("fred"));
2355     }
2356 
2357     /**
2358      * Thread.UncaughtExceptionHandler that captures the first exception thrown.
2359      */
2360     private static class CapturingUHE implements Thread.UncaughtExceptionHandler {
2361         Thread thread;
2362         Throwable exception;
2363         @Override
2364         public void uncaughtException(Thread t, Throwable e) {
2365             synchronized (this) {
2366                 if (thread == null) {
2367                     this.thread = t;
2368                     this.exception = e;
2369                 }
2370             }
2371         }
2372         Thread thread() {
2373             synchronized (this) {
2374                 return thread;
2375             }
2376         }
2377         Throwable exception() {
2378             synchronized (this) {
2379                 return exception;
2380             }
2381         }
2382     }
2383 
2384     /**
2385      * Waits for the given thread to reach a given state.
2386      */
2387     private void await(Thread thread, Thread.State expectedState) throws InterruptedException {
2388         Thread.State state = thread.getState();
2389         while (state != expectedState) {
2390             assertTrue(state != Thread.State.TERMINATED, "Thread has terminated");
2391             Thread.sleep(10);
2392             state = thread.getState();
2393         }
2394     }
2395 
2396     /**
2397      * Schedule a thread to be interrupted after a delay.
2398      */
2399     private void scheduleInterrupt(Thread thread, long delayInMillis) {
2400         scheduler.schedule(thread::interrupt, delayInMillis, TimeUnit.MILLISECONDS);
2401     }
2402 }
< prev index next >