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 }