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 releases carrier when virtual thread holds a monitor. 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 lock = new Object(); 1118 var thread = factory.newThread(() -> { 1119 list.add("A"); 1120 var child = factory.newThread(() -> { 1121 list.add("B"); 1122 synchronized (lock) { 1123 Thread.yield(); 1124 } 1125 list.add("B"); 1126 }); 1127 child.start(); 1128 Thread.yield(); 1129 list.add("A"); 1130 try { child.join(); } catch (InterruptedException e) { } 1131 }); 1132 thread.start(); 1133 thread.join(); 1134 } 1135 assertEquals(List.of("A", "B", "A", "B"), list); 1136 } 1137 1138 /** 1139 * Test Thread.yield when thread is pinned by native frame. 1140 */ 1141 @Test 1142 void testYield3() throws Exception { 1143 assumeTrue(VThreadScheduler.supportsCustomScheduler(), "No support for custom schedulers"); 1144 var list = new CopyOnWriteArrayList<String>(); 1145 try (ExecutorService scheduler = Executors.newFixedThreadPool(1)) { 1146 ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); 1147 var thread = factory.newThread(() -> { 1148 list.add("A"); 1149 var child = factory.newThread(() -> { 1150 list.add("B"); 1151 }); 1152 child.start(); 1153 VThreadPinner.runPinned(() -> { 1154 Thread.yield(); // pinned so will be a no-op 1155 list.add("A"); 1156 }); 1157 try { child.join(); } catch (InterruptedException e) { } 1158 }); 1159 thread.start(); 1160 thread.join(); 1161 } 1162 assertEquals(List.of("A", "A", "B"), list); 1163 } 1164 1165 /** 1166 * Test Thread.yield does not consume the thread's parking permit. 1167 */ 1168 @Test 1169 void testYield4() throws Exception { 1170 var thread = Thread.ofVirtual().start(() -> { 1171 LockSupport.unpark(Thread.currentThread()); 1172 Thread.yield(); 1173 LockSupport.park(); // should not park 1174 }); 1175 thread.join(); 1176 } 1177 1178 /** 1179 * Test Thread.yield does not make available the thread's parking permit. 1180 */ 1181 @Test 1182 void testYield5() throws Exception { 1183 var thread = Thread.ofVirtual().start(() -> { 1184 Thread.yield(); 1185 LockSupport.park(); // should park 1186 }); 1187 try { 1188 await(thread, Thread.State.WAITING); 1189 } finally { 1190 LockSupport.unpark(thread); 1191 thread.join(); 1192 } 1193 } 1194 1195 /** 1196 * Test Thread.onSpinWait. 1197 */ 1198 @Test 1199 void testOnSpinWait() throws Exception { 1200 VThreadRunner.run(() -> { 1201 Thread me = Thread.currentThread(); 1202 Thread.onSpinWait(); 1203 assertTrue(Thread.currentThread() == me); 1204 }); 1205 } 1206 1207 /** 1208 * Test Thread.sleep(-1). 1209 */ 1210 @Test 1211 void testSleep1() throws Exception { 1212 VThreadRunner.run(() -> { 1213 assertThrows(IllegalArgumentException.class, () -> Thread.sleep(-1)); 1214 assertThrows(IllegalArgumentException.class, () -> Thread.sleep(-1, 0)); 1215 assertThrows(IllegalArgumentException.class, () -> Thread.sleep(0, -1)); 1216 assertThrows(IllegalArgumentException.class, () -> Thread.sleep(0, 1_000_000)); 1217 }); 1218 VThreadRunner.run(() -> Thread.sleep(Duration.ofMillis(-1))); 1219 } 1220 1221 /** 1222 * Test Thread.sleep(0). 1223 */ 1224 @Test 1225 void testSleep2() throws Exception { 1226 VThreadRunner.run(() -> Thread.sleep(0)); 1227 VThreadRunner.run(() -> Thread.sleep(0, 0)); 1228 VThreadRunner.run(() -> Thread.sleep(Duration.ofMillis(0))); 1229 } 1230 1231 /** 1232 * Tasks that sleep for 1 second. 1233 */ 1234 static Stream<ThrowingRunnable> oneSecondSleepers() { 1235 return Stream.of( 1236 () -> Thread.sleep(1000), 1237 () -> Thread.sleep(Duration.ofSeconds(1)) 1238 ); 1239 } 1240 1241 /** 1242 * Test Thread.sleep duration. 1243 */ 1244 @ParameterizedTest 1245 @MethodSource("oneSecondSleepers") 1246 void testSleep3(ThrowingRunnable sleeper) throws Exception { 1247 VThreadRunner.run(() -> { 1248 long start = millisTime(); 1249 sleeper.run(); 1250 expectDuration(start, /*min*/900, /*max*/20_000); 1251 }); 1252 } 1253 1254 /** 1255 * Tasks that sleep for zero or longer duration. 1256 */ 1257 static Stream<ThrowingRunnable> sleepers() { 1258 return Stream.of( 1259 () -> Thread.sleep(0), 1260 () -> Thread.sleep(0, 0), 1261 () -> Thread.sleep(1000), 1262 () -> Thread.sleep(1000, 0), 1263 () -> Thread.sleep(Duration.ofMillis(0)), 1264 () -> Thread.sleep(Duration.ofMillis(1000)) 1265 ); 1266 } 1267 1268 /** 1269 * Test Thread.sleep with interrupt status set. 1270 */ 1271 @ParameterizedTest 1272 @MethodSource("sleepers") 1273 void testSleep4(ThrowingRunnable sleeper) throws Exception { 1274 VThreadRunner.run(() -> { 1275 Thread me = Thread.currentThread(); 1276 me.interrupt(); 1277 try { 1278 sleeper.run(); 1279 fail("sleep was not interrupted"); 1280 } catch (InterruptedException e) { 1281 // expected 1282 assertFalse(me.isInterrupted()); 1283 } 1284 }); 1285 } 1286 1287 /** 1288 * Test Thread.sleep with interrupt status set and a negative duration. 1289 */ 1290 @Test 1291 void testSleep4() throws Exception { 1292 VThreadRunner.run(() -> { 1293 Thread me = Thread.currentThread(); 1294 me.interrupt(); 1295 Thread.sleep(Duration.ofMillis(-1000)); // does nothing 1296 assertTrue(me.isInterrupted()); 1297 }); 1298 } 1299 1300 /** 1301 * Tasks that sleep for a long time. 1302 */ 1303 static Stream<ThrowingRunnable> longSleepers() { 1304 return Stream.of( 1305 () -> Thread.sleep(20_000), 1306 () -> Thread.sleep(20_000, 0), 1307 () -> Thread.sleep(Duration.ofSeconds(20)) 1308 ); 1309 } 1310 1311 /** 1312 * Test interrupting Thread.sleep. 1313 */ 1314 @ParameterizedTest 1315 @MethodSource("longSleepers") 1316 void testSleep5(ThrowingRunnable sleeper) throws Exception { 1317 VThreadRunner.run(() -> { 1318 Thread t = Thread.currentThread(); 1319 scheduleInterrupt(t, 100); 1320 try { 1321 sleeper.run(); 1322 fail("sleep was not interrupted"); 1323 } catch (InterruptedException e) { 1324 // interrupt status should be cleared 1325 assertFalse(t.isInterrupted()); 1326 } 1327 }); 1328 } 1329 1330 /** 1331 * Test that Thread.sleep does not disrupt parking permit. 1332 */ 1333 @Test 1334 void testSleep6() throws Exception { 1335 VThreadRunner.run(() -> { 1336 LockSupport.unpark(Thread.currentThread()); 1337 1338 long start = millisTime(); 1339 Thread.sleep(1000); 1340 expectDuration(start, /*min*/900, /*max*/20_000); 1341 1342 // check that parking permit was not consumed 1343 LockSupport.park(); 1344 }); 1345 } 1346 1347 /** 1348 * Test that Thread.sleep is not disrupted by unparking thread. 1349 */ 1350 @Test 1351 void testSleep7() throws Exception { 1352 AtomicReference<Exception> exc = new AtomicReference<>(); 1353 var thread = Thread.ofVirtual().start(() -> { 1354 try { 1355 long start = millisTime(); 1356 Thread.sleep(1000); 1357 expectDuration(start, /*min*/900, /*max*/20_000); 1358 } catch (Exception e) { 1359 exc.set(e); 1360 } 1361 1362 }); 1363 // attempt to disrupt sleep 1364 for (int i = 0; i < 5; i++) { 1365 Thread.sleep(20); 1366 LockSupport.unpark(thread); 1367 } 1368 thread.join(); 1369 Exception e = exc.get(); 1370 if (e != null) { 1371 throw e; 1372 } 1373 } 1374 1375 /** 1376 * Test Thread.sleep when pinned. 1377 */ 1378 @Test 1379 void testSleep8() throws Exception { 1380 VThreadPinner.runPinned(() -> { 1381 long start = millisTime(); 1382 Thread.sleep(1000); 1383 expectDuration(start, /*min*/900, /*max*/20_000); 1384 }); 1385 } 1386 1387 /** 1388 * Test Thread.sleep when pinned and with interrupt status set. 1389 */ 1390 @Test 1391 void testSleep9() throws Exception { 1392 VThreadRunner.run(() -> { 1393 Thread me = Thread.currentThread(); 1394 me.interrupt(); 1395 try { 1396 VThreadPinner.runPinned(() -> { 1397 Thread.sleep(2000); 1398 }); 1399 fail("sleep not interrupted"); 1400 } catch (InterruptedException e) { 1401 // expected 1402 assertFalse(me.isInterrupted()); 1403 } 1404 }); 1405 } 1406 1407 /** 1408 * Test interrupting Thread.sleep when pinned. 1409 */ 1410 @Test 1411 void testSleep10() throws Exception { 1412 VThreadRunner.run(() -> { 1413 Thread t = Thread.currentThread(); 1414 scheduleInterrupt(t, 100); 1415 try { 1416 VThreadPinner.runPinned(() -> { 1417 Thread.sleep(20 * 1000); 1418 }); 1419 fail("sleep not interrupted"); 1420 } catch (InterruptedException e) { 1421 // interrupt status should be cleared 1422 assertFalse(t.isInterrupted()); 1423 } 1424 }); 1425 } 1426 1427 /** 1428 * Test Thread.sleep(null). 1429 */ 1430 @Test 1431 void testSleep11() throws Exception { 1432 assertThrows(NullPointerException.class, () -> Thread.sleep(null)); 1433 VThreadRunner.run(() -> { 1434 assertThrows(NullPointerException.class, () -> Thread.sleep(null)); 1435 }); 1436 } 1437 1438 /** 1439 * Returns the current time in milliseconds. 1440 */ 1441 private static long millisTime() { 1442 long now = System.nanoTime(); 1443 return TimeUnit.MILLISECONDS.convert(now, TimeUnit.NANOSECONDS); 1444 } 1445 1446 /** 1447 * Check the duration of a task 1448 * @param start start time, in milliseconds 1449 * @param min minimum expected duration, in milliseconds 1450 * @param max maximum expected duration, in milliseconds 1451 * @return the duration (now - start), in milliseconds 1452 */ 1453 private static void expectDuration(long start, long min, long max) { 1454 long duration = millisTime() - start; 1455 assertTrue(duration >= min, 1456 "Duration " + duration + "ms, expected >= " + min + "ms"); 1457 assertTrue(duration <= max, 1458 "Duration " + duration + "ms, expected <= " + max + "ms"); 1459 } 1460 1461 /** 1462 * Test Thread.xxxContextClassLoader from the current thread. 1463 */ 1464 @Test 1465 void testContextClassLoader1() throws Exception { 1466 ClassLoader loader = new ClassLoader() { }; 1467 VThreadRunner.run(() -> { 1468 Thread t = Thread.currentThread(); 1469 t.setContextClassLoader(loader); 1470 assertTrue(t.getContextClassLoader() == loader); 1471 }); 1472 } 1473 1474 /** 1475 * Test inheriting initial value of TCCL from platform thread. 1476 */ 1477 @Test 1478 void testContextClassLoader2() throws Exception { 1479 ClassLoader loader = new ClassLoader() { }; 1480 Thread t = Thread.currentThread(); 1481 ClassLoader savedLoader = t.getContextClassLoader(); 1482 t.setContextClassLoader(loader); 1483 try { 1484 VThreadRunner.run(() -> { 1485 assertTrue(Thread.currentThread().getContextClassLoader() == loader); 1486 }); 1487 } finally { 1488 t.setContextClassLoader(savedLoader); 1489 } 1490 } 1491 1492 /** 1493 * Test inheriting initial value of TCCL from virtual thread. 1494 */ 1495 @Test 1496 void testContextClassLoader3() throws Exception { 1497 VThreadRunner.run(() -> { 1498 ClassLoader loader = new ClassLoader() { }; 1499 Thread.currentThread().setContextClassLoader(loader); 1500 VThreadRunner.run(() -> { 1501 assertTrue(Thread.currentThread().getContextClassLoader() == loader); 1502 }); 1503 }); 1504 } 1505 1506 /** 1507 * Test inheriting initial value of TCCL through an intermediate virtual thread. 1508 */ 1509 @Test 1510 void testContextClassLoader4() throws Exception { 1511 ClassLoader loader = new ClassLoader() { }; 1512 Thread t = Thread.currentThread(); 1513 ClassLoader savedLoader = t.getContextClassLoader(); 1514 t.setContextClassLoader(loader); 1515 try { 1516 VThreadRunner.run(() -> { 1517 VThreadRunner.run(() -> { 1518 assertTrue(Thread.currentThread().getContextClassLoader() == loader); 1519 }); 1520 }); 1521 } finally { 1522 t.setContextClassLoader(savedLoader); 1523 } 1524 } 1525 1526 /** 1527 * Test Thread.xxxContextClassLoader when thread does not inherit the 1528 * initial value of inheritable thread locals. 1529 */ 1530 @Test 1531 void testContextClassLoader5() throws Exception { 1532 VThreadRunner.run(() -> { 1533 ClassLoader loader = new ClassLoader() { }; 1534 Thread.currentThread().setContextClassLoader(loader); 1535 int characteristics = VThreadRunner.NO_INHERIT_THREAD_LOCALS; 1536 VThreadRunner.run(characteristics, () -> { 1537 Thread t = Thread.currentThread(); 1538 assertTrue(t.getContextClassLoader() == ClassLoader.getSystemClassLoader()); 1539 t.setContextClassLoader(loader); 1540 assertTrue(t.getContextClassLoader() == loader); 1541 }); 1542 }); 1543 } 1544 1545 /** 1546 * Test Thread.setUncaughtExceptionHandler. 1547 */ 1548 @Test 1549 void testUncaughtExceptionHandler1() throws Exception { 1550 class FooException extends RuntimeException { } 1551 var handler = new CapturingUHE(); 1552 Thread thread = Thread.ofVirtual().start(() -> { 1553 Thread me = Thread.currentThread(); 1554 assertTrue(me.getUncaughtExceptionHandler() == me.getThreadGroup()); 1555 me.setUncaughtExceptionHandler(handler); 1556 assertTrue(me.getUncaughtExceptionHandler() == handler); 1557 throw new FooException(); 1558 }); 1559 thread.join(); 1560 assertInstanceOf(FooException.class, handler.exception()); 1561 assertEquals(thread, handler.thread()); 1562 assertNull(thread.getUncaughtExceptionHandler()); 1563 } 1564 1565 /** 1566 * Test default UncaughtExceptionHandler. 1567 */ 1568 @Test 1569 void testUncaughtExceptionHandler2() throws Exception { 1570 class FooException extends RuntimeException { } 1571 var handler = new CapturingUHE(); 1572 Thread.UncaughtExceptionHandler savedHandler = Thread.getDefaultUncaughtExceptionHandler(); 1573 Thread.setDefaultUncaughtExceptionHandler(handler); 1574 Thread thread; 1575 try { 1576 thread = Thread.ofVirtual().start(() -> { 1577 Thread me = Thread.currentThread(); 1578 throw new FooException(); 1579 }); 1580 thread.join(); 1581 } finally { 1582 Thread.setDefaultUncaughtExceptionHandler(savedHandler); // restore 1583 } 1584 assertInstanceOf(FooException.class, handler.exception()); 1585 assertEquals(thread, handler.thread()); 1586 assertNull(thread.getUncaughtExceptionHandler()); 1587 } 1588 1589 /** 1590 * Test Thread and default UncaughtExceptionHandler set. 1591 */ 1592 @Test 1593 void testUncaughtExceptionHandler3() throws Exception { 1594 class FooException extends RuntimeException { } 1595 var defaultHandler = new CapturingUHE(); 1596 var threadHandler = new CapturingUHE(); 1597 Thread.UncaughtExceptionHandler savedHandler = Thread.getDefaultUncaughtExceptionHandler(); 1598 Thread.setDefaultUncaughtExceptionHandler(defaultHandler); 1599 Thread thread; 1600 try { 1601 thread = Thread.ofVirtual().start(() -> { 1602 Thread me = Thread.currentThread(); 1603 assertTrue(me.getUncaughtExceptionHandler() == me.getThreadGroup()); 1604 me.setUncaughtExceptionHandler(threadHandler); 1605 assertTrue(me.getUncaughtExceptionHandler() == threadHandler); 1606 throw new FooException(); 1607 }); 1608 thread.join(); 1609 } finally { 1610 Thread.setDefaultUncaughtExceptionHandler(savedHandler); // restore 1611 } 1612 assertInstanceOf(FooException.class, threadHandler.exception()); 1613 assertNull(defaultHandler.exception()); 1614 assertEquals(thread, threadHandler.thread()); 1615 assertNull(thread.getUncaughtExceptionHandler()); 1616 } 1617 1618 /** 1619 * Test no Thread or default UncaughtExceptionHandler set. 1620 */ 1621 @Test 1622 void testUncaughtExceptionHandler4() throws Exception { 1623 Thread.UncaughtExceptionHandler savedHandler = Thread.getDefaultUncaughtExceptionHandler(); 1624 Thread.setDefaultUncaughtExceptionHandler(null); 1625 try { 1626 class FooException extends RuntimeException { } 1627 Thread thread = Thread.ofVirtual().start(() -> { 1628 throw new FooException(); 1629 }); 1630 thread.join(); 1631 assertNull(thread.getUncaughtExceptionHandler()); 1632 } finally { 1633 Thread.setDefaultUncaughtExceptionHandler(savedHandler); 1634 } 1635 } 1636 1637 /** 1638 * Test Thread::threadId and getId. 1639 */ 1640 @Test 1641 void testThreadId1() throws Exception { 1642 record ThreadIds(long threadId, long id) { } 1643 var ref = new AtomicReference<ThreadIds>(); 1644 1645 Thread vthread = Thread.ofVirtual().unstarted(() -> { 1646 Thread thread = Thread.currentThread(); 1647 ref.set(new ThreadIds(thread.threadId(), thread.getId())); 1648 LockSupport.park(); 1649 }); 1650 1651 // unstarted 1652 long tid = vthread.threadId(); 1653 1654 // running 1655 ThreadIds tids; 1656 vthread.start(); 1657 try { 1658 while ((tids = ref.get()) == null) { 1659 Thread.sleep(10); 1660 } 1661 assertTrue(tids.threadId() == tid); 1662 assertTrue(tids.id() == tid); 1663 } finally { 1664 LockSupport.unpark(vthread); 1665 vthread.join(); 1666 } 1667 1668 // terminated 1669 assertTrue(vthread.threadId() == tid); 1670 assertTrue(vthread.getId() == tid); 1671 } 1672 1673 /** 1674 * Test that each Thread has a unique ID 1675 */ 1676 @Test 1677 void testThreadId2() throws Exception { 1678 // thread ID should be unique 1679 long tid1 = Thread.ofVirtual().unstarted(() -> { }).threadId(); 1680 long tid2 = Thread.ofVirtual().unstarted(() -> { }).threadId(); 1681 long tid3 = Thread.currentThread().threadId(); 1682 assertFalse(tid1 == tid2); 1683 assertFalse(tid1 == tid3); 1684 assertFalse(tid2 == tid3); 1685 } 1686 1687 /** 1688 * Test Thread::getState when thread is new/unstarted. 1689 */ 1690 @Test 1691 void testGetState1() { 1692 var thread = Thread.ofVirtual().unstarted(() -> { }); 1693 assertEquals(Thread.State.NEW, thread.getState()); 1694 } 1695 1696 /** 1697 * Test Thread::getState when thread is terminated. 1698 */ 1699 @Test 1700 void testGetState2() throws Exception { 1701 var thread = Thread.ofVirtual().start(() -> { }); 1702 thread.join(); 1703 assertEquals(Thread.State.TERMINATED, thread.getState()); 1704 } 1705 1706 /** 1707 * Test Thread::getState when thread is runnable (mounted). 1708 */ 1709 @Test 1710 void testGetState3() throws Exception { 1711 var started = new CountDownLatch(1); 1712 var done = new AtomicBoolean(); 1713 var thread = Thread.ofVirtual().start(() -> { 1714 started.countDown(); 1715 1716 // spin until done 1717 while (!done.get()) { 1718 Thread.onSpinWait(); 1719 } 1720 }); 1721 try { 1722 // wait for thread to start 1723 started.await(); 1724 1725 // thread should be runnable 1726 assertEquals(Thread.State.RUNNABLE, thread.getState()); 1727 } finally { 1728 done.set(true); 1729 thread.join(); 1730 } 1731 } 1732 1733 /** 1734 * Test Thread::getState when thread is runnable (not mounted). 1735 */ 1736 @Test 1737 void testGetState4() throws Exception { 1738 assumeTrue(VThreadScheduler.supportsCustomScheduler(), "No support for custom schedulers"); 1739 AtomicBoolean completed = new AtomicBoolean(); 1740 try (ExecutorService scheduler = Executors.newFixedThreadPool(1)) { 1741 ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); 1742 Thread thread1 = factory.newThread(() -> { 1743 Thread thread2 = factory.newThread(LockSupport::park); 1744 assertEquals(Thread.State.NEW, thread2.getState()); 1745 1746 // start t2 to make it runnable 1747 thread2.start(); 1748 try { 1749 assertEquals(Thread.State.RUNNABLE, thread2.getState()); 1750 1751 // yield to allow t2 to run and park 1752 Thread.yield(); 1753 assertEquals(Thread.State.WAITING, thread2.getState()); 1754 } finally { 1755 // unpark t2 to make it runnable again 1756 LockSupport.unpark(thread2); 1757 } 1758 1759 // t2 should be runnable (not mounted) 1760 assertEquals(Thread.State.RUNNABLE, thread2.getState()); 1761 1762 completed.set(true); 1763 }); 1764 thread1.start(); 1765 thread1.join(); 1766 } 1767 assertTrue(completed.get() == true); 1768 } 1769 1770 /** 1771 * Test Thread::getState when thread is blocked waiting to enter a monitor. 1772 */ 1773 @ParameterizedTest 1774 @ValueSource(booleans = { true, false }) 1775 void testGetState5(boolean pinned) throws Exception { 1776 var ready = new AtomicBoolean(); 1777 var thread = Thread.ofVirtual().unstarted(() -> { 1778 if (pinned) { 1779 VThreadPinner.runPinned(() -> { 1780 ready.set(true); 1781 synchronized (lock) { } 1782 }); 1783 } else { 1784 ready.set(true); 1785 synchronized (lock) { } 1786 } 1787 }); 1788 synchronized (lock) { 1789 thread.start(); 1790 awaitTrue(ready); 1791 1792 // wait for thread to block 1793 await(thread, Thread.State.BLOCKED); 1794 } 1795 thread.join(); 1796 } 1797 1798 /** 1799 * Test Thread::getState when thread is waiting in Object.wait. 1800 */ 1801 @ParameterizedTest 1802 @ValueSource(booleans = { true, false }) 1803 void testGetState6(boolean pinned) throws Exception { 1804 var ready = new AtomicBoolean(); 1805 var thread = Thread.ofVirtual().start(() -> { 1806 synchronized (lock) { 1807 try { 1808 if (pinned) { 1809 VThreadPinner.runPinned(() -> { 1810 ready.set(true); 1811 lock.wait(); 1812 }); 1813 } else { 1814 ready.set(true); 1815 lock.wait(); 1816 } 1817 } catch (InterruptedException e) { } 1818 } 1819 }); 1820 try { 1821 // wait for thread to wait 1822 awaitTrue(ready); 1823 await(thread, Thread.State.WAITING); 1824 1825 // notify, thread should block trying to reenter 1826 synchronized (lock) { 1827 lock.notifyAll(); 1828 await(thread, Thread.State.BLOCKED); 1829 } 1830 } finally { 1831 thread.interrupt(); 1832 thread.join(); 1833 } 1834 } 1835 1836 /** 1837 * Test Thread::getState when thread is waiting in Object.wait(millis). 1838 */ 1839 @ParameterizedTest 1840 @ValueSource(booleans = { true, false }) 1841 void testGetState7(boolean pinned) throws Exception { 1842 var ready = new AtomicBoolean(); 1843 var thread = Thread.ofVirtual().start(() -> { 1844 synchronized (lock) { 1845 try { 1846 if (pinned) { 1847 VThreadPinner.runPinned(() -> { 1848 ready.set(true); 1849 lock.wait(Long.MAX_VALUE); 1850 }); 1851 } else { 1852 ready.set(true); 1853 lock.wait(Long.MAX_VALUE); 1854 } 1855 } catch (InterruptedException e) { } 1856 } 1857 }); 1858 try { 1859 // wait for thread to timed-wait 1860 awaitTrue(ready); 1861 await(thread, Thread.State.TIMED_WAITING); 1862 1863 // notify, thread should block trying to reenter 1864 synchronized (lock) { 1865 lock.notifyAll(); 1866 await(thread, Thread.State.BLOCKED); 1867 } 1868 } finally { 1869 thread.interrupt(); 1870 thread.join(); 1871 } 1872 } 1873 1874 /** 1875 * Test Thread::getState when thread is parked. 1876 */ 1877 @Test 1878 void testGetState8() throws Exception { 1879 var thread = Thread.ofVirtual().start(LockSupport::park); 1880 try { 1881 await(thread, Thread.State.WAITING); 1882 } finally { 1883 LockSupport.unpark(thread); 1884 thread.join(); 1885 } 1886 } 1887 1888 /** 1889 * Test Thread::getState when thread is timed parked. 1890 */ 1891 @Test 1892 void testGetState9() throws Exception { 1893 var thread = Thread.ofVirtual().start(() -> LockSupport.parkNanos(Long.MAX_VALUE)); 1894 try { 1895 await(thread, Thread.State.TIMED_WAITING); 1896 } finally { 1897 LockSupport.unpark(thread); 1898 thread.join(); 1899 } 1900 } 1901 1902 /** 1903 * Test Thread::getState when thread is parked while holding a monitor. 1904 */ 1905 @Test 1906 void testGetState10() throws Exception { 1907 var started = new CountDownLatch(1); 1908 var done = new AtomicBoolean(); 1909 var thread = Thread.ofVirtual().start(() -> { 1910 started.countDown(); 1911 synchronized (lock) { 1912 while (!done.get()) { 1913 LockSupport.park(); 1914 } 1915 } 1916 }); 1917 try { 1918 // wait for thread to start 1919 started.await(); 1920 1921 // wait for thread to park 1922 await(thread, Thread.State.WAITING); 1923 } finally { 1924 done.set(true); 1925 LockSupport.unpark(thread); 1926 thread.join(); 1927 } 1928 } 1929 1930 /** 1931 * Test Thread::getState when thread is timed parked while holding a monitor. 1932 */ 1933 @Test 1934 void testGetState11() throws Exception { 1935 var started = new CountDownLatch(1); 1936 var done = new AtomicBoolean(); 1937 var thread = Thread.ofVirtual().start(() -> { 1938 started.countDown(); 1939 synchronized (lock) { 1940 while (!done.get()) { 1941 LockSupport.parkNanos(Long.MAX_VALUE); 1942 } 1943 } 1944 }); 1945 try { 1946 // wait for thread to start 1947 started.await(); 1948 1949 // wait for thread to park 1950 await(thread, Thread.State.TIMED_WAITING); 1951 } finally { 1952 done.set(true); 1953 LockSupport.unpark(thread); 1954 thread.join(); 1955 } 1956 } 1957 1958 /** 1959 * Test Thread::isAlive. 1960 */ 1961 @Test 1962 void testIsAlive1() throws Exception { 1963 // unstarted 1964 var thread = Thread.ofVirtual().unstarted(LockSupport::park); 1965 assertFalse(thread.isAlive()); 1966 1967 // started 1968 thread.start(); 1969 try { 1970 assertTrue(thread.isAlive()); 1971 } finally { 1972 LockSupport.unpark(thread); 1973 thread.join(); 1974 } 1975 1976 // terminated 1977 assertFalse(thread.isAlive()); 1978 } 1979 1980 /** 1981 * Test Thread.holdsLock when lock not held. 1982 */ 1983 @Test 1984 void testHoldsLock1() throws Exception { 1985 VThreadRunner.run(() -> { 1986 var lock = new Object(); 1987 assertFalse(Thread.holdsLock(lock)); 1988 }); 1989 } 1990 1991 /** 1992 * Test Thread.holdsLock when lock held. 1993 */ 1994 @Test 1995 void testHoldsLock2() throws Exception { 1996 VThreadRunner.run(() -> { 1997 var lock = new Object(); 1998 synchronized (lock) { 1999 assertTrue(Thread.holdsLock(lock)); 2000 } 2001 }); 2002 } 2003 2004 /** 2005 * Test Thread.holdsLock when lock held by carrier thread. 2006 */ 2007 @Disabled 2008 @Test 2009 void testHoldsLock3() throws Exception { 2010 assumeTrue(VThreadScheduler.supportsCustomScheduler(), "No support for custom schedulers"); 2011 2012 Object lock = new Object(); 2013 2014 // carrier thread runs all tasks while holding the lock 2015 ThreadFactory carrierThreadFactory = task -> Thread.ofPlatform().unstarted(() -> { 2016 synchronized (lock) { 2017 task.run(); 2018 } 2019 }); 2020 try (ExecutorService pool = Executors.newSingleThreadExecutor(carrierThreadFactory)) { 2021 Executor scheduler = task -> pool.submit(task::run); 2022 ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); 2023 2024 // start virtual that tests if it holds the lock 2025 var result = new AtomicReference<Boolean>(); 2026 Thread vthread = factory.newThread(() -> { 2027 result.set(Thread.holdsLock(lock)); 2028 }); 2029 vthread.start(); 2030 vthread.join(); 2031 boolean holdsLock = result.get(); 2032 assertFalse(holdsLock, "Thread.holdsLock should return false"); 2033 } 2034 } 2035 2036 /** 2037 * Test Thread::getStackTrace on unstarted thread. 2038 */ 2039 @Test 2040 void testGetStackTraceUnstarted() { 2041 var thread = Thread.ofVirtual().unstarted(() -> { }); 2042 StackTraceElement[] stack = thread.getStackTrace(); 2043 assertTrue(stack.length == 0); 2044 } 2045 2046 /** 2047 * Test Thread::getStackTrace on thread that has been started but has not run. 2048 */ 2049 @Test 2050 void testGetStackTraceStarted() throws Exception { 2051 assumeTrue(VThreadScheduler.supportsCustomScheduler(), "No support for custom schedulers"); 2052 Executor scheduler = task -> { }; 2053 ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); 2054 Thread thread = factory.newThread(() -> { }); 2055 thread.start(); 2056 StackTraceElement[] stack = thread.getStackTrace(); 2057 assertTrue(stack.length == 0); 2058 } 2059 2060 /** 2061 * Test Thread::getStackTrace on thread that is runnable-mounted. 2062 */ 2063 @Test 2064 void testGetStackTraceRunnableMounted() throws Exception { 2065 var ready = new AtomicBoolean(); 2066 var done = new AtomicBoolean(); 2067 2068 class Foo { 2069 void spinUntilDone() { 2070 ready.set(true); 2071 while (!done.get()) { 2072 Thread.onSpinWait(); 2073 } 2074 } 2075 } 2076 2077 Foo foo = new Foo(); 2078 var thread = Thread.ofVirtual().start(foo::spinUntilDone); 2079 try { 2080 awaitTrue(ready); 2081 StackTraceElement[] stack = thread.getStackTrace(); 2082 assertTrue(contains(stack, Foo.class.getName() + ".spinUntilDone")); 2083 } finally { 2084 done.set(true); 2085 thread.join(); 2086 } 2087 } 2088 2089 /** 2090 * Test Thread::getStackTrace on thread that is runnable-unmounted. 2091 */ 2092 @Test 2093 void testGetStackTraceRunnableUnmounted() throws Exception { 2094 assumeTrue(VThreadScheduler.supportsCustomScheduler(), "No support for custom schedulers"); 2095 2096 // custom scheduler with one carrier thread 2097 try (ExecutorService scheduler = Executors.newFixedThreadPool(1)) { 2098 ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); 2099 2100 // start thread1 to park 2101 Thread thread1 = factory.newThread(LockSupport::park); 2102 thread1.start(); 2103 await(thread1, Thread.State.WAITING); 2104 2105 // start thread2 to spin and pin the carrier thread 2106 var started = new AtomicBoolean(); 2107 var done = new AtomicBoolean(); 2108 Thread thread2 = factory.newThread(() -> { 2109 started.set(true); 2110 while (!done.get()) { 2111 Thread.onSpinWait(); 2112 } 2113 }); 2114 thread2.start(); 2115 awaitTrue(started); 2116 2117 try { 2118 // unpark thread1, it should be "stuck" in runnable state 2119 // (the carrier thread is pinned, no other virtual thread can run) 2120 LockSupport.unpark(thread1); 2121 assertEquals(Thread.State.RUNNABLE, thread1.getState()); 2122 2123 // print thread1's stack trace 2124 StackTraceElement[] stack = thread1.getStackTrace(); 2125 assertTrue(contains(stack, "LockSupport.park")); 2126 2127 } finally { 2128 done.set(true); 2129 } 2130 } 2131 } 2132 2133 /** 2134 * Test Thread::getStackTrace on thread blocked on monitor enter. 2135 */ 2136 @ParameterizedTest 2137 @ValueSource(booleans = { true, false }) 2138 void testGetStackTraceBlocked(boolean pinned) throws Exception { 2139 class Foo { 2140 void enter() { 2141 synchronized (this) { } 2142 } 2143 } 2144 Foo foo = new Foo(); 2145 var ready = new AtomicBoolean(); 2146 var thread = Thread.ofVirtual().unstarted(() -> { 2147 if (pinned) { 2148 VThreadPinner.runPinned(() -> { 2149 ready.set(true); 2150 foo.enter(); 2151 }); 2152 } else { 2153 ready.set(true); 2154 foo.enter(); 2155 } 2156 }); 2157 synchronized (foo) { 2158 thread.start(); 2159 awaitTrue(ready); 2160 2161 // wait for thread to block 2162 await(thread, Thread.State.BLOCKED); 2163 2164 StackTraceElement[] stack = thread.getStackTrace(); 2165 assertTrue(contains(stack, Foo.class.getName() + ".enter")); 2166 } 2167 thread.join(); 2168 } 2169 2170 /** 2171 * Test Thread::getStackTrace when thread is waiting in Object.wait. 2172 */ 2173 @ParameterizedTest 2174 @ValueSource(booleans = { true, false }) 2175 void testGetStackTraceWaiting(boolean pinned) throws Exception { 2176 var ready = new AtomicBoolean(); 2177 var thread = Thread.ofVirtual().start(() -> { 2178 synchronized (lock) { 2179 try { 2180 if (pinned) { 2181 VThreadPinner.runPinned(() -> { 2182 ready.set(true); 2183 lock.wait(); 2184 }); 2185 } else { 2186 ready.set(true); 2187 lock.wait(); 2188 } 2189 } catch (InterruptedException e) { } 2190 } 2191 }); 2192 try { 2193 // wait for thread to wait 2194 awaitTrue(ready); 2195 await(thread, Thread.State.WAITING); 2196 2197 StackTraceElement[] stack = thread.getStackTrace(); 2198 assertTrue(contains(stack, "Object.wait")); 2199 } finally { 2200 thread.interrupt(); 2201 thread.join(); 2202 } 2203 } 2204 2205 /** 2206 * Test Thread::getStackTrace when thread is waiting in timed-Object.wait. 2207 */ 2208 @ParameterizedTest 2209 @ValueSource(booleans = { true, false }) 2210 void testGetStackTraceTimedWaiting(boolean pinned) throws Exception { 2211 var ready = new AtomicBoolean(); 2212 var thread = Thread.ofVirtual().start(() -> { 2213 synchronized (lock) { 2214 try { 2215 if (pinned) { 2216 VThreadPinner.runPinned(() -> { 2217 ready.set(true); 2218 lock.wait(Long.MAX_VALUE); 2219 }); 2220 } else { 2221 ready.set(true); 2222 lock.wait(Long.MAX_VALUE); 2223 } 2224 } catch (InterruptedException e) { } 2225 } 2226 }); 2227 try { 2228 // wait for thread to wait 2229 awaitTrue(ready); 2230 await(thread, Thread.State.TIMED_WAITING); 2231 2232 StackTraceElement[] stack = thread.getStackTrace(); 2233 assertTrue(contains(stack, "Object.wait")); 2234 } finally { 2235 thread.interrupt(); 2236 thread.join(); 2237 } 2238 } 2239 2240 /** 2241 * Test Thread::getStackTrace when thread in park. 2242 */ 2243 @ParameterizedTest 2244 @ValueSource(booleans = { true, false }) 2245 void testGetStackTraceParked(boolean pinned) throws Exception { 2246 var ready = new AtomicBoolean(); 2247 var done = new AtomicBoolean(); 2248 var thread = Thread.ofVirtual().start(() -> { 2249 if (pinned) { 2250 VThreadPinner.runPinned(() -> { 2251 ready.set(true); 2252 while (!done.get()) { 2253 LockSupport.park(); 2254 } 2255 }); 2256 } else { 2257 ready.set(true); 2258 while (!done.get()) { 2259 LockSupport.park(); 2260 } 2261 } 2262 }); 2263 try { 2264 // wait for thread to park 2265 awaitTrue(ready); 2266 await(thread, Thread.State.WAITING); 2267 2268 StackTraceElement[] stack = thread.getStackTrace(); 2269 assertTrue(contains(stack, "LockSupport.park")); 2270 } finally { 2271 done.set(true); 2272 LockSupport.unpark(thread); 2273 thread.join(); 2274 } 2275 } 2276 2277 /** 2278 * Test Thread::getStackTrace when thread in timed-park. 2279 */ 2280 @ParameterizedTest 2281 @ValueSource(booleans = { true, false }) 2282 void testGetStackTraceTimedPark(boolean pinned) throws Exception { 2283 var ready = new AtomicBoolean(); 2284 var done = new AtomicBoolean(); 2285 var thread = Thread.ofVirtual().start(() -> { 2286 if (pinned) { 2287 ready.set(true); 2288 VThreadPinner.runPinned(() -> { 2289 while (!done.get()) { 2290 LockSupport.parkNanos(Long.MAX_VALUE); 2291 } 2292 }); 2293 } else { 2294 ready.set(true); 2295 while (!done.get()) { 2296 LockSupport.parkNanos(Long.MAX_VALUE); 2297 } 2298 } 2299 }); 2300 try { 2301 // wait for thread to park 2302 awaitTrue(ready); 2303 await(thread, Thread.State.TIMED_WAITING); 2304 2305 StackTraceElement[] stack = thread.getStackTrace(); 2306 assertTrue(contains(stack, "LockSupport.parkNanos")); 2307 } finally { 2308 done.set(true); 2309 LockSupport.unpark(thread); 2310 thread.join(); 2311 } 2312 } 2313 2314 /** 2315 * Test Thread::getStackTrace on terminated thread. 2316 */ 2317 @Test 2318 void testGetStackTraceTerminated() throws Exception { 2319 var thread = Thread.ofVirtual().start(() -> { }); 2320 thread.join(); 2321 StackTraceElement[] stack = thread.getStackTrace(); 2322 assertTrue(stack.length == 0); 2323 } 2324 2325 /** 2326 * Test that Thread.getAllStackTraces does not include virtual threads. 2327 */ 2328 @Test 2329 void testGetAllStackTraces1() throws Exception { 2330 VThreadRunner.run(() -> { 2331 Set<Thread> threads = Thread.getAllStackTraces().keySet(); 2332 assertFalse(threads.stream().anyMatch(Thread::isVirtual)); 2333 }); 2334 } 2335 2336 /** 2337 * Test that Thread.getAllStackTraces includes carrier threads. 2338 */ 2339 @Test 2340 void testGetAllStackTraces2() throws Exception { 2341 assumeTrue(VThreadScheduler.supportsCustomScheduler(), "No support for custom schedulers"); 2342 try (ForkJoinPool pool = new ForkJoinPool(1)) { 2343 AtomicReference<Thread> ref = new AtomicReference<>(); 2344 Executor scheduler = task -> { 2345 pool.submit(() -> { 2346 ref.set(Thread.currentThread()); 2347 task.run(); 2348 }); 2349 }; 2350 2351 ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler); 2352 Thread vthread = factory.newThread(() -> { 2353 synchronized (lock) { 2354 try { 2355 lock.wait(); 2356 } catch (Exception e) { } 2357 } 2358 }); 2359 vthread.start(); 2360 2361 // get carrier Thread 2362 Thread carrier; 2363 while ((carrier = ref.get()) == null) { 2364 Thread.sleep(20); 2365 } 2366 2367 // wait for virtual thread to block in wait 2368 await(vthread, Thread.State.WAITING); 2369 2370 // get all stack traces 2371 Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces(); 2372 2373 // allow virtual thread to terminate 2374 synchronized (lock) { 2375 lock.notifyAll(); 2376 } 2377 vthread.join(); 2378 2379 // stack trace for the carrier thread 2380 StackTraceElement[] stackTrace = map.get(carrier); 2381 assertNotNull(stackTrace); 2382 assertTrue(contains(stackTrace, "java.util.concurrent.ForkJoinPool")); 2383 assertFalse(contains(stackTrace, "java.lang.Object.wait")); 2384 2385 // there should be no stack trace for the virtual thread 2386 assertNull(map.get(vthread)); 2387 } 2388 } 2389 2390 private boolean contains(StackTraceElement[] stack, String expected) { 2391 return Stream.of(stack) 2392 .map(Object::toString) 2393 .anyMatch(s -> s.contains(expected)); 2394 } 2395 2396 /** 2397 * Test Thread::getThreadGroup on virtual thread created by platform thread. 2398 */ 2399 @Test 2400 void testThreadGroup1() throws Exception { 2401 var thread = Thread.ofVirtual().unstarted(LockSupport::park); 2402 var vgroup = thread.getThreadGroup(); 2403 thread.start(); 2404 try { 2405 assertEquals(vgroup, thread.getThreadGroup()); 2406 } finally { 2407 LockSupport.unpark(thread); 2408 thread.join(); 2409 } 2410 assertNull(thread.getThreadGroup()); 2411 } 2412 2413 /** 2414 * Test Thread::getThreadGroup on platform thread created by virtual thread. 2415 */ 2416 @Test 2417 void testThreadGroup2() throws Exception { 2418 VThreadRunner.run(() -> { 2419 ThreadGroup vgroup = Thread.currentThread().getThreadGroup(); 2420 Thread child = new Thread(() -> { }); 2421 ThreadGroup group = child.getThreadGroup(); 2422 assertEquals(vgroup, group); 2423 }); 2424 } 2425 2426 /** 2427 * Test ThreadGroup returned by Thread::getThreadGroup and subgroup 2428 * created with 2-arg ThreadGroup constructor. 2429 */ 2430 @Test 2431 void testThreadGroup3() throws Exception { 2432 var ref = new AtomicReference<ThreadGroup>(); 2433 var thread = Thread.startVirtualThread(() -> { 2434 ref.set(Thread.currentThread().getThreadGroup()); 2435 }); 2436 thread.join(); 2437 2438 ThreadGroup vgroup = ref.get(); 2439 assertEquals(Thread.MAX_PRIORITY, vgroup.getMaxPriority()); 2440 2441 ThreadGroup group = new ThreadGroup(vgroup, "group"); 2442 assertTrue(group.getParent() == vgroup); 2443 assertEquals(Thread.MAX_PRIORITY, group.getMaxPriority()); 2444 2445 vgroup.setMaxPriority(Thread.MAX_PRIORITY - 1); 2446 assertEquals(Thread.MAX_PRIORITY, vgroup.getMaxPriority()); 2447 assertEquals(Thread.MAX_PRIORITY - 1, group.getMaxPriority()); 2448 2449 vgroup.setMaxPriority(Thread.MIN_PRIORITY); 2450 assertEquals(Thread.MAX_PRIORITY, vgroup.getMaxPriority()); 2451 assertEquals(Thread.MIN_PRIORITY, group.getMaxPriority()); 2452 } 2453 2454 /** 2455 * Test ThreadGroup returned by Thread::getThreadGroup and subgroup 2456 * created with 1-arg ThreadGroup constructor. 2457 */ 2458 @Test 2459 void testThreadGroup4() throws Exception { 2460 VThreadRunner.run(() -> { 2461 ThreadGroup vgroup = Thread.currentThread().getThreadGroup(); 2462 assertEquals(Thread.MAX_PRIORITY, vgroup.getMaxPriority()); 2463 2464 ThreadGroup group = new ThreadGroup("group"); 2465 assertEquals(vgroup, group.getParent()); 2466 assertEquals(Thread.MAX_PRIORITY, group.getMaxPriority()); 2467 2468 vgroup.setMaxPriority(Thread.MAX_PRIORITY - 1); 2469 assertEquals(Thread.MAX_PRIORITY, vgroup.getMaxPriority()); 2470 assertEquals(Thread.MAX_PRIORITY - 1, group.getMaxPriority()); 2471 2472 vgroup.setMaxPriority(Thread.MIN_PRIORITY); 2473 assertEquals(Thread.MAX_PRIORITY, vgroup.getMaxPriority()); 2474 assertEquals(Thread.MIN_PRIORITY, group.getMaxPriority()); 2475 }); 2476 } 2477 2478 /** 2479 * Test Thread.enumerate(false). 2480 */ 2481 @Test 2482 void testEnumerate1() throws Exception { 2483 VThreadRunner.run(() -> { 2484 ThreadGroup vgroup = Thread.currentThread().getThreadGroup(); 2485 Thread[] threads = new Thread[100]; 2486 int n = vgroup.enumerate(threads, /*recurse*/false); 2487 assertFalse(Arrays.stream(threads, 0, n).anyMatch(Thread::isVirtual)); 2488 }); 2489 } 2490 2491 /** 2492 * Test Thread.enumerate(true). 2493 */ 2494 @Test 2495 void testEnumerate2() throws Exception { 2496 VThreadRunner.run(() -> { 2497 ThreadGroup vgroup = Thread.currentThread().getThreadGroup(); 2498 Thread[] threads = new Thread[100]; 2499 int n = vgroup.enumerate(threads, /*recurse*/true); 2500 assertFalse(Arrays.stream(threads, 0, n).anyMatch(Thread::isVirtual)); 2501 }); 2502 } 2503 2504 /** 2505 * Test equals and hashCode. 2506 */ 2507 @Test 2508 void testEqualsAndHashCode() throws Exception { 2509 Thread vthread1 = Thread.ofVirtual().unstarted(LockSupport::park); 2510 Thread vthread2 = Thread.ofVirtual().unstarted(LockSupport::park); 2511 2512 // unstarted 2513 assertTrue(vthread1.equals(vthread1)); 2514 assertTrue(vthread2.equals(vthread2)); 2515 assertFalse(vthread1.equals(vthread2)); 2516 assertFalse(vthread2.equals(vthread1)); 2517 int hc1 = vthread1.hashCode(); 2518 int hc2 = vthread2.hashCode(); 2519 2520 vthread1.start(); 2521 vthread2.start(); 2522 try { 2523 // started, maybe running or parked 2524 assertTrue(vthread1.equals(vthread1)); 2525 assertTrue(vthread2.equals(vthread2)); 2526 assertFalse(vthread1.equals(vthread2)); 2527 assertFalse(vthread2.equals(vthread1)); 2528 assertTrue(vthread1.hashCode() == hc1); 2529 assertTrue(vthread2.hashCode() == hc2); 2530 } finally { 2531 LockSupport.unpark(vthread1); 2532 LockSupport.unpark(vthread2); 2533 } 2534 vthread1.join(); 2535 vthread2.join(); 2536 2537 // terminated 2538 assertTrue(vthread1.equals(vthread1)); 2539 assertTrue(vthread2.equals(vthread2)); 2540 assertFalse(vthread1.equals(vthread2)); 2541 assertFalse(vthread2.equals(vthread1)); 2542 assertTrue(vthread1.hashCode() == hc1); 2543 assertTrue(vthread2.hashCode() == hc2); 2544 } 2545 2546 /** 2547 * Test toString on unstarted thread. 2548 */ 2549 @Test 2550 void testToString1() { 2551 Thread thread = Thread.ofVirtual().unstarted(() -> { }); 2552 thread.setName("fred"); 2553 assertTrue(thread.toString().contains("fred")); 2554 } 2555 2556 /** 2557 * Test toString on running thread. 2558 */ 2559 @Test 2560 void testToString2() throws Exception { 2561 VThreadRunner.run(() -> { 2562 Thread me = Thread.currentThread(); 2563 me.setName("fred"); 2564 assertTrue(me.toString().contains("fred")); 2565 }); 2566 } 2567 2568 /** 2569 * Test toString on parked thread. 2570 */ 2571 @Test 2572 void testToString3() throws Exception { 2573 Thread thread = Thread.ofVirtual().start(() -> { 2574 Thread me = Thread.currentThread(); 2575 me.setName("fred"); 2576 LockSupport.park(); 2577 }); 2578 await(thread, Thread.State.WAITING); 2579 try { 2580 assertTrue(thread.toString().contains("fred")); 2581 } finally { 2582 LockSupport.unpark(thread); 2583 thread.join(); 2584 } 2585 } 2586 2587 /** 2588 * Test toString on terminated thread. 2589 */ 2590 @Test 2591 void testToString4() throws Exception { 2592 Thread thread = Thread.ofVirtual().start(() -> { 2593 Thread me = Thread.currentThread(); 2594 me.setName("fred"); 2595 }); 2596 thread.join(); 2597 assertTrue(thread.toString().contains("fred")); 2598 } 2599 2600 /** 2601 * Thread.UncaughtExceptionHandler that captures the first exception thrown. 2602 */ 2603 private static class CapturingUHE implements Thread.UncaughtExceptionHandler { 2604 Thread thread; 2605 Throwable exception; 2606 @Override 2607 public void uncaughtException(Thread t, Throwable e) { 2608 synchronized (this) { 2609 if (thread == null) { 2610 this.thread = t; 2611 this.exception = e; 2612 } 2613 } 2614 } 2615 Thread thread() { 2616 synchronized (this) { 2617 return thread; 2618 } 2619 } 2620 Throwable exception() { 2621 synchronized (this) { 2622 return exception; 2623 } 2624 } 2625 } 2626 2627 /** 2628 * Waits for the boolean value to become true. 2629 */ 2630 private static void awaitTrue(AtomicBoolean ref) throws Exception { 2631 while (!ref.get()) { 2632 Thread.sleep(20); 2633 } 2634 } 2635 2636 /** 2637 * Waits for the given thread to reach a given state. 2638 */ 2639 private void await(Thread thread, Thread.State expectedState) throws InterruptedException { 2640 Thread.State state = thread.getState(); 2641 while (state != expectedState) { 2642 assertTrue(state != Thread.State.TERMINATED, "Thread has terminated"); 2643 Thread.sleep(10); 2644 state = thread.getState(); 2645 } 2646 } 2647 2648 /** 2649 * Schedule a thread to be interrupted after a delay. 2650 */ 2651 private void scheduleInterrupt(Thread thread, long delayInMillis) { 2652 scheduler.schedule(thread::interrupt, delayInMillis, TimeUnit.MILLISECONDS); 2653 } 2654 }