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 Thread createPinnedVThread(Runnable runnable) { 255 final Object syncObj = new Object(); 256 return Thread.ofVirtual().unstarted(() -> { 257 synchronized (syncObj) { 258 runnable.run(); 259 } 260 }); 261 } 262 263 // Native implementation of suspendWaiting. 264 // Returns false if the method is not able to reach the desired state in several tries. 265 private static native boolean trySuspendInWaitingState(Thread vthread); 266 267 // Suspends virtual thread and ensures it's suspended in "waiting" state 268 // (to handle possible spurious wakeups). 269 // throws an exception if the method is not able to reach the desired state in several tries. 270 private static void suspendWaiting(Thread vthread) { 271 boolean result = trySuspendInWaitingState(vthread); 272 if (!result) { 273 throw new RuntimeException("Failed to suspend thread in WAITING state"); 274 } 275 } 276 277 // Tests thread states (vthread and the carrier thread). 278 // expectedStrong specifies value which must be present in vthreat state; 279 // expectedWeak is a combination of bits which may be set in vthreat state 280 // (at least one of the bit must set, but not all). 281 // Note: Last steps of the testing are interrupt/resume the virtual thread, 282 // so after the call vthread is interrupted. 283 private static native void testThread(Thread vthread, boolean isVThreadSuspended, 284 boolean testInterrupt, 285 int expectedStrong, int expectedWeak); 286 private static native int getErrorCount(); 287 // To retry test case when spurious wakeup detected. 288 private static native int resetErrorCount(int count); 289 290 private static boolean waitInNativeReady = false; 291 292 // Sets waitInNativeReady static field to true 293 // and then waits until endWait() method is called. 294 private static native void waitInNative(); 295 // Signals waitInNative() to exit. 296 private static native void endWait(); 297 298 private static void testThreadStates(Thread vthread, boolean isVThreadSuspended, 299 boolean testInterrupt, 300 int expectedStrong, int expectedWeak) { 301 String name = vthread.toString(); 302 log("Thread " + name); 303 testThread(vthread, isVThreadSuspended, testInterrupt, expectedStrong, expectedWeak); 304 } 305 306 private static void testThreadStates(Thread vthread, boolean isVThreadSuspended, 307 boolean testInterrupt, int expectedState) { 308 testThreadStates(vthread, isVThreadSuspended, testInterrupt, expectedState, 0); 309 } 310 311 // helper class to print status of each test 312 private static class TestStatus { 313 private final String name; 314 private final int startErrorCount; 315 TestStatus(String name) { 316 this.name = name; 317 startErrorCount = getErrorCount(); 318 log(">>" + name); 319 } 320 void print() { 321 log("<<" + name + (startErrorCount == getErrorCount() ? " - OK" : " - FAILED")); 322 log(""); 323 } 324 } 325 326 private static void log(Object s) { 327 System.out.println(s); 328 } 329 }