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