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 }
|