1 /*
  2  * Copyright (c) 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 8309612 8310584
 27  * @summary The test verifies that JVMTI GetThreadState function reports expected state
 28  *          for mounted (pinned) virtual thread and its carrier thread
 29  * @requires vm.jvmti
 30  * @requires vm.continuations
 31  * @run main/othervm/native
 32  *      -agentlib:GetThreadStateMountedTest
 33  *      GetThreadStateMountedTest
 34  */
 35 
 36 import java.util.concurrent.CountDownLatch;
 37 import java.util.concurrent.locks.LockSupport;
 38 
 39 /**
 40  * The test implements different scenarios to get desired JVMTI thread states.
 41  * For each scenario the test also checks states after carrier and virtual threads suspend/resume
 42  * and after virtual thread interrupt.
 43  * Special handling is required for WAITING state scenarios:
 44  * Spurious wakeups may cause unexpected thread state change and this causes test failure.
 45  * To avoid this, the test thread should be suspended (i.e. carrier and/or mounted virtual thread is suspended).
 46  */
 47 public class GetThreadStateMountedTest {
 48 
 49     static final int JVMTI_THREAD_STATE_RUNNABLE                 = 0x0004;
 50     static final int JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER = 0x0400;
 51     static final int JVMTI_THREAD_STATE_WAITING                  = 0x0080;
 52     static final int JVMTI_THREAD_STATE_WAITING_INDEFINITELY     = 0x0010;
 53     static final int JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT     = 0x0020;
 54     static final int JVMTI_THREAD_STATE_IN_OBJECT_WAIT           = 0x0100;
 55     static final int JVMTI_THREAD_STATE_SLEEPING                 = 0x0040;
 56     static final int JVMTI_THREAD_STATE_PARKED                   = 0x0200;
 57     static final int JVMTI_THREAD_STATE_IN_NATIVE                = 0x400000;
 58 
 59     static void runnable() throws Exception {
 60         TestStatus status = new TestStatus("JVMTI_THREAD_STATE_RUNNABLE");
 61         CountDownLatch ready = new CountDownLatch(1);
 62         final boolean[] stopFlag = new boolean[1];
 63         Thread vthread = createPinnedVThread(() -> {
 64             ready.countDown();
 65             int i = 0;
 66             while (!stopFlag[0]) {
 67                 if (i < 200) {
 68                     i++;
 69                 } else {
 70                     i = 0;
 71                 }
 72             }
 73         });
 74         vthread.start();
 75         ready.await();
 76         testThreadStates(vthread, false, true, JVMTI_THREAD_STATE_RUNNABLE);
 77         stopFlag[0] = true;
 78         status.print();
 79     }
 80 
 81     static void blockedOnMonitorEnter() throws Exception {
 82         // JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER
 83         // Thread is waiting to enter a synchronized block/method or,
 84         // after an Object.wait(), waiting to re-enter a synchronized block/method.
 85         TestStatus status = new TestStatus("JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER");
 86         CountDownLatch ready = new CountDownLatch(1);
 87         final Object syncObj = new Object();
 88         Thread vthread = createPinnedVThread(() -> {
 89             ready.countDown();
 90             synchronized (syncObj) {
 91             }
 92         });
 93         synchronized (syncObj) {
 94             vthread.start();
 95             ready.await();
 96             Thread.sleep(500); // wait some time to ensure the thread is blocked on monitor
 97             testThreadStates(vthread, false, true, JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER);
 98         }
 99         status.print();
100     }
101 
102     static void waiting(boolean withTimeout) throws Exception {
103         // JVMTI_THREAD_STATE_WAITING
104         // Thread is waiting.
105         // JVMTI_THREAD_STATE_WAITING_INDEFINITELY
106         // Thread is waiting without a timeout. For example, Object.wait().
107         // JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT
108         // Thread is waiting with a maximum time to wait specified. For example, Object.wait(long).
109         TestStatus status = new TestStatus(withTimeout
110                                            ? "JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT"
111                                            : "JVMTI_THREAD_STATE_WAITING_INDEFINITELY");
112         CountDownLatch ready = new CountDownLatch(1);
113         // Test thread exits by InterruptedException,
114         // stopFlag is to handle spurious wakeups.
115         final boolean[] stopFlag = new boolean[1];
116         final Object syncObj = new Object();
117         Thread vthread = createPinnedVThread(() -> {
118             synchronized (syncObj) {
119                 try {
120                     ready.countDown();
121                     while (!stopFlag[0]) {
122                         if (withTimeout) {
123                             syncObj.wait(60000);
124                         } else {
125                             syncObj.wait();
126                         }
127                     }
128                 } catch (InterruptedException ex) {
129                     // expected after testThreadStates
130                 }
131             }
132         });
133         vthread.start();
134         ready.await();
135 
136         // Suspend test thread in "waiting" state.
137         suspendWaiting(vthread);
138 
139         int expectedState = JVMTI_THREAD_STATE_WAITING
140                 | JVMTI_THREAD_STATE_IN_OBJECT_WAIT
141                 | (withTimeout
142                     ? JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT
143                     : JVMTI_THREAD_STATE_WAITING_INDEFINITELY);
144         testThreadStates(vthread, true,  true, expectedState);
145         // signal test thread to finish (for safety, Object.wait should throw InterruptedException)
146         synchronized (syncObj) {
147             stopFlag[0] = true;
148             syncObj.notifyAll();
149         }
150         status.print();
151     }
152 
153     static void sleeping() throws Exception {
154         // JVMTI_THREAD_STATE_SLEEPING
155         // Thread is sleeping -- Thread.sleep.
156         // JVMTI_THREAD_STATE_PARKED
157         // A virtual thread that is sleeping, in Thread.sleep,
158         // may have this state flag set instead of JVMTI_THREAD_STATE_SLEEPING.
159         TestStatus status = new TestStatus("JVMTI_THREAD_STATE_SLEEPING");
160         CountDownLatch ready = new CountDownLatch(1);
161         // Test thread exits by InterruptedException,
162         // stopFlag is to handle spurious wakeups.
163         final boolean[] stopFlag = new boolean[1];
164         Thread vthread = createPinnedVThread(() -> {
165             ready.countDown();
166             try {
167                 while (!stopFlag[0]) {
168                     Thread.sleep(60000);
169                 }
170             } catch (InterruptedException ex) {
171                 // expected, ignore
172             }
173         });
174         vthread.start();
175         ready.await();
176 
177         // Suspend test thread in "waiting" state.
178         suspendWaiting(vthread);
179 
180         // vthread is suspended, set stopFlag before testThreadStates
181         stopFlag[0] = true;
182 
183         // don't test interrupt() - it causes thread state change for parked thread
184         // even if it's suspended
185         testThreadStates(vthread, true, false,
186                 JVMTI_THREAD_STATE_WAITING | JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT,
187                 JVMTI_THREAD_STATE_SLEEPING | JVMTI_THREAD_STATE_PARKED);
188         status.print();
189     }
190 
191     static void parked() throws Exception {
192         // JVMTI_THREAD_STATE_PARKED
193         // Thread is parked, for example: LockSupport.park, LockSupport.parkUtil and LockSupport.parkNanos.
194         TestStatus status = new TestStatus("JVMTI_THREAD_STATE_PARKED");
195         CountDownLatch ready = new CountDownLatch(1);
196         final boolean[] stopFlag = new boolean[1];
197 
198         Thread vthread = createPinnedVThread(() -> {
199             ready.countDown();
200             while (!stopFlag[0]) {
201                 LockSupport.park(Thread.currentThread());
202             }
203         });
204         vthread.start();
205         ready.await();
206 
207         // Suspend test thread in "waiting" state.
208         suspendWaiting(vthread);
209 
210         // vthread is suspended, set stopFlag before testThreadStates
211         stopFlag[0] = true;
212 
213         // don't test interrupt() - it causes thread state change for parked thread
214         // even if it's suspended
215         testThreadStates(vthread, true, false,
216                 JVMTI_THREAD_STATE_WAITING | JVMTI_THREAD_STATE_WAITING_INDEFINITELY | JVMTI_THREAD_STATE_PARKED);
217         // allow test thread to finish
218         LockSupport.unpark(vthread);
219         status.print();
220     }
221 
222     static void inNative() throws Exception {
223         TestStatus status = new TestStatus("JVMTI_THREAD_STATE_IN_NATIVE");
224         Thread vthread = createPinnedVThread(() -> {
225             waitInNative();
226         });
227         vthread.start();
228         while (!waitInNativeReady) {
229             Thread.sleep(50);
230         }
231         testThreadStates(vthread, false, true,
232                 JVMTI_THREAD_STATE_RUNNABLE | JVMTI_THREAD_STATE_IN_NATIVE,
233                 0);
234         endWait();
235         status.print();
236     }
237 
238 
239     public static void main(String[] args) throws Exception {
240         runnable();
241         blockedOnMonitorEnter();
242         waiting(false);
243         waiting(true);
244         sleeping();
245         parked();
246         inNative();
247 
248         int errCount = getErrorCount();
249         if (errCount > 0) {
250             throw new RuntimeException("Test failed, " + errCount + " errors");
251         }
252     }
253 
254     private static native void runFromNative(Runnable runnable);
255 
256     private static Thread createPinnedVThread(Runnable runnable) {
257         return Thread.ofVirtual().unstarted(() -> runFromNative(runnable));
258     }
259 
260     private static void runUpcall(Runnable runnable) {
261         runnable.run();
262     }
263 
264     // Native implementation of suspendWaiting.
265     // Returns false if the method is not able to reach the desired state in several tries.
266     private static native boolean trySuspendInWaitingState(Thread vthread);
267 
268     // Suspends virtual thread and ensures it's suspended in "waiting" state
269     // (to handle possible spurious wakeups).
270     // throws an exception if the method is not able to reach the desired state in several tries.
271     private static void suspendWaiting(Thread vthread) {
272         boolean result = trySuspendInWaitingState(vthread);
273         if (!result) {
274             throw new RuntimeException("Failed to suspend thread in WAITING state");
275         }
276     }
277 
278     // Tests thread states (vthread and the carrier thread).
279     // expectedStrong specifies value which must be present in vthreat state;
280     // expectedWeak is a combination of bits which may be set in vthreat state
281     // (at least one of the bit must set, but not all).
282     // Note: Last steps of the testing are interrupt/resume the virtual thread,
283     // so after the call vthread is interrupted.
284     private static native void testThread(Thread vthread, boolean isVThreadSuspended,
285                                           boolean testInterrupt,
286                                           int expectedStrong, int expectedWeak);
287     private static native int getErrorCount();
288     // To retry test case when spurious wakeup detected.
289     private static native int resetErrorCount(int count);
290 
291     private static boolean waitInNativeReady = false;
292 
293     // Sets waitInNativeReady static field to true
294     // and then waits until endWait() method is called.
295     private static native void waitInNative();
296     // Signals waitInNative() to exit.
297     private static native void endWait();
298 
299     private static void testThreadStates(Thread vthread, boolean isVThreadSuspended,
300                                          boolean testInterrupt,
301                                          int expectedStrong, int expectedWeak) {
302         String name = vthread.toString();
303         log("Thread " + name);
304         testThread(vthread, isVThreadSuspended, testInterrupt, expectedStrong, expectedWeak);
305     }
306 
307     private static void testThreadStates(Thread vthread, boolean isVThreadSuspended,
308                                          boolean testInterrupt, int expectedState) {
309         testThreadStates(vthread, isVThreadSuspended, testInterrupt, expectedState, 0);
310     }
311 
312     // helper class to print status of each test
313     private static class TestStatus {
314         private final String name;
315         private final int startErrorCount;
316         TestStatus(String name) {
317             this.name = name;
318             startErrorCount = getErrorCount();
319             log(">>" + name);
320         }
321         void print() {
322             log("<<" + name + (startErrorCount == getErrorCount() ? " - OK" : " - FAILED"));
323             log("");
324         }
325     }
326 
327     private static void log(Object s) {
328         System.out.println(s);
329     }
330 }