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