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