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