1 /* 2 * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 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 8321270 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.VThreadPinner; 65 import jdk.test.lib.thread.VThreadRunner; 66 import jdk.test.lib.thread.VThreadScheduler; 67 import org.junit.jupiter.api.Test; 68 import org.junit.jupiter.api.BeforeAll; 69 import org.junit.jupiter.api.AfterAll; 70 import org.junit.jupiter.api.Disabled; 71 import org.junit.jupiter.params.ParameterizedTest; 72 import org.junit.jupiter.params.provider.MethodSource; 73 import org.junit.jupiter.params.provider.ValueSource; 74 import static org.junit.jupiter.api.Assertions.*; 75 import static org.junit.jupiter.api.Assumptions.*; 76 77 class ThreadAPI { 78 private static final Object lock = new Object(); 79 80 // used for scheduling thread interrupt 81 private static ScheduledExecutorService scheduler; 82 83 @BeforeAll 84 static void setup() { 85 ThreadFactory factory = Executors.defaultThreadFactory(); 86 scheduler = Executors.newSingleThreadScheduledExecutor(factory); 87 88 // need >=2 carriers for testing pinning 89 VThreadRunner.ensureParallelism(2); 90 } 91 92 @AfterAll 93 static void finish() { 94 scheduler.shutdown(); 95 } 96 97 /** 98 * An operation that does not return a result but may throw an exception. 99 */ 100 @FunctionalInterface 101 interface ThrowingRunnable { 102 void run() throws Exception; 103 } 104 105 /** 106 * Test Thread.currentThread before/after park. 107 */ 108 @Test 109 void testCurrentThread1() throws Exception { 110 var before = new AtomicReference<Thread>(); 111 var after = new AtomicReference<Thread>(); 112 var thread = Thread.ofVirtual().start(() -> { 113 before.set(Thread.currentThread()); 114 LockSupport.park(); 115 after.set(Thread.currentThread()); 116 }); 117 await(thread, Thread.State.WAITING); 118 LockSupport.unpark(thread); 119 thread.join(); 120 assertTrue(before.get() == thread); 121 assertTrue(after.get() == thread); 122 } 123 124 /** 125 * Test Thread.currentThread before/after entering synchronized block. 126 */ 127 @Test 128 void testCurrentThread2() throws Exception { 129 var ref1 = new AtomicReference<Thread>(); 130 var ref2 = new AtomicReference<Thread>(); 131 var ref3 = new AtomicReference<Thread>(); 132 var thread = Thread.ofVirtual().unstarted(() -> { 133 ref1.set(Thread.currentThread()); 134 synchronized (lock) { 135 ref2.set(Thread.currentThread()); 136 } 137 ref3.set(Thread.currentThread()); 138 }); 139 synchronized (lock) { 140 thread.start(); 141 await(thread, Thread.State.BLOCKED); 142 } 143 thread.join(); 144 assertTrue(ref1.get() == thread); 145 assertTrue(ref2.get() == thread); 146 assertTrue(ref3.get() == thread); 147 } 148 149 /** 150 * Test Thread.currentThread before/after acquiring lock. 151 */ 152 @Test 153 void testCurrentThread3() throws Exception { 154 var ref1 = new AtomicReference<Thread>(); 155 var ref2 = new AtomicReference<Thread>(); 156 var ref3 = new AtomicReference<Thread>(); 157 var lock = new ReentrantLock(); 158 var thread = Thread.ofVirtual().unstarted(() -> { 159 ref1.set(Thread.currentThread()); 160 lock.lock(); 161 try { 162 ref2.set(Thread.currentThread()); 163 } finally { 164 lock.unlock(); 165 } 166 ref3.set(Thread.currentThread()); 167 }); 168 lock.lock(); 169 try { 170 thread.start(); 171 await(thread, Thread.State.WAITING); 172 } finally { 173 lock.unlock(); 174 } 175 thread.join(); 176 assertTrue(ref1.get() == thread); 177 assertTrue(ref2.get() == thread); 178 assertTrue(ref3.get() == thread); 179 } 180 181 /** 182 * Test Thread::run. 183 */ 184 @Test 185 void testRun1() throws Exception { 186 var ref = new AtomicBoolean(); 187 var thread = Thread.ofVirtual().unstarted(() -> ref.set(true)); 188 thread.run(); 189 assertFalse(ref.get()); 190 } 191 192 /** 193 * Test Thread::start. 194 */ 195 @Test 196 void testStart1() throws Exception { 197 var ref = new AtomicBoolean(); 198 var thread = Thread.ofVirtual().unstarted(() -> ref.set(true)); 199 assertFalse(ref.get()); 200 thread.start(); 201 thread.join(); 202 assertTrue(ref.get()); 203 } 204 205 /** 206 * Test Thread::start, thread already started. 207 */ 208 @Test 209 void testStart2() throws Exception { 210 var thread = Thread.ofVirtual().start(LockSupport::park); 211 try { 212 assertThrows(IllegalThreadStateException.class, thread::start); 213 } finally { 214 LockSupport.unpark(thread); 215 thread.join(); 216 } 217 } 218 219 /** 220 * Test Thread::start, thread already terminated. 221 */ 222 @Test 223 void testStart3() throws Exception { 224 var thread = Thread.ofVirtual().start(() -> { }); 225 thread.join(); 226 assertThrows(IllegalThreadStateException.class, thread::start); 227 } 228 229 /** 230 * Test Thread.startVirtualThread. 231 */ 232 @Test 233 void testStartVirtualThread() throws Exception { 234 var ref = new AtomicReference<Thread>(); 235 Thread vthread = Thread.startVirtualThread(() -> { 236 ref.set(Thread.currentThread()); 237 LockSupport.park(); 238 }); 239 try { 240 assertTrue(vthread.isVirtual()); 241 242 // Thread.currentThread() returned by the virtual thread 243 Thread current; 244 while ((current = ref.get()) == null) { 245 Thread.sleep(10); 246 } 247 assertTrue(current == vthread); 248 } finally { 249 LockSupport.unpark(vthread); 250 } 251 252 assertThrows(NullPointerException.class, () -> Thread.startVirtualThread(null)); 253 } 254 255 /** 256 * Test Thread::stop from current thread. 257 */ 258 @Test 259 void testStop1() throws Exception { 260 VThreadRunner.run(() -> { 261 Thread t = Thread.currentThread(); 262 assertThrows(UnsupportedOperationException.class, t::stop); 263 }); 264 } 265 266 /** 267 * Test Thread::stop from another thread. 268 */ 269 @Test 270 void testStop2() throws Exception { 271 var thread = Thread.ofVirtual().start(() -> { 272 try { 273 Thread.sleep(20*1000); 274 } catch (InterruptedException e) { } 275 }); 276 try { 277 assertThrows(UnsupportedOperationException.class, thread::stop); 278 } finally { 279 thread.interrupt(); 280 thread.join(); 281 } 282 } 283 284 /** 285 * Test Thread.join before thread starts, platform thread invokes join. 286 */ 287 @Test 288 void testJoin1() throws Exception { 289 var thread = Thread.ofVirtual().unstarted(() -> { }); 290 291 thread.join(); 292 thread.join(0); 293 thread.join(0, 0); 294 thread.join(100); 295 thread.join(100, 0); 296 assertThrows(IllegalThreadStateException.class, 297 () -> thread.join(Duration.ofMillis(-100))); 298 assertThrows(IllegalThreadStateException.class, 299 () -> thread.join(Duration.ofMillis(0))); 300 assertThrows(IllegalThreadStateException.class, 301 () -> thread.join(Duration.ofMillis(100))); 302 } 303 304 /** 305 * Test Thread.join before thread starts, virtual thread invokes join. 306 */ 307 @Test 308 void testJoin2() throws Exception { 309 VThreadRunner.run(this::testJoin1); 310 } 311 312 /** 313 * Test Thread.join where thread does not terminate, platform thread invokes join. 314 */ 315 @Test 316 void testJoin3() throws Exception { 317 var thread = Thread.ofVirtual().start(LockSupport::park); 318 try { 319 thread.join(20); 320 thread.join(20, 0); 321 thread.join(20, 20); 322 thread.join(0, 20); 323 assertFalse(thread.join(Duration.ofMillis(-20))); 324 assertFalse(thread.join(Duration.ofMillis(0))); 325 assertFalse(thread.join(Duration.ofMillis(20))); 326 assertTrue(thread.isAlive()); 327 } finally { 328 LockSupport.unpark(thread); 329 thread.join(); 330 } 331 } 332 333 /** 334 * Test Thread.join where thread does not terminate, virtual thread invokes join. 335 */ 336 @Test 337 void testJoin4() throws Exception { 338 VThreadRunner.run(this::testJoin3); 339 } 340 341 /** 342 * Test Thread.join where thread terminates, platform thread invokes join. 343 */ 344 @Test 345 void testJoin5() throws Exception { 346 var thread = Thread.ofVirtual().start(() -> { 347 try { 348 Thread.sleep(50); 349 } catch (InterruptedException e) { } 350 }); 351 thread.join(); 352 assertFalse(thread.isAlive()); 353 } 354 355 /** 356 * Test Thread.join where thread terminates, virtual thread invokes join. 357 */ 358 @Test 359 void testJoin6() throws Exception { 360 VThreadRunner.run(this::testJoin5); 361 } 362 363 /** 364 * Test Thread.join where thread terminates, platform thread invokes timed-join. 365 */ 366 @Test 367 void testJoin7() throws Exception { 368 var thread = Thread.ofVirtual().start(() -> { 369 try { 370 Thread.sleep(50); 371 } catch (InterruptedException e) { } 372 }); 373 thread.join(10*1000); 374 assertFalse(thread.isAlive()); 375 } 376 377 /** 378 * Test Thread.join where thread terminates, virtual thread invokes timed-join. 379 */ 380 @Test 381 void testJoin8() throws Exception { 382 VThreadRunner.run(this::testJoin7); 383 } 384 385 /** 386 * Test Thread.join where thread terminates, platform thread invokes timed-join. 387 */ 388 @Test 389 void testJoin11() throws Exception { 390 var thread = Thread.ofVirtual().start(() -> { 391 try { 392 Thread.sleep(50); 393 } catch (InterruptedException e) { } 394 }); 395 assertTrue(thread.join(Duration.ofSeconds(10))); 396 assertFalse(thread.isAlive()); 397 } 398 399 /** 400 * Test Thread.join where thread terminates, virtual thread invokes timed-join. 401 */ 402 @Test 403 void testJoin12() throws Exception { 404 VThreadRunner.run(this::testJoin11); 405 } 406 407 /** 408 * Test Thread.join where thread already terminated, platform thread invokes join. 409 */ 410 @Test 411 void testJoin13() throws Exception { 412 var thread = Thread.ofVirtual().start(() -> { }); 413 while (thread.isAlive()) { 414 Thread.sleep(10); 415 } 416 thread.join(); 417 thread.join(0); 418 thread.join(0, 0); 419 thread.join(100); 420 thread.join(100, 0); 421 assertTrue(thread.join(Duration.ofMillis(-100))); 422 assertTrue(thread.join(Duration.ofMillis(0))); 423 assertTrue(thread.join(Duration.ofMillis(100))); 424 } 425 426 /** 427 * Test Thread.join where thread already terminated, virtual thread invokes join. 428 */ 429 @Test 430 void testJoin14() throws Exception { 431 VThreadRunner.run(this::testJoin13); 432 } 433 434 /** 435 * Test platform thread invoking Thread.join with interrupt status set. 436 */ 437 @Test 438 void testJoin15() throws Exception { 439 var thread = Thread.ofVirtual().start(LockSupport::park); 440 Thread.currentThread().interrupt(); 441 try { 442 assertThrows(InterruptedException.class, thread::join); 443 } finally { 444 Thread.interrupted(); 445 LockSupport.unpark(thread); 446 thread.join(); 447 } 448 } 449 450 /** 451 * Test virtual thread invoking Thread.join with interrupt status set. 452 */ 453 @Test 454 void testJoin16() throws Exception { 455 VThreadRunner.run(this::testJoin15); 456 } 457 458 /** 459 * Test platform thread invoking timed-Thread.join with interrupt status set. 460 */ 461 @Test 462 void testJoin17() throws Exception { 463 var thread = Thread.ofVirtual().start(LockSupport::park); 464 Thread.currentThread().interrupt(); 465 try { 466 assertThrows(InterruptedException.class, () -> thread.join(100)); 467 } finally { 468 Thread.interrupted(); 469 LockSupport.unpark(thread); 470 thread.join(); 471 } 472 } 473 474 /** 475 * Test virtual thread invoking timed-Thread.join with interrupt status set. 476 */ 477 @Test 478 void testJoin18() throws Exception { 479 VThreadRunner.run(this::testJoin17); 480 } 481 482 /** 483 * Test platform thread invoking timed-Thread.join with interrupt status set. 484 */ 485 @Test 486 void testJoin19() throws Exception { 487 var thread = Thread.ofVirtual().start(LockSupport::park); 488 Thread.currentThread().interrupt(); 489 try { 490 assertThrows(InterruptedException.class, 491 () -> thread.join(Duration.ofMillis(100))); 492 } finally { 493 Thread.interrupted(); 494 LockSupport.unpark(thread); 495 thread.join(); 496 } 497 } 498 499 /** 500 * Test virtual thread invoking timed-Thread.join with interrupt status set. 501 */ 502 @Test 503 void testJoin20() throws Exception { 504 VThreadRunner.run(this::testJoin19); 505 } 506 507 /** 508 * Test interrupt of platform thread blocked in Thread.join. 509 */ 510 @Test 511 void testJoin21() throws Exception { 512 var thread = Thread.ofVirtual().start(LockSupport::park); 513 scheduleInterrupt(Thread.currentThread(), 100); 514 try { 515 assertThrows(InterruptedException.class, thread::join); 516 } finally { 517 Thread.interrupted(); 518 LockSupport.unpark(thread); 519 thread.join(); 520 } 521 } 522 523 /** 524 * Test interrupt of virtual thread blocked in Thread.join. 525 */ 526 @Test 527 void testJoin22() throws Exception { 528 VThreadRunner.run(this::testJoin17); 529 } 530 531 /** 532 * Test interrupt of platform thread blocked in timed-Thread.join. 533 */ 534 @Test 535 void testJoin23() throws Exception { 536 var thread = Thread.ofVirtual().start(LockSupport::park); 537 scheduleInterrupt(Thread.currentThread(), 100); 538 try { 539 assertThrows(InterruptedException.class, () -> thread.join(10*1000)); 540 } finally { 541 Thread.interrupted(); 542 LockSupport.unpark(thread); 543 thread.join(); 544 } 545 } 546 547 /** 548 * Test interrupt of virtual thread blocked in Thread.join. 549 */ 550 @Test 551 void testJoin24() throws Exception { 552 VThreadRunner.run(this::testJoin23); 553 } 554 555 /** 556 * Test interrupt of platform thread blocked in Thread.join. 557 */ 558 @Test 559 void testJoin25() throws Exception { 560 var thread = Thread.ofVirtual().start(LockSupport::park); 561 scheduleInterrupt(Thread.currentThread(), 100); 562 try { 563 assertThrows(InterruptedException.class, 564 () -> thread.join(Duration.ofSeconds(10))); 565 } finally { 566 Thread.interrupted(); 567 LockSupport.unpark(thread); 568 thread.join(); 569 } 570 } 571 572 /** 573 * Test interrupt of virtual thread blocked in Thread.join. 574 */ 575 @Test 576 void testJoin26() throws Exception { 577 VThreadRunner.run(this::testJoin25); 578 } 579 580 /** 581 * Test virtual thread calling Thread.join to wait for platform thread to terminate. 582 */ 583 @Test 584 void testJoin27() throws Exception { 585 AtomicBoolean done = new AtomicBoolean(); 586 VThreadRunner.run(() -> { 587 var thread = new Thread(() -> { 588 while (!done.get()) { 589 LockSupport.park(); 590 } 591 }); 592 thread.start(); 593 try { 594 assertFalse(thread.join(Duration.ofMillis(-100))); 595 assertFalse(thread.join(Duration.ofMillis(0))); 596 assertFalse(thread.join(Duration.ofMillis(100))); 597 } finally { 598 done.set(true); 599 LockSupport.unpark(thread); 600 thread.join(); 601 } 602 }); 603 } 604 605 /** 606 * Test virtual thread calling Thread.join to wait for platform thread to terminate. 607 */ 608 @Test 609 void testJoin28() throws Exception { 610 long nanos = TimeUnit.NANOSECONDS.convert(100, TimeUnit.MILLISECONDS); 611 VThreadRunner.run(() -> { 612 var thread = new Thread(() -> LockSupport.parkNanos(nanos)); 613 thread.start(); 614 try { 615 assertTrue(thread.join(Duration.ofSeconds(Integer.MAX_VALUE))); 616 assertFalse(thread.isAlive()); 617 } finally { 618 LockSupport.unpark(thread); 619 thread.join(); 620 } 621 }); 622 } 623 624 /** 625 * Test virtual thread with interrupt status set calling Thread.join to wait 626 * for platform thread to terminate. 627 */ 628 @Test 629 void testJoin29() throws Exception { 630 VThreadRunner.run(() -> { 631 var thread = new Thread(LockSupport::park); 632 thread.start(); 633 Thread.currentThread().interrupt(); 634 try { 635 thread.join(Duration.ofSeconds(Integer.MAX_VALUE)); 636 fail("join not interrupted"); 637 } catch (InterruptedException expected) { 638 assertFalse(Thread.interrupted()); 639 } finally { 640 LockSupport.unpark(thread); 641 thread.join(); 642 } 643 }); 644 } 645 646 /** 647 * Test interrupting virtual thread that is waiting in Thread.join for 648 * platform thread to terminate. 649 */ 650 @Test 651 void testJoin30() throws Exception { 652 VThreadRunner.run(() -> { 653 AtomicBoolean done = new AtomicBoolean(); 654 var thread = new Thread(() -> { 655 while (!done.get()) { 656 LockSupport.park(); 657 } 658 }); 659 thread.start(); 660 scheduleInterrupt(Thread.currentThread(), 100); 661 try { 662 thread.join(Duration.ofSeconds(Integer.MAX_VALUE)); 663 fail("join not interrupted"); 664 } catch (InterruptedException expected) { 665 assertFalse(Thread.interrupted()); 666 } finally { 667 done.set(true); 668 LockSupport.unpark(thread); 669 thread.join(); 670 } 671 }); 672 } 673 674 /** 675 * Test platform thread invoking Thread.join on a thread that is parking 676 * and unparking. 677 */ 678 @Test 679 void testJoin31() throws Exception { 680 Thread thread = Thread.ofVirtual().start(() -> { 681 synchronized (lock) { 682 for (int i = 0; i < 10; i++) { 683 LockSupport.parkNanos(Duration.ofMillis(20).toNanos()); 684 } 685 } 686 }); 687 thread.join(); 688 assertFalse(thread.isAlive()); 689 } 690 691 /** 692 * Test virtual thread invoking Thread.join on a thread that is parking 693 * and unparking. 694 */ 695 @Test 696 void testJoin32() throws Exception { 697 VThreadRunner.run(this::testJoin31); 698 } 699 700 /** 701 * Test platform thread invoking timed-Thread.join on a thread that is parking 702 * and unparking while pinned. 703 */ 704 @Test 705 void testJoin33() throws Exception { 706 AtomicBoolean done = new AtomicBoolean(); 707 Thread thread = Thread.ofVirtual().start(() -> { 708 VThreadPinner.runPinned(() -> { 709 while (!done.get()) { 710 LockSupport.parkNanos(Duration.ofMillis(20).toNanos()); 711 } 712 }); 713 }); 714 try { 715 assertFalse(thread.join(Duration.ofMillis(100))); 716 } finally { 717 done.set(true); 718 thread.join(); 719 } 720 } 721 722 /** 723 * Test virtual thread invoking timed-Thread.join on a thread that is parking 724 * and unparking while pinned. 725 */ 726 @Test 727 void testJoin34() throws Exception { 728 VThreadRunner.run(this::testJoin33); 729 } 730 731 /** 732 * Test Thread.join(null). 733 */ 734 @Test 735 void testJoin35() throws Exception { 736 var thread = Thread.ofVirtual().unstarted(LockSupport::park); 737 738 // unstarted 739 assertThrows(NullPointerException.class, () -> thread.join(null)); 740 741 // started 742 thread.start(); 743 try { 744 assertThrows(NullPointerException.class, () -> thread.join(null)); 745 } finally { 746 LockSupport.unpark(thread); 747 } 748 thread.join(); 749 750 // terminated 751 assertThrows(NullPointerException.class, () -> thread.join(null)); 752 } 753 754 /** 755 * Test Thread.interrupt on current thread. 756 */ 757 @Test 758 void testInterrupt1() throws Exception { 759 VThreadRunner.run(() -> { 760 Thread me = Thread.currentThread(); 761 assertFalse(me.isInterrupted()); 762 me.interrupt(); 763 assertTrue(me.isInterrupted()); 764 Thread.interrupted(); // clear interrupt status 765 assertFalse(me.isInterrupted()); 766 me.interrupt(); 767 }); 768 } 769 770 /** 771 * Test Thread.interrupt before thread started. 772 */ 773 @Test 774 void testInterrupt2() throws Exception { 775 var thread = Thread.ofVirtual().unstarted(() -> { }); 776 thread.interrupt(); 777 assertTrue(thread.isInterrupted()); 778 } 779 780 /** 781 * Test Thread.interrupt after thread started. 782 */ 783 @Test 784 void testInterrupt3() throws Exception { 785 var thread = Thread.ofVirtual().start(() -> { }); 786 thread.join(); 787 thread.interrupt(); 788 assertTrue(thread.isInterrupted()); 789 } 790 791 /** 792 * Test termination with interrupt status set. 793 */ 794 @Test 795 void testInterrupt4() throws Exception { 796 var thread = Thread.ofVirtual().start(() -> { 797 Thread.currentThread().interrupt(); 798 }); 799 thread.join(); 800 assertTrue(thread.isInterrupted()); 801 } 802 803 /** 804 * Test Thread.interrupt of thread blocked in Selector.select. 805 */ 806 @Test 807 void testInterrupt5() throws Exception { 808 var exception = new AtomicReference<Exception>(); 809 var thread = Thread.ofVirtual().start(() -> { 810 try { 811 try (var sel = Selector.open()) { 812 sel.select(); 813 assertTrue(Thread.currentThread().isInterrupted()); 814 } 815 } catch (Exception e) { 816 exception.set(e); 817 } 818 }); 819 Thread.sleep(100); // give time for thread to block 820 thread.interrupt(); 821 thread.join(); 822 assertNull(exception.get()); 823 } 824 825 /** 826 * Test Thread.interrupt of thread parked in sleep. 827 */ 828 @Test 829 void testInterrupt6() throws Exception { 830 var exception = new AtomicReference<Exception>(); 831 var thread = Thread.ofVirtual().start(() -> { 832 try { 833 try { 834 Thread.sleep(60*1000); 835 fail("sleep not interrupted"); 836 } catch (InterruptedException e) { 837 // interrupt status should be reset 838 assertFalse(Thread.interrupted()); 839 } 840 } catch (Exception e) { 841 exception.set(e); 842 } 843 }); 844 await(thread, Thread.State.TIMED_WAITING); 845 thread.interrupt(); 846 thread.join(); 847 assertNull(exception.get()); 848 } 849 850 /** 851 * Test Thread.interrupt of parked thread. 852 */ 853 @Test 854 void testInterrupt7() throws Exception { 855 var exception = new AtomicReference<Exception>(); 856 var thread = Thread.ofVirtual().start(() -> { 857 try { 858 LockSupport.park(); 859 assertTrue(Thread.currentThread().isInterrupted()); 860 } catch (Exception e) { 861 exception.set(e); 862 } 863 }); 864 await(thread, Thread.State.WAITING); 865 thread.interrupt(); 866 thread.join(); 867 assertNull(exception.get()); 868 } 869 870 /** 871 * Test trying to park with interrupt status set. 872 */ 873 @Test 874 void testInterrupt8() throws Exception { 875 VThreadRunner.run(() -> { 876 Thread me = Thread.currentThread(); 877 me.interrupt(); 878 LockSupport.park(); 879 assertTrue(Thread.interrupted()); 880 }); 881 } 882 883 /** 884 * Test trying to wait with interrupt status set. 885 */ 886 @Test 887 void testInterrupt9() throws Exception { 888 VThreadRunner.run(() -> { 889 Thread me = Thread.currentThread(); 890 me.interrupt(); 891 synchronized (lock) { 892 try { 893 lock.wait(); 894 fail("wait not interrupted"); 895 } catch (InterruptedException expected) { 896 assertFalse(Thread.interrupted()); 897 } 898 } 899 }); 900 } 901 902 /** 903 * Test trying to block with interrupt status set. 904 */ 905 @Test 906 void testInterrupt10() throws Exception { 907 VThreadRunner.run(() -> { 908 Thread me = Thread.currentThread(); 909 me.interrupt(); 910 try (Selector sel = Selector.open()) { 911 sel.select(); 912 assertTrue(Thread.interrupted()); 913 } 914 }); 915 } 916 917 /** 918 * Test Thread.getName and setName from current thread, started without name. 919 */ 920 @Test 921 void testSetName1() throws Exception { 922 VThreadRunner.run(() -> { 923 Thread me = Thread.currentThread(); 924 assertTrue(me.getName().isEmpty()); 925 me.setName("fred"); 926 assertEquals("fred", me.getName()); 927 }); 928 } 929 930 /** 931 * Test Thread.getName and setName from current thread, started with name. 932 */ 933 @Test 934 void testSetName2() throws Exception { 935 VThreadRunner.run("fred", () -> { 936 Thread me = Thread.currentThread(); 937 assertEquals("fred", me.getName()); 938 me.setName("joe"); 939 assertEquals("joe", me.getName()); 940 }); 941 } 942 943 /** 944 * Test Thread.getName and setName from another thread. 945 */ 946 @Test 947 void testSetName3() throws Exception { 948 var thread = Thread.ofVirtual().unstarted(LockSupport::park); 949 assertTrue(thread.getName().isEmpty()); 950 951 // not started 952 thread.setName("fred1"); 953 assertEquals("fred1", thread.getName()); 954 955 // started 956 thread.start(); 957 try { 958 assertEquals("fred1", thread.getName()); 959 thread.setName("fred2"); 960 assertEquals("fred2", thread.getName()); 961 } finally { 962 LockSupport.unpark(thread); 963 thread.join(); 964 } 965 966 // terminated 967 assertEquals("fred2", thread.getName()); 968 thread.setName("fred3"); 969 assertEquals("fred3", thread.getName()); 970 } 971 972 /** 973 * Test Thread.getPriority and setPriority from current thread. 974 */ 975 @Test 976 void testSetPriority1() throws Exception { 977 VThreadRunner.run(() -> { 978 Thread me = Thread.currentThread(); 979 assertEquals(Thread.NORM_PRIORITY, me.getPriority()); 980 981 me.setPriority(Thread.MAX_PRIORITY); 982 assertEquals(Thread.NORM_PRIORITY, me.getPriority()); 983 984 me.setPriority(Thread.NORM_PRIORITY); 985 assertEquals(Thread.NORM_PRIORITY, me.getPriority()); 986 987 me.setPriority(Thread.MIN_PRIORITY); 988 assertEquals(Thread.NORM_PRIORITY, me.getPriority()); 989 990 assertThrows(IllegalArgumentException.class, () -> me.setPriority(-1)); 991 }); 992 } 993 994 /** 995 * Test Thread.getPriority and setPriority from another thread. 996 */ 997 @Test 998 void testSetPriority2() throws Exception { 999 var thread = Thread.ofVirtual().unstarted(LockSupport::park); 1000 1001 // not started 1002 assertEquals(Thread.NORM_PRIORITY, thread.getPriority()); 1003 1004 thread.setPriority(Thread.MAX_PRIORITY); 1005 assertEquals(Thread.NORM_PRIORITY, thread.getPriority()); 1006 1007 thread.setPriority(Thread.NORM_PRIORITY); 1008 assertEquals(Thread.NORM_PRIORITY, thread.getPriority()); 1009 1010 thread.setPriority(Thread.MIN_PRIORITY); 1011 assertEquals(Thread.NORM_PRIORITY, thread.getPriority()); 1012 1013 assertThrows(IllegalArgumentException.class, () -> thread.setPriority(-1)); 1014 1015 // running 1016 thread.start(); 1017 try { 1018 assertEquals(Thread.NORM_PRIORITY, thread.getPriority()); 1019 thread.setPriority(Thread.NORM_PRIORITY); 1020 1021 thread.setPriority(Thread.MAX_PRIORITY); 1022 assertEquals(Thread.NORM_PRIORITY, thread.getPriority()); 1023 1024 thread.setPriority(Thread.NORM_PRIORITY); 1025 assertEquals(Thread.NORM_PRIORITY, thread.getPriority()); 1026 1027 thread.setPriority(Thread.MIN_PRIORITY); 1028 assertEquals(Thread.NORM_PRIORITY, thread.getPriority()); 1029 1030 assertThrows(IllegalArgumentException.class, () -> thread.setPriority(-1)); 1031 1032 } finally { 1033 LockSupport.unpark(thread); 1034 } 1035 thread.join(); 1036 1037 // terminated 1038 assertEquals(Thread.NORM_PRIORITY, thread.getPriority()); 1039 } 1040 1041 /** 1042 * Test Thread.isDaemon and setDaemon from current thread. 1043 */ 1044 @Test 1045 void testSetDaemon1() throws Exception { 1046 VThreadRunner.run(() -> { 1047 Thread me = Thread.currentThread(); 1048 assertTrue(me.isDaemon()); 1049 assertThrows(IllegalThreadStateException.class, () -> me.setDaemon(true)); 1050 assertThrows(IllegalArgumentException.class, () -> me.setDaemon(false)); 1051 }); 1052 } 1053 1054 /** 1055 * Test Thread.isDaemon and setDaemon from another thread. 1056 */ 1057 @Test 1058 void testSetDaemon2() throws Exception { 1059 var thread = Thread.ofVirtual().unstarted(LockSupport::park); 1060 1061 // not started 1062 assertTrue(thread.isDaemon()); 1063 thread.setDaemon(true); 1064 assertThrows(IllegalArgumentException.class, () -> thread.setDaemon(false)); 1065 1066 // running 1067 thread.start(); 1068 try { 1069 assertTrue(thread.isDaemon()); 1070 assertThrows(IllegalThreadStateException.class, () -> thread.setDaemon(true)); 1071 assertThrows(IllegalArgumentException.class, () -> thread.setDaemon(false)); 1072 } finally { 1073 LockSupport.unpark(thread); 1074 } 1075 thread.join(); 1076 1077 // terminated 1078 assertTrue(thread.isDaemon()); 1079 } 1080 1081 /** 1082 * Test Thread.yield releases carrier thread. 1083 */ 1084 @Test 1085 void testYield1() throws Exception { 1086 assumeTrue(VThreadScheduler.supportsCustomScheduler(), "No support for custom schedulers"); 1087 var list = new CopyOnWriteArrayList<String>(); 1088 try (ExecutorService scheduler = Executors.newFixedThreadPool(1)) { 1089 ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); 1090 var thread = factory.newThread(() -> { 1091 list.add("A"); 1092 var child = factory.newThread(() -> { 1093 list.add("B"); 1094 Thread.yield(); 1095 list.add("B"); 1096 }); 1097 child.start(); 1098 Thread.yield(); 1099 list.add("A"); 1100 try { child.join(); } catch (InterruptedException e) { } 1101 }); 1102 thread.start(); 1103 thread.join(); 1104 } 1105 assertEquals(List.of("A", "B", "A", "B"), list); 1106 } 1107 1108 /** 1109 * Test Thread.yield when thread is pinned by native frame. 1110 */ 1111 @Test 1112 void testYield2() throws Exception { 1113 assumeTrue(VThreadScheduler.supportsCustomScheduler(), "No support for custom schedulers"); 1114 var list = new CopyOnWriteArrayList<String>(); 1115 try (ExecutorService scheduler = Executors.newFixedThreadPool(1)) { 1116 ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); 1117 var thread = factory.newThread(() -> { 1118 list.add("A"); 1119 var child = factory.newThread(() -> { 1120 list.add("B"); 1121 }); 1122 child.start(); 1123 VThreadPinner.runPinned(() -> { 1124 Thread.yield(); // pinned so will be a no-op 1125 list.add("A"); 1126 }); 1127 try { child.join(); } catch (InterruptedException e) { } 1128 }); 1129 thread.start(); 1130 thread.join(); 1131 } 1132 assertEquals(List.of("A", "A", "B"), list); 1133 } 1134 1135 /** 1136 * Test Thread.yield does not consume the thread's parking permit. 1137 */ 1138 @Test 1139 void testYield3() throws Exception { 1140 var thread = Thread.ofVirtual().start(() -> { 1141 LockSupport.unpark(Thread.currentThread()); 1142 Thread.yield(); 1143 LockSupport.park(); // should not park 1144 }); 1145 thread.join(); 1146 } 1147 1148 /** 1149 * Test Thread.yield does not make available the thread's parking permit. 1150 */ 1151 @Test 1152 void testYield4() throws Exception { 1153 var thread = Thread.ofVirtual().start(() -> { 1154 Thread.yield(); 1155 LockSupport.park(); // should park 1156 }); 1157 try { 1158 await(thread, Thread.State.WAITING); 1159 } finally { 1160 LockSupport.unpark(thread); 1161 thread.join(); 1162 } 1163 } 1164 1165 /** 1166 * Test Thread.onSpinWait. 1167 */ 1168 @Test 1169 void testOnSpinWait() throws Exception { 1170 VThreadRunner.run(() -> { 1171 Thread me = Thread.currentThread(); 1172 Thread.onSpinWait(); 1173 assertTrue(Thread.currentThread() == me); 1174 }); 1175 } 1176 1177 /** 1178 * Test Thread.sleep(-1). 1179 */ 1180 @Test 1181 void testSleep1() throws Exception { 1182 VThreadRunner.run(() -> { 1183 assertThrows(IllegalArgumentException.class, () -> Thread.sleep(-1)); 1184 assertThrows(IllegalArgumentException.class, () -> Thread.sleep(-1, 0)); 1185 assertThrows(IllegalArgumentException.class, () -> Thread.sleep(0, -1)); 1186 assertThrows(IllegalArgumentException.class, () -> Thread.sleep(0, 1_000_000)); 1187 }); 1188 VThreadRunner.run(() -> Thread.sleep(Duration.ofMillis(-1))); 1189 } 1190 1191 /** 1192 * Test Thread.sleep(0). 1193 */ 1194 @Test 1195 void testSleep2() throws Exception { 1196 VThreadRunner.run(() -> Thread.sleep(0)); 1197 VThreadRunner.run(() -> Thread.sleep(0, 0)); 1198 VThreadRunner.run(() -> Thread.sleep(Duration.ofMillis(0))); 1199 } 1200 1201 /** 1202 * Tasks that sleep for 1 second. 1203 */ 1204 static Stream<ThrowingRunnable> oneSecondSleepers() { 1205 return Stream.of( 1206 () -> Thread.sleep(1000), 1207 () -> Thread.sleep(Duration.ofSeconds(1)) 1208 ); 1209 } 1210 1211 /** 1212 * Test Thread.sleep duration. 1213 */ 1214 @ParameterizedTest 1215 @MethodSource("oneSecondSleepers") 1216 void testSleep3(ThrowingRunnable sleeper) throws Exception { 1217 VThreadRunner.run(() -> { 1218 long start = millisTime(); 1219 sleeper.run(); 1220 expectDuration(start, /*min*/900, /*max*/20_000); 1221 }); 1222 } 1223 1224 /** 1225 * Tasks that sleep for zero or longer duration. 1226 */ 1227 static Stream<ThrowingRunnable> sleepers() { 1228 return Stream.of( 1229 () -> Thread.sleep(0), 1230 () -> Thread.sleep(0, 0), 1231 () -> Thread.sleep(1000), 1232 () -> Thread.sleep(1000, 0), 1233 () -> Thread.sleep(Duration.ofMillis(0)), 1234 () -> Thread.sleep(Duration.ofMillis(1000)) 1235 ); 1236 } 1237 1238 /** 1239 * Test Thread.sleep with interrupt status set. 1240 */ 1241 @ParameterizedTest 1242 @MethodSource("sleepers") 1243 void testSleep4(ThrowingRunnable sleeper) throws Exception { 1244 VThreadRunner.run(() -> { 1245 Thread me = Thread.currentThread(); 1246 me.interrupt(); 1247 try { 1248 sleeper.run(); 1249 fail("sleep was not interrupted"); 1250 } catch (InterruptedException e) { 1251 // expected 1252 assertFalse(me.isInterrupted()); 1253 } 1254 }); 1255 } 1256 1257 /** 1258 * Test Thread.sleep with interrupt status set and a negative duration. 1259 */ 1260 @Test 1261 void testSleep4() throws Exception { 1262 VThreadRunner.run(() -> { 1263 Thread me = Thread.currentThread(); 1264 me.interrupt(); 1265 Thread.sleep(Duration.ofMillis(-1000)); // does nothing 1266 assertTrue(me.isInterrupted()); 1267 }); 1268 } 1269 1270 /** 1271 * Tasks that sleep for a long time. 1272 */ 1273 static Stream<ThrowingRunnable> longSleepers() { 1274 return Stream.of( 1275 () -> Thread.sleep(20_000), 1276 () -> Thread.sleep(20_000, 0), 1277 () -> Thread.sleep(Duration.ofSeconds(20)) 1278 ); 1279 } 1280 1281 /** 1282 * Test interrupting Thread.sleep. 1283 */ 1284 @ParameterizedTest 1285 @MethodSource("longSleepers") 1286 void testSleep5(ThrowingRunnable sleeper) throws Exception { 1287 VThreadRunner.run(() -> { 1288 Thread t = Thread.currentThread(); 1289 scheduleInterrupt(t, 100); 1290 try { 1291 sleeper.run(); 1292 fail("sleep was not interrupted"); 1293 } catch (InterruptedException e) { 1294 // interrupt status should be cleared 1295 assertFalse(t.isInterrupted()); 1296 } 1297 }); 1298 } 1299 1300 /** 1301 * Test that Thread.sleep does not disrupt parking permit. 1302 */ 1303 @Test 1304 void testSleep6() throws Exception { 1305 VThreadRunner.run(() -> { 1306 LockSupport.unpark(Thread.currentThread()); 1307 1308 long start = millisTime(); 1309 Thread.sleep(1000); 1310 expectDuration(start, /*min*/900, /*max*/20_000); 1311 1312 // check that parking permit was not consumed 1313 LockSupport.park(); 1314 }); 1315 } 1316 1317 /** 1318 * Test that Thread.sleep is not disrupted by unparking thread. 1319 */ 1320 @Test 1321 void testSleep7() throws Exception { 1322 AtomicReference<Exception> exc = new AtomicReference<>(); 1323 var thread = Thread.ofVirtual().start(() -> { 1324 try { 1325 long start = millisTime(); 1326 Thread.sleep(1000); 1327 expectDuration(start, /*min*/900, /*max*/20_000); 1328 } catch (Exception e) { 1329 exc.set(e); 1330 } 1331 1332 }); 1333 // attempt to disrupt sleep 1334 for (int i = 0; i < 5; i++) { 1335 Thread.sleep(20); 1336 LockSupport.unpark(thread); 1337 } 1338 thread.join(); 1339 Exception e = exc.get(); 1340 if (e != null) { 1341 throw e; 1342 } 1343 } 1344 1345 /** 1346 * Test Thread.sleep when pinned. 1347 */ 1348 @Test 1349 void testSleep8() throws Exception { 1350 VThreadPinner.runPinned(() -> { 1351 long start = millisTime(); 1352 Thread.sleep(1000); 1353 expectDuration(start, /*min*/900, /*max*/20_000); 1354 }); 1355 } 1356 1357 /** 1358 * Test Thread.sleep when pinned and with interrupt status set. 1359 */ 1360 @Test 1361 void testSleep9() throws Exception { 1362 VThreadRunner.run(() -> { 1363 Thread me = Thread.currentThread(); 1364 me.interrupt(); 1365 try { 1366 VThreadPinner.runPinned(() -> { 1367 Thread.sleep(2000); 1368 }); 1369 fail("sleep not interrupted"); 1370 } catch (InterruptedException e) { 1371 // expected 1372 assertFalse(me.isInterrupted()); 1373 } 1374 }); 1375 } 1376 1377 /** 1378 * Test interrupting Thread.sleep when pinned. 1379 */ 1380 @Test 1381 void testSleep10() throws Exception { 1382 VThreadRunner.run(() -> { 1383 Thread t = Thread.currentThread(); 1384 scheduleInterrupt(t, 100); 1385 try { 1386 VThreadPinner.runPinned(() -> { 1387 Thread.sleep(20 * 1000); 1388 }); 1389 fail("sleep not interrupted"); 1390 } catch (InterruptedException e) { 1391 // interrupt status should be cleared 1392 assertFalse(t.isInterrupted()); 1393 } 1394 }); 1395 } 1396 1397 /** 1398 * Test Thread.sleep(null). 1399 */ 1400 @Test 1401 void testSleep11() throws Exception { 1402 assertThrows(NullPointerException.class, () -> Thread.sleep(null)); 1403 VThreadRunner.run(() -> { 1404 assertThrows(NullPointerException.class, () -> Thread.sleep(null)); 1405 }); 1406 } 1407 1408 /** 1409 * Returns the current time in milliseconds. 1410 */ 1411 private static long millisTime() { 1412 long now = System.nanoTime(); 1413 return TimeUnit.MILLISECONDS.convert(now, TimeUnit.NANOSECONDS); 1414 } 1415 1416 /** 1417 * Check the duration of a task 1418 * @param start start time, in milliseconds 1419 * @param min minimum expected duration, in milliseconds 1420 * @param max maximum expected duration, in milliseconds 1421 * @return the duration (now - start), in milliseconds 1422 */ 1423 private static void expectDuration(long start, long min, long max) { 1424 long duration = millisTime() - start; 1425 assertTrue(duration >= min, 1426 "Duration " + duration + "ms, expected >= " + min + "ms"); 1427 assertTrue(duration <= max, 1428 "Duration " + duration + "ms, expected <= " + max + "ms"); 1429 } 1430 1431 /** 1432 * Test Thread.xxxContextClassLoader from the current thread. 1433 */ 1434 @Test 1435 void testContextClassLoader1() throws Exception { 1436 ClassLoader loader = new ClassLoader() { }; 1437 VThreadRunner.run(() -> { 1438 Thread t = Thread.currentThread(); 1439 t.setContextClassLoader(loader); 1440 assertTrue(t.getContextClassLoader() == loader); 1441 }); 1442 } 1443 1444 /** 1445 * Test inheriting initial value of TCCL from platform thread. 1446 */ 1447 @Test 1448 void testContextClassLoader2() throws Exception { 1449 ClassLoader loader = new ClassLoader() { }; 1450 Thread t = Thread.currentThread(); 1451 ClassLoader savedLoader = t.getContextClassLoader(); 1452 t.setContextClassLoader(loader); 1453 try { 1454 VThreadRunner.run(() -> { 1455 assertTrue(Thread.currentThread().getContextClassLoader() == loader); 1456 }); 1457 } finally { 1458 t.setContextClassLoader(savedLoader); 1459 } 1460 } 1461 1462 /** 1463 * Test inheriting initial value of TCCL from virtual thread. 1464 */ 1465 @Test 1466 void testContextClassLoader3() throws Exception { 1467 VThreadRunner.run(() -> { 1468 ClassLoader loader = new ClassLoader() { }; 1469 Thread.currentThread().setContextClassLoader(loader); 1470 VThreadRunner.run(() -> { 1471 assertTrue(Thread.currentThread().getContextClassLoader() == loader); 1472 }); 1473 }); 1474 } 1475 1476 /** 1477 * Test inheriting initial value of TCCL through an intermediate virtual thread. 1478 */ 1479 @Test 1480 void testContextClassLoader4() throws Exception { 1481 ClassLoader loader = new ClassLoader() { }; 1482 Thread t = Thread.currentThread(); 1483 ClassLoader savedLoader = t.getContextClassLoader(); 1484 t.setContextClassLoader(loader); 1485 try { 1486 VThreadRunner.run(() -> { 1487 VThreadRunner.run(() -> { 1488 assertTrue(Thread.currentThread().getContextClassLoader() == loader); 1489 }); 1490 }); 1491 } finally { 1492 t.setContextClassLoader(savedLoader); 1493 } 1494 } 1495 1496 /** 1497 * Test Thread.xxxContextClassLoader when thread does not inherit the 1498 * initial value of inheritable thread locals. 1499 */ 1500 @Test 1501 void testContextClassLoader5() throws Exception { 1502 VThreadRunner.run(() -> { 1503 ClassLoader loader = new ClassLoader() { }; 1504 Thread.currentThread().setContextClassLoader(loader); 1505 int characteristics = VThreadRunner.NO_INHERIT_THREAD_LOCALS; 1506 VThreadRunner.run(characteristics, () -> { 1507 Thread t = Thread.currentThread(); 1508 assertTrue(t.getContextClassLoader() == ClassLoader.getSystemClassLoader()); 1509 t.setContextClassLoader(loader); 1510 assertTrue(t.getContextClassLoader() == loader); 1511 }); 1512 }); 1513 } 1514 1515 /** 1516 * Test Thread.setUncaughtExceptionHandler. 1517 */ 1518 @Test 1519 void testUncaughtExceptionHandler1() throws Exception { 1520 class FooException extends RuntimeException { } 1521 var handler = new CapturingUHE(); 1522 Thread thread = Thread.ofVirtual().start(() -> { 1523 Thread me = Thread.currentThread(); 1524 assertTrue(me.getUncaughtExceptionHandler() == me.getThreadGroup()); 1525 me.setUncaughtExceptionHandler(handler); 1526 assertTrue(me.getUncaughtExceptionHandler() == handler); 1527 throw new FooException(); 1528 }); 1529 thread.join(); 1530 assertInstanceOf(FooException.class, handler.exception()); 1531 assertEquals(thread, handler.thread()); 1532 assertNull(thread.getUncaughtExceptionHandler()); 1533 } 1534 1535 /** 1536 * Test default UncaughtExceptionHandler. 1537 */ 1538 @Test 1539 void testUncaughtExceptionHandler2() throws Exception { 1540 class FooException extends RuntimeException { } 1541 var handler = new CapturingUHE(); 1542 Thread.UncaughtExceptionHandler savedHandler = Thread.getDefaultUncaughtExceptionHandler(); 1543 Thread.setDefaultUncaughtExceptionHandler(handler); 1544 Thread thread; 1545 try { 1546 thread = Thread.ofVirtual().start(() -> { 1547 Thread me = Thread.currentThread(); 1548 throw new FooException(); 1549 }); 1550 thread.join(); 1551 } finally { 1552 Thread.setDefaultUncaughtExceptionHandler(savedHandler); // restore 1553 } 1554 assertInstanceOf(FooException.class, handler.exception()); 1555 assertEquals(thread, handler.thread()); 1556 assertNull(thread.getUncaughtExceptionHandler()); 1557 } 1558 1559 /** 1560 * Test Thread and default UncaughtExceptionHandler set. 1561 */ 1562 @Test 1563 void testUncaughtExceptionHandler3() throws Exception { 1564 class FooException extends RuntimeException { } 1565 var defaultHandler = new CapturingUHE(); 1566 var threadHandler = new CapturingUHE(); 1567 Thread.UncaughtExceptionHandler savedHandler = Thread.getDefaultUncaughtExceptionHandler(); 1568 Thread.setDefaultUncaughtExceptionHandler(defaultHandler); 1569 Thread thread; 1570 try { 1571 thread = Thread.ofVirtual().start(() -> { 1572 Thread me = Thread.currentThread(); 1573 assertTrue(me.getUncaughtExceptionHandler() == me.getThreadGroup()); 1574 me.setUncaughtExceptionHandler(threadHandler); 1575 assertTrue(me.getUncaughtExceptionHandler() == threadHandler); 1576 throw new FooException(); 1577 }); 1578 thread.join(); 1579 } finally { 1580 Thread.setDefaultUncaughtExceptionHandler(savedHandler); // restore 1581 } 1582 assertInstanceOf(FooException.class, threadHandler.exception()); 1583 assertNull(defaultHandler.exception()); 1584 assertEquals(thread, threadHandler.thread()); 1585 assertNull(thread.getUncaughtExceptionHandler()); 1586 } 1587 1588 /** 1589 * Test no Thread or default UncaughtExceptionHandler set. 1590 */ 1591 @Test 1592 void testUncaughtExceptionHandler4() throws Exception { 1593 Thread.UncaughtExceptionHandler savedHandler = Thread.getDefaultUncaughtExceptionHandler(); 1594 Thread.setDefaultUncaughtExceptionHandler(null); 1595 try { 1596 class FooException extends RuntimeException { } 1597 Thread thread = Thread.ofVirtual().start(() -> { 1598 throw new FooException(); 1599 }); 1600 thread.join(); 1601 assertNull(thread.getUncaughtExceptionHandler()); 1602 } finally { 1603 Thread.setDefaultUncaughtExceptionHandler(savedHandler); 1604 } 1605 } 1606 1607 /** 1608 * Test Thread::threadId and getId. 1609 */ 1610 @Test 1611 void testThreadId1() throws Exception { 1612 record ThreadIds(long threadId, long id) { } 1613 var ref = new AtomicReference<ThreadIds>(); 1614 1615 Thread vthread = Thread.ofVirtual().unstarted(() -> { 1616 Thread thread = Thread.currentThread(); 1617 ref.set(new ThreadIds(thread.threadId(), thread.getId())); 1618 LockSupport.park(); 1619 }); 1620 1621 // unstarted 1622 long tid = vthread.threadId(); 1623 1624 // running 1625 ThreadIds tids; 1626 vthread.start(); 1627 try { 1628 while ((tids = ref.get()) == null) { 1629 Thread.sleep(10); 1630 } 1631 assertTrue(tids.threadId() == tid); 1632 assertTrue(tids.id() == tid); 1633 } finally { 1634 LockSupport.unpark(vthread); 1635 vthread.join(); 1636 } 1637 1638 // terminated 1639 assertTrue(vthread.threadId() == tid); 1640 assertTrue(vthread.getId() == tid); 1641 } 1642 1643 /** 1644 * Test that each Thread has a unique ID 1645 */ 1646 @Test 1647 void testThreadId2() throws Exception { 1648 // thread ID should be unique 1649 long tid1 = Thread.ofVirtual().unstarted(() -> { }).threadId(); 1650 long tid2 = Thread.ofVirtual().unstarted(() -> { }).threadId(); 1651 long tid3 = Thread.currentThread().threadId(); 1652 assertFalse(tid1 == tid2); 1653 assertFalse(tid1 == tid3); 1654 assertFalse(tid2 == tid3); 1655 } 1656 1657 /** 1658 * Test Thread::getState when thread is new/unstarted. 1659 */ 1660 @Test 1661 void testGetState1() { 1662 var thread = Thread.ofVirtual().unstarted(() -> { }); 1663 assertEquals(Thread.State.NEW, thread.getState()); 1664 } 1665 1666 /** 1667 * Test Thread::getState when thread is terminated. 1668 */ 1669 @Test 1670 void testGetState2() throws Exception { 1671 var thread = Thread.ofVirtual().start(() -> { }); 1672 thread.join(); 1673 assertEquals(Thread.State.TERMINATED, thread.getState()); 1674 } 1675 1676 /** 1677 * Test Thread::getState when thread is runnable (mounted). 1678 */ 1679 @Test 1680 void testGetState3() throws Exception { 1681 var started = new CountDownLatch(1); 1682 var done = new AtomicBoolean(); 1683 var thread = Thread.ofVirtual().start(() -> { 1684 started.countDown(); 1685 1686 // spin until done 1687 while (!done.get()) { 1688 Thread.onSpinWait(); 1689 } 1690 }); 1691 try { 1692 // wait for thread to start 1693 started.await(); 1694 1695 // thread should be runnable 1696 assertEquals(Thread.State.RUNNABLE, thread.getState()); 1697 } finally { 1698 done.set(true); 1699 thread.join(); 1700 } 1701 } 1702 1703 /** 1704 * Test Thread::getState when thread is runnable (not mounted). 1705 */ 1706 @Test 1707 void testGetState4() throws Exception { 1708 assumeTrue(VThreadScheduler.supportsCustomScheduler(), "No support for custom schedulers"); 1709 AtomicBoolean completed = new AtomicBoolean(); 1710 try (ExecutorService scheduler = Executors.newFixedThreadPool(1)) { 1711 ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); 1712 Thread thread1 = factory.newThread(() -> { 1713 Thread thread2 = factory.newThread(LockSupport::park); 1714 assertEquals(Thread.State.NEW, thread2.getState()); 1715 1716 // start t2 to make it runnable 1717 thread2.start(); 1718 try { 1719 assertEquals(Thread.State.RUNNABLE, thread2.getState()); 1720 1721 // yield to allow t2 to run and park 1722 Thread.yield(); 1723 assertEquals(Thread.State.WAITING, thread2.getState()); 1724 } finally { 1725 // unpark t2 to make it runnable again 1726 LockSupport.unpark(thread2); 1727 } 1728 1729 // t2 should be runnable (not mounted) 1730 assertEquals(Thread.State.RUNNABLE, thread2.getState()); 1731 1732 completed.set(true); 1733 }); 1734 thread1.start(); 1735 thread1.join(); 1736 } 1737 assertTrue(completed.get() == true); 1738 } 1739 1740 /** 1741 * Test Thread::getState when thread is blocked waiting to enter a monitor. 1742 */ 1743 @ParameterizedTest 1744 @ValueSource(booleans = { true, false }) 1745 void testGetState5(boolean pinned) throws Exception { 1746 var ready = new AtomicBoolean(); 1747 var thread = Thread.ofVirtual().unstarted(() -> { 1748 if (pinned) { 1749 VThreadPinner.runPinned(() -> { 1750 ready.set(true); 1751 synchronized (lock) { } 1752 }); 1753 } else { 1754 ready.set(true); 1755 synchronized (lock) { } 1756 } 1757 }); 1758 synchronized (lock) { 1759 thread.start(); 1760 awaitTrue(ready); 1761 1762 // wait for thread to block 1763 await(thread, Thread.State.BLOCKED); 1764 } 1765 thread.join(); 1766 } 1767 1768 /** 1769 * Test Thread::getState when thread is waiting in Object.wait. 1770 */ 1771 @ParameterizedTest 1772 @ValueSource(booleans = { true, false }) 1773 void testGetState6(boolean pinned) throws Exception { 1774 var ready = new AtomicBoolean(); 1775 var thread = Thread.ofVirtual().start(() -> { 1776 synchronized (lock) { 1777 try { 1778 if (pinned) { 1779 VThreadPinner.runPinned(() -> { 1780 ready.set(true); 1781 lock.wait(); 1782 }); 1783 } else { 1784 ready.set(true); 1785 lock.wait(); 1786 } 1787 } catch (InterruptedException e) { } 1788 } 1789 }); 1790 try { 1791 // wait for thread to wait 1792 awaitTrue(ready); 1793 await(thread, Thread.State.WAITING); 1794 1795 // notify, thread should block trying to reenter 1796 synchronized (lock) { 1797 lock.notifyAll(); 1798 await(thread, Thread.State.BLOCKED); 1799 } 1800 } finally { 1801 thread.interrupt(); 1802 thread.join(); 1803 } 1804 } 1805 1806 /** 1807 * Test Thread::getState when thread is waiting in Object.wait(millis). 1808 */ 1809 @ParameterizedTest 1810 @ValueSource(booleans = { true, false }) 1811 void testGetState7(boolean pinned) throws Exception { 1812 var ready = new AtomicBoolean(); 1813 var thread = Thread.ofVirtual().start(() -> { 1814 synchronized (lock) { 1815 try { 1816 if (pinned) { 1817 VThreadPinner.runPinned(() -> { 1818 ready.set(true); 1819 lock.wait(Long.MAX_VALUE); 1820 }); 1821 } else { 1822 ready.set(true); 1823 lock.wait(Long.MAX_VALUE); 1824 } 1825 } catch (InterruptedException e) { } 1826 } 1827 }); 1828 try { 1829 // wait for thread to timed-wait 1830 awaitTrue(ready); 1831 await(thread, Thread.State.TIMED_WAITING); 1832 1833 // notify, thread should block trying to reenter 1834 synchronized (lock) { 1835 lock.notifyAll(); 1836 await(thread, Thread.State.BLOCKED); 1837 } 1838 } finally { 1839 thread.interrupt(); 1840 thread.join(); 1841 } 1842 } 1843 1844 /** 1845 * Test Thread::getState when thread is parked. 1846 */ 1847 @Test 1848 void testGetState8() throws Exception { 1849 var thread = Thread.ofVirtual().start(LockSupport::park); 1850 try { 1851 await(thread, Thread.State.WAITING); 1852 } finally { 1853 LockSupport.unpark(thread); 1854 thread.join(); 1855 } 1856 } 1857 1858 /** 1859 * Test Thread::getState when thread is timed parked. 1860 */ 1861 @Test 1862 void testGetState9() throws Exception { 1863 var thread = Thread.ofVirtual().start(() -> LockSupport.parkNanos(Long.MAX_VALUE)); 1864 try { 1865 await(thread, Thread.State.TIMED_WAITING); 1866 } finally { 1867 LockSupport.unpark(thread); 1868 thread.join(); 1869 } 1870 } 1871 1872 /** 1873 * Test Thread::getState when thread is parked while holding a monitor. 1874 */ 1875 @Test 1876 void testGetState10() throws Exception { 1877 var started = new CountDownLatch(1); 1878 var done = new AtomicBoolean(); 1879 var thread = Thread.ofVirtual().start(() -> { 1880 started.countDown(); 1881 synchronized (lock) { 1882 while (!done.get()) { 1883 LockSupport.park(); 1884 } 1885 } 1886 }); 1887 try { 1888 // wait for thread to start 1889 started.await(); 1890 1891 // wait for thread to park 1892 await(thread, Thread.State.WAITING); 1893 } finally { 1894 done.set(true); 1895 LockSupport.unpark(thread); 1896 thread.join(); 1897 } 1898 } 1899 1900 /** 1901 * Test Thread::getState when thread is timed parked while holding a monitor. 1902 */ 1903 @Test 1904 void testGetState11() throws Exception { 1905 var started = new CountDownLatch(1); 1906 var done = new AtomicBoolean(); 1907 var thread = Thread.ofVirtual().start(() -> { 1908 started.countDown(); 1909 synchronized (lock) { 1910 while (!done.get()) { 1911 LockSupport.parkNanos(Long.MAX_VALUE); 1912 } 1913 } 1914 }); 1915 try { 1916 // wait for thread to start 1917 started.await(); 1918 1919 // wait for thread to park 1920 await(thread, Thread.State.TIMED_WAITING); 1921 } finally { 1922 done.set(true); 1923 LockSupport.unpark(thread); 1924 thread.join(); 1925 } 1926 } 1927 1928 /** 1929 * Test Thread::isAlive. 1930 */ 1931 @Test 1932 void testIsAlive1() throws Exception { 1933 // unstarted 1934 var thread = Thread.ofVirtual().unstarted(LockSupport::park); 1935 assertFalse(thread.isAlive()); 1936 1937 // started 1938 thread.start(); 1939 try { 1940 assertTrue(thread.isAlive()); 1941 } finally { 1942 LockSupport.unpark(thread); 1943 thread.join(); 1944 } 1945 1946 // terminated 1947 assertFalse(thread.isAlive()); 1948 } 1949 1950 /** 1951 * Test Thread.holdsLock when lock not held. 1952 */ 1953 @Test 1954 void testHoldsLock1() throws Exception { 1955 VThreadRunner.run(() -> { 1956 var lock = new Object(); 1957 assertFalse(Thread.holdsLock(lock)); 1958 }); 1959 } 1960 1961 /** 1962 * Test Thread.holdsLock when lock held. 1963 */ 1964 @Test 1965 void testHoldsLock2() throws Exception { 1966 VThreadRunner.run(() -> { 1967 var lock = new Object(); 1968 synchronized (lock) { 1969 assertTrue(Thread.holdsLock(lock)); 1970 } 1971 }); 1972 } 1973 1974 /** 1975 * Test Thread::getStackTrace on unstarted thread. 1976 */ 1977 @Test 1978 void testGetStackTraceUnstarted() { 1979 var thread = Thread.ofVirtual().unstarted(() -> { }); 1980 StackTraceElement[] stack = thread.getStackTrace(); 1981 assertTrue(stack.length == 0); 1982 } 1983 1984 /** 1985 * Test Thread::getStackTrace on thread that has been started but has not run. 1986 */ 1987 @Test 1988 void testGetStackTraceStarted() throws Exception { 1989 assumeTrue(VThreadScheduler.supportsCustomScheduler(), "No support for custom schedulers"); 1990 Executor scheduler = task -> { }; 1991 ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); 1992 Thread thread = factory.newThread(() -> { }); 1993 thread.start(); 1994 StackTraceElement[] stack = thread.getStackTrace(); 1995 assertTrue(stack.length == 0); 1996 } 1997 1998 /** 1999 * Test Thread::getStackTrace on thread that is runnable-mounted. 2000 */ 2001 @Test 2002 void testGetStackTraceRunnableMounted() throws Exception { 2003 var ready = new AtomicBoolean(); 2004 var done = new AtomicBoolean(); 2005 2006 class Foo { 2007 void spinUntilDone() { 2008 ready.set(true); 2009 while (!done.get()) { 2010 Thread.onSpinWait(); 2011 } 2012 } 2013 } 2014 2015 Foo foo = new Foo(); 2016 var thread = Thread.ofVirtual().start(foo::spinUntilDone); 2017 try { 2018 awaitTrue(ready); 2019 StackTraceElement[] stack = thread.getStackTrace(); 2020 assertTrue(contains(stack, Foo.class.getName() + ".spinUntilDone")); 2021 } finally { 2022 done.set(true); 2023 thread.join(); 2024 } 2025 } 2026 2027 /** 2028 * Test Thread::getStackTrace on thread that is runnable-unmounted. 2029 */ 2030 @Test 2031 void testGetStackTraceRunnableUnmounted() throws Exception { 2032 assumeTrue(VThreadScheduler.supportsCustomScheduler(), "No support for custom schedulers"); 2033 2034 // custom scheduler with one carrier thread 2035 try (ExecutorService scheduler = Executors.newFixedThreadPool(1)) { 2036 ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); 2037 2038 // start thread1 to park 2039 Thread thread1 = factory.newThread(LockSupport::park); 2040 thread1.start(); 2041 await(thread1, Thread.State.WAITING); 2042 2043 // start thread2 to spin and pin the carrier thread 2044 var started = new AtomicBoolean(); 2045 var done = new AtomicBoolean(); 2046 Thread thread2 = factory.newThread(() -> { 2047 started.set(true); 2048 while (!done.get()) { 2049 Thread.onSpinWait(); 2050 } 2051 }); 2052 thread2.start(); 2053 awaitTrue(started); 2054 2055 try { 2056 // unpark thread1, it should be "stuck" in runnable state 2057 // (the carrier thread is pinned, no other virtual thread can run) 2058 LockSupport.unpark(thread1); 2059 assertEquals(Thread.State.RUNNABLE, thread1.getState()); 2060 2061 // print thread1's stack trace 2062 StackTraceElement[] stack = thread1.getStackTrace(); 2063 assertTrue(contains(stack, "LockSupport.park")); 2064 2065 } finally { 2066 done.set(true); 2067 } 2068 } 2069 } 2070 2071 /** 2072 * Test Thread::getStackTrace on thread blocked on monitor enter. 2073 */ 2074 @ParameterizedTest 2075 @ValueSource(booleans = { true, false }) 2076 void testGetStackTraceBlocked(boolean pinned) throws Exception { 2077 class Foo { 2078 void enter() { 2079 synchronized (this) { } 2080 } 2081 } 2082 Foo foo = new Foo(); 2083 var ready = new AtomicBoolean(); 2084 var thread = Thread.ofVirtual().unstarted(() -> { 2085 if (pinned) { 2086 VThreadPinner.runPinned(() -> { 2087 ready.set(true); 2088 foo.enter(); 2089 }); 2090 } else { 2091 ready.set(true); 2092 foo.enter(); 2093 } 2094 }); 2095 synchronized (foo) { 2096 thread.start(); 2097 awaitTrue(ready); 2098 2099 // wait for thread to block 2100 await(thread, Thread.State.BLOCKED); 2101 2102 StackTraceElement[] stack = thread.getStackTrace(); 2103 assertTrue(contains(stack, Foo.class.getName() + ".enter")); 2104 } 2105 thread.join(); 2106 } 2107 2108 /** 2109 * Test Thread::getStackTrace when thread is waiting in Object.wait. 2110 */ 2111 @ParameterizedTest 2112 @ValueSource(booleans = { true, false }) 2113 void testGetStackTraceWaiting(boolean pinned) throws Exception { 2114 var ready = new AtomicBoolean(); 2115 var thread = Thread.ofVirtual().start(() -> { 2116 synchronized (lock) { 2117 try { 2118 if (pinned) { 2119 VThreadPinner.runPinned(() -> { 2120 ready.set(true); 2121 lock.wait(); 2122 }); 2123 } else { 2124 ready.set(true); 2125 lock.wait(); 2126 } 2127 } catch (InterruptedException e) { } 2128 } 2129 }); 2130 try { 2131 // wait for thread to wait 2132 awaitTrue(ready); 2133 await(thread, Thread.State.WAITING); 2134 2135 StackTraceElement[] stack = thread.getStackTrace(); 2136 assertTrue(contains(stack, "Object.wait")); 2137 } finally { 2138 thread.interrupt(); 2139 thread.join(); 2140 } 2141 } 2142 2143 /** 2144 * Test Thread::getStackTrace when thread is waiting in timed-Object.wait. 2145 */ 2146 @ParameterizedTest 2147 @ValueSource(booleans = { true, false }) 2148 void testGetStackTraceTimedWaiting(boolean pinned) throws Exception { 2149 var ready = new AtomicBoolean(); 2150 var thread = Thread.ofVirtual().start(() -> { 2151 synchronized (lock) { 2152 try { 2153 if (pinned) { 2154 VThreadPinner.runPinned(() -> { 2155 ready.set(true); 2156 lock.wait(Long.MAX_VALUE); 2157 }); 2158 } else { 2159 ready.set(true); 2160 lock.wait(Long.MAX_VALUE); 2161 } 2162 } catch (InterruptedException e) { } 2163 } 2164 }); 2165 try { 2166 // wait for thread to wait 2167 awaitTrue(ready); 2168 await(thread, Thread.State.TIMED_WAITING); 2169 2170 StackTraceElement[] stack = thread.getStackTrace(); 2171 assertTrue(contains(stack, "Object.wait")); 2172 } finally { 2173 thread.interrupt(); 2174 thread.join(); 2175 } 2176 } 2177 2178 /** 2179 * Test Thread::getStackTrace when thread in park. 2180 */ 2181 @ParameterizedTest 2182 @ValueSource(booleans = { true, false }) 2183 void testGetStackTraceParked(boolean pinned) throws Exception { 2184 var ready = new AtomicBoolean(); 2185 var done = new AtomicBoolean(); 2186 var thread = Thread.ofVirtual().start(() -> { 2187 if (pinned) { 2188 VThreadPinner.runPinned(() -> { 2189 ready.set(true); 2190 while (!done.get()) { 2191 LockSupport.park(); 2192 } 2193 }); 2194 } else { 2195 ready.set(true); 2196 while (!done.get()) { 2197 LockSupport.park(); 2198 } 2199 } 2200 }); 2201 try { 2202 // wait for thread to park 2203 awaitTrue(ready); 2204 await(thread, Thread.State.WAITING); 2205 2206 StackTraceElement[] stack = thread.getStackTrace(); 2207 assertTrue(contains(stack, "LockSupport.park")); 2208 } finally { 2209 done.set(true); 2210 LockSupport.unpark(thread); 2211 thread.join(); 2212 } 2213 } 2214 2215 /** 2216 * Test Thread::getStackTrace when thread in timed-park. 2217 */ 2218 @ParameterizedTest 2219 @ValueSource(booleans = { true, false }) 2220 void testGetStackTraceTimedPark(boolean pinned) throws Exception { 2221 var ready = new AtomicBoolean(); 2222 var done = new AtomicBoolean(); 2223 var thread = Thread.ofVirtual().start(() -> { 2224 if (pinned) { 2225 ready.set(true); 2226 VThreadPinner.runPinned(() -> { 2227 while (!done.get()) { 2228 LockSupport.parkNanos(Long.MAX_VALUE); 2229 } 2230 }); 2231 } else { 2232 ready.set(true); 2233 while (!done.get()) { 2234 LockSupport.parkNanos(Long.MAX_VALUE); 2235 } 2236 } 2237 }); 2238 try { 2239 // wait for thread to park 2240 awaitTrue(ready); 2241 await(thread, Thread.State.TIMED_WAITING); 2242 2243 StackTraceElement[] stack = thread.getStackTrace(); 2244 assertTrue(contains(stack, "LockSupport.parkNanos")); 2245 } finally { 2246 done.set(true); 2247 LockSupport.unpark(thread); 2248 thread.join(); 2249 } 2250 } 2251 2252 /** 2253 * Test Thread::getStackTrace on terminated thread. 2254 */ 2255 @Test 2256 void testGetStackTraceTerminated() throws Exception { 2257 var thread = Thread.ofVirtual().start(() -> { }); 2258 thread.join(); 2259 StackTraceElement[] stack = thread.getStackTrace(); 2260 assertTrue(stack.length == 0); 2261 } 2262 2263 /** 2264 * Test that Thread.getAllStackTraces does not include virtual threads. 2265 */ 2266 @Test 2267 void testGetAllStackTraces1() throws Exception { 2268 VThreadRunner.run(() -> { 2269 Set<Thread> threads = Thread.getAllStackTraces().keySet(); 2270 assertFalse(threads.stream().anyMatch(Thread::isVirtual)); 2271 }); 2272 } 2273 2274 /** 2275 * Test that Thread.getAllStackTraces includes carrier threads. 2276 */ 2277 @Test 2278 void testGetAllStackTraces2() throws Exception { 2279 assumeTrue(VThreadScheduler.supportsCustomScheduler(), "No support for custom schedulers"); 2280 try (ForkJoinPool pool = new ForkJoinPool(1)) { 2281 AtomicReference<Thread> ref = new AtomicReference<>(); 2282 Executor scheduler = task -> { 2283 pool.submit(() -> { 2284 ref.set(Thread.currentThread()); 2285 task.run(); 2286 }); 2287 }; 2288 2289 ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); 2290 Thread vthread = factory.newThread(() -> { 2291 synchronized (lock) { 2292 try { 2293 lock.wait(); 2294 } catch (Exception e) { } 2295 } 2296 }); 2297 vthread.start(); 2298 2299 // get carrier Thread 2300 Thread carrier; 2301 while ((carrier = ref.get()) == null) { 2302 Thread.sleep(20); 2303 } 2304 2305 // wait for virtual thread to block in wait 2306 await(vthread, Thread.State.WAITING); 2307 2308 // get all stack traces 2309 Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces(); 2310 2311 // allow virtual thread to terminate 2312 synchronized (lock) { 2313 lock.notifyAll(); 2314 } 2315 vthread.join(); 2316 2317 // stack trace for the carrier thread 2318 StackTraceElement[] stackTrace = map.get(carrier); 2319 assertNotNull(stackTrace); 2320 assertTrue(contains(stackTrace, "java.util.concurrent.ForkJoinPool")); 2321 assertFalse(contains(stackTrace, "java.lang.Object.wait")); 2322 2323 // there should be no stack trace for the virtual thread 2324 assertNull(map.get(vthread)); 2325 } 2326 } 2327 2328 private boolean contains(StackTraceElement[] stack, String expected) { 2329 return Stream.of(stack) 2330 .map(Object::toString) 2331 .anyMatch(s -> s.contains(expected)); 2332 } 2333 2334 /** 2335 * Test Thread::getThreadGroup on virtual thread created by platform thread. 2336 */ 2337 @Test 2338 void testThreadGroup1() throws Exception { 2339 var thread = Thread.ofVirtual().unstarted(LockSupport::park); 2340 var vgroup = thread.getThreadGroup(); 2341 thread.start(); 2342 try { 2343 assertEquals(vgroup, thread.getThreadGroup()); 2344 } finally { 2345 LockSupport.unpark(thread); 2346 thread.join(); 2347 } 2348 assertNull(thread.getThreadGroup()); 2349 } 2350 2351 /** 2352 * Test Thread::getThreadGroup on platform thread created by virtual thread. 2353 */ 2354 @Test 2355 void testThreadGroup2() throws Exception { 2356 VThreadRunner.run(() -> { 2357 ThreadGroup vgroup = Thread.currentThread().getThreadGroup(); 2358 Thread child = new Thread(() -> { }); 2359 ThreadGroup group = child.getThreadGroup(); 2360 assertEquals(vgroup, group); 2361 }); 2362 } 2363 2364 /** 2365 * Test ThreadGroup returned by Thread::getThreadGroup and subgroup 2366 * created with 2-arg ThreadGroup constructor. 2367 */ 2368 @Test 2369 void testThreadGroup3() throws Exception { 2370 var ref = new AtomicReference<ThreadGroup>(); 2371 var thread = Thread.startVirtualThread(() -> { 2372 ref.set(Thread.currentThread().getThreadGroup()); 2373 }); 2374 thread.join(); 2375 2376 ThreadGroup vgroup = ref.get(); 2377 assertEquals(Thread.MAX_PRIORITY, vgroup.getMaxPriority()); 2378 2379 ThreadGroup group = new ThreadGroup(vgroup, "group"); 2380 assertTrue(group.getParent() == vgroup); 2381 assertEquals(Thread.MAX_PRIORITY, group.getMaxPriority()); 2382 2383 vgroup.setMaxPriority(Thread.MAX_PRIORITY - 1); 2384 assertEquals(Thread.MAX_PRIORITY, vgroup.getMaxPriority()); 2385 assertEquals(Thread.MAX_PRIORITY - 1, group.getMaxPriority()); 2386 2387 vgroup.setMaxPriority(Thread.MIN_PRIORITY); 2388 assertEquals(Thread.MAX_PRIORITY, vgroup.getMaxPriority()); 2389 assertEquals(Thread.MIN_PRIORITY, group.getMaxPriority()); 2390 } 2391 2392 /** 2393 * Test ThreadGroup returned by Thread::getThreadGroup and subgroup 2394 * created with 1-arg ThreadGroup constructor. 2395 */ 2396 @Test 2397 void testThreadGroup4() throws Exception { 2398 VThreadRunner.run(() -> { 2399 ThreadGroup vgroup = Thread.currentThread().getThreadGroup(); 2400 assertEquals(Thread.MAX_PRIORITY, vgroup.getMaxPriority()); 2401 2402 ThreadGroup group = new ThreadGroup("group"); 2403 assertEquals(vgroup, group.getParent()); 2404 assertEquals(Thread.MAX_PRIORITY, group.getMaxPriority()); 2405 2406 vgroup.setMaxPriority(Thread.MAX_PRIORITY - 1); 2407 assertEquals(Thread.MAX_PRIORITY, vgroup.getMaxPriority()); 2408 assertEquals(Thread.MAX_PRIORITY - 1, group.getMaxPriority()); 2409 2410 vgroup.setMaxPriority(Thread.MIN_PRIORITY); 2411 assertEquals(Thread.MAX_PRIORITY, vgroup.getMaxPriority()); 2412 assertEquals(Thread.MIN_PRIORITY, group.getMaxPriority()); 2413 }); 2414 } 2415 2416 /** 2417 * Test Thread.enumerate(false). 2418 */ 2419 @Test 2420 void testEnumerate1() throws Exception { 2421 VThreadRunner.run(() -> { 2422 ThreadGroup vgroup = Thread.currentThread().getThreadGroup(); 2423 Thread[] threads = new Thread[100]; 2424 int n = vgroup.enumerate(threads, /*recurse*/false); 2425 assertFalse(Arrays.stream(threads, 0, n).anyMatch(Thread::isVirtual)); 2426 }); 2427 } 2428 2429 /** 2430 * Test Thread.enumerate(true). 2431 */ 2432 @Test 2433 void testEnumerate2() throws Exception { 2434 VThreadRunner.run(() -> { 2435 ThreadGroup vgroup = Thread.currentThread().getThreadGroup(); 2436 Thread[] threads = new Thread[100]; 2437 int n = vgroup.enumerate(threads, /*recurse*/true); 2438 assertFalse(Arrays.stream(threads, 0, n).anyMatch(Thread::isVirtual)); 2439 }); 2440 } 2441 2442 /** 2443 * Test equals and hashCode. 2444 */ 2445 @Test 2446 void testEqualsAndHashCode() throws Exception { 2447 Thread vthread1 = Thread.ofVirtual().unstarted(LockSupport::park); 2448 Thread vthread2 = Thread.ofVirtual().unstarted(LockSupport::park); 2449 2450 // unstarted 2451 assertTrue(vthread1.equals(vthread1)); 2452 assertTrue(vthread2.equals(vthread2)); 2453 assertFalse(vthread1.equals(vthread2)); 2454 assertFalse(vthread2.equals(vthread1)); 2455 int hc1 = vthread1.hashCode(); 2456 int hc2 = vthread2.hashCode(); 2457 2458 vthread1.start(); 2459 vthread2.start(); 2460 try { 2461 // started, maybe running or parked 2462 assertTrue(vthread1.equals(vthread1)); 2463 assertTrue(vthread2.equals(vthread2)); 2464 assertFalse(vthread1.equals(vthread2)); 2465 assertFalse(vthread2.equals(vthread1)); 2466 assertTrue(vthread1.hashCode() == hc1); 2467 assertTrue(vthread2.hashCode() == hc2); 2468 } finally { 2469 LockSupport.unpark(vthread1); 2470 LockSupport.unpark(vthread2); 2471 } 2472 vthread1.join(); 2473 vthread2.join(); 2474 2475 // terminated 2476 assertTrue(vthread1.equals(vthread1)); 2477 assertTrue(vthread2.equals(vthread2)); 2478 assertFalse(vthread1.equals(vthread2)); 2479 assertFalse(vthread2.equals(vthread1)); 2480 assertTrue(vthread1.hashCode() == hc1); 2481 assertTrue(vthread2.hashCode() == hc2); 2482 } 2483 2484 /** 2485 * Test toString on unstarted thread. 2486 */ 2487 @Test 2488 void testToString1() { 2489 Thread thread = Thread.ofVirtual().unstarted(() -> { }); 2490 thread.setName("fred"); 2491 assertTrue(thread.toString().contains("fred")); 2492 } 2493 2494 /** 2495 * Test toString on running thread. 2496 */ 2497 @Test 2498 void testToString2() throws Exception { 2499 VThreadRunner.run(() -> { 2500 Thread me = Thread.currentThread(); 2501 me.setName("fred"); 2502 assertTrue(me.toString().contains("fred")); 2503 }); 2504 } 2505 2506 /** 2507 * Test toString on parked thread. 2508 */ 2509 @Test 2510 void testToString3() throws Exception { 2511 Thread thread = Thread.ofVirtual().start(() -> { 2512 Thread me = Thread.currentThread(); 2513 me.setName("fred"); 2514 LockSupport.park(); 2515 }); 2516 await(thread, Thread.State.WAITING); 2517 try { 2518 assertTrue(thread.toString().contains("fred")); 2519 } finally { 2520 LockSupport.unpark(thread); 2521 thread.join(); 2522 } 2523 } 2524 2525 /** 2526 * Test toString on terminated thread. 2527 */ 2528 @Test 2529 void testToString4() throws Exception { 2530 Thread thread = Thread.ofVirtual().start(() -> { 2531 Thread me = Thread.currentThread(); 2532 me.setName("fred"); 2533 }); 2534 thread.join(); 2535 assertTrue(thread.toString().contains("fred")); 2536 } 2537 2538 /** 2539 * Thread.UncaughtExceptionHandler that captures the first exception thrown. 2540 */ 2541 private static class CapturingUHE implements Thread.UncaughtExceptionHandler { 2542 Thread thread; 2543 Throwable exception; 2544 @Override 2545 public void uncaughtException(Thread t, Throwable e) { 2546 synchronized (this) { 2547 if (thread == null) { 2548 this.thread = t; 2549 this.exception = e; 2550 } 2551 } 2552 } 2553 Thread thread() { 2554 synchronized (this) { 2555 return thread; 2556 } 2557 } 2558 Throwable exception() { 2559 synchronized (this) { 2560 return exception; 2561 } 2562 } 2563 } 2564 2565 /** 2566 * Waits for the boolean value to become true. 2567 */ 2568 private static void awaitTrue(AtomicBoolean ref) throws Exception { 2569 while (!ref.get()) { 2570 Thread.sleep(20); 2571 } 2572 } 2573 2574 /** 2575 * Waits for the given thread to reach a given state. 2576 */ 2577 private void await(Thread thread, Thread.State expectedState) throws InterruptedException { 2578 Thread.State state = thread.getState(); 2579 while (state != expectedState) { 2580 assertTrue(state != Thread.State.TERMINATED, "Thread has terminated"); 2581 Thread.sleep(10); 2582 state = thread.getState(); 2583 } 2584 } 2585 2586 /** 2587 * Schedule a thread to be interrupted after a delay. 2588 */ 2589 private void scheduleInterrupt(Thread thread, long delayInMillis) { 2590 scheduler.schedule(thread::interrupt, delayInMillis, TimeUnit.MILLISECONDS); 2591 } 2592 }