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