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