1 /*
  2  * Copyright (c) 2022, 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
 26  * @bug 8284161 8286788
 27  * @summary Test that a carrier thread waits on a virtual thread
 28  * @requires vm.continuations
 29  * @modules java.base/java.lang:+open
 30  * @run junit CarrierThreadWaits
 31  */
 32 
 33 /**
 34  * @test
 35  * @requires vm.continuations & vm.debug
 36  * @modules java.base/java.lang:+open
 37  * @run junit/othervm -XX:LockingMode=0 CarrierThreadWaits
 38  */
 39 
 40 import java.lang.management.LockInfo;
 41 import java.lang.management.ManagementFactory;
 42 import java.lang.management.ThreadInfo;
 43 import java.util.concurrent.Executor;
 44 import java.util.concurrent.ForkJoinPool;
 45 import java.util.concurrent.atomic.AtomicBoolean;
 46 import java.util.concurrent.atomic.AtomicReference;
 47 
 48 import org.junit.jupiter.api.Test;
 49 import static org.junit.jupiter.api.Assertions.*;
 50 
 51 class CarrierThreadWaits {
 52 
 53     @Test
 54     void testCarrierThreadWaiting() throws Exception {
 55         try (ForkJoinPool pool = new ForkJoinPool(1)) {
 56             var carrierRef = new AtomicReference<Thread>();
 57             var vthreadRef = new AtomicReference<Thread>();
 58 
 59             Executor scheduler = task -> {
 60                 pool.submit(() -> {
 61                     Thread carrier = Thread.currentThread();
 62                     carrierRef.set(carrier);
 63                     Thread vthread = vthreadRef.get();
 64 
 65                     System.err.format("%s run task (%s) ...%n", carrier, vthread);
 66                     task.run();
 67                     System.err.format("%s task done (%s)%n", carrier, vthread);
 68                 });
 69             };
 70 
 71             // start a virtual thread that spins and remains mounted until "done"
 72             var started = new AtomicBoolean();
 73             var done = new AtomicBoolean();
 74             Thread.Builder builder = ThreadBuilders.virtualThreadBuilder(scheduler);
 75             Thread vthread = builder.unstarted(() -> {
 76                 started.set(true);
 77                 while (!done.get()) {
 78                     Thread.onSpinWait();
 79                 }
 80             });
 81             vthreadRef.set(vthread);
 82             vthread.start();
 83 
 84             try {
 85                 // wait for virtual thread to start
 86                 while (!started.get()) {
 87                     Thread.sleep(10);
 88                 }
 89 
 90                 Thread carrier = carrierRef.get();
 91 
 92                 long carrierId = carrier.threadId();
 93                 long vthreadId = vthread.threadId();
 94 
 95                 // carrier thread should be on WAITING on virtual thread
 96                 ThreadInfo ti = ManagementFactory.getThreadMXBean().getThreadInfo(carrierId);
 97                 Thread.State state = ti.getThreadState();
 98                 LockInfo lockInfo = ti.getLockInfo();
 99                 assertEquals(Thread.State.WAITING, state);
100                 assertNotNull(lockInfo);
101                 assertEquals(vthread.getClass().getName(), lockInfo.getClassName());
102                 assertEquals(System.identityHashCode(vthread), lockInfo.getIdentityHashCode());
103                 assertEquals(vthreadId, ti.getLockOwnerId());
104             } finally {
105                 done.set(true);
106             }
107         }
108     }
109 
110 }