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