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 id=default 26 * @bug 8312498 27 * @summary Basic test for JVMTI GetThreadState with virtual threads 28 * @modules java.base/java.lang:+open 29 * @library /test/lib 30 * @run junit/othervm/native --enable-native-access=ALL-UNNAMED GetThreadStateTest 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/native -XX:+UnlockExperimentalVMOptions -XX:-VMContinuations --enable-native-access=ALL-UNNAMED GetThreadStateTest 39 */ 40 41 import java.util.StringJoiner; 42 import java.util.concurrent.atomic.AtomicBoolean; 43 import java.util.concurrent.locks.LockSupport; 44 45 import jdk.test.lib.thread.VThreadRunner; 46 import jdk.test.lib.thread.VThreadPinner; 47 import org.junit.jupiter.api.BeforeAll; 48 import org.junit.jupiter.api.Test; 49 import org.junit.jupiter.params.ParameterizedTest; 50 import org.junit.jupiter.params.provider.ValueSource; 51 import static org.junit.jupiter.api.Assertions.*; 52 53 class GetThreadStateTest { 54 55 @BeforeAll 56 static void setup() { 57 System.loadLibrary("GetThreadStateTest"); 58 init(); 59 60 // need >=2 carriers for testing pinning when main thread is a virtual thread 61 if (Thread.currentThread().isVirtual()) { 62 VThreadRunner.ensureParallelism(2); 63 } 64 } 65 66 /** 67 * Test state of new/unstarted thread. 68 */ 69 @Test 70 void testUnstarted() { 71 var thread = Thread.ofVirtual().unstarted(() -> { }); 72 check(thread, /*new*/ 0); 73 } 74 75 /** 76 * Test state of terminated thread. 77 */ 78 @Test 79 void testTerminated() throws Exception { 80 var thread = Thread.ofVirtual().start(() -> { }); 81 thread.join(); 82 check(thread, JVMTI_THREAD_STATE_TERMINATED); 83 } 84 85 /** 86 * Test state of runnable thread. 87 */ 88 @Test 89 void testRunnable() throws Exception { 90 var started = new AtomicBoolean(); 91 var done = new AtomicBoolean(); 92 var thread = Thread.ofVirtual().start(() -> { 93 started.set(true); 94 95 // spin until done 96 while (!done.get()) { 97 Thread.onSpinWait(); 98 } 99 }); 100 try { 101 // wait for thread to start execution 102 awaitTrue(started); 103 104 // thread should be runnable 105 int expected = JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_RUNNABLE; 106 check(thread, expected); 107 108 // re-test with interrupt status set 109 thread.interrupt(); 110 check(thread, expected | JVMTI_THREAD_STATE_INTERRUPTED); 111 } finally { 112 done.set(true); 113 thread.join(); 114 } 115 } 116 117 /** 118 * Test state of thread waiting to enter a monitor when pinned and not pinned. 119 */ 120 @ParameterizedTest 121 @ValueSource(booleans = { true, false }) 122 void testMonitorEnter(boolean pinned) throws Exception { 123 var started = new AtomicBoolean(); 124 Object lock = new Object(); 125 var thread = Thread.ofVirtual().unstarted(() -> { 126 started.set(true); 127 if (pinned) { 128 VThreadPinner.runPinned(() -> { 129 synchronized (lock) { } 130 }); 131 } else { 132 synchronized (lock) { } 133 } 134 }); 135 try { 136 synchronized (lock) { 137 // start thread and wait for it to start execution 138 thread.start(); 139 awaitTrue(started); 140 141 // thread should block on monitor enter 142 int expected = JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER; 143 await(thread, expected); 144 145 // re-test with interrupt status set 146 thread.interrupt(); 147 check(thread, expected | JVMTI_THREAD_STATE_INTERRUPTED); 148 } 149 } finally { 150 thread.join(); 151 } 152 } 153 154 /** 155 * Test state of thread waiting in Object.wait(). 156 */ 157 @Test 158 void testObjectWait() throws Exception { 159 var started = new AtomicBoolean(); 160 Object lock = new Object(); 161 var thread = Thread.ofVirtual().start(() -> { 162 synchronized (lock) { 163 started.set(true); 164 try { 165 lock.wait(); 166 } catch (InterruptedException e) { } 167 } 168 }); 169 try { 170 // wait for thread to start execution 171 awaitTrue(started); 172 173 // thread should wait 174 int expected = JVMTI_THREAD_STATE_ALIVE | 175 JVMTI_THREAD_STATE_WAITING | 176 JVMTI_THREAD_STATE_WAITING_INDEFINITELY | 177 JVMTI_THREAD_STATE_IN_OBJECT_WAIT; 178 await(thread, expected); 179 180 // notify so thread waits to re-enter monitor 181 synchronized (lock) { 182 lock.notifyAll(); 183 expected = JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER; 184 check(thread, expected); 185 186 // re-test with interrupt status set 187 thread.interrupt(); 188 check(thread, expected | JVMTI_THREAD_STATE_INTERRUPTED); 189 } 190 } finally { 191 thread.interrupt(); 192 thread.join(); 193 } 194 } 195 196 /** 197 * Test state of thread waiting in Object.wait(millis). 198 */ 199 @Test 200 void testObjectWaitMillis() throws Exception { 201 var started = new AtomicBoolean(); 202 Object lock = new Object(); 203 var thread = Thread.ofVirtual().start(() -> { 204 synchronized (lock) { 205 started.set(true); 206 try { 207 lock.wait(Long.MAX_VALUE); 208 } catch (InterruptedException e) { } 209 } 210 }); 211 try { 212 // wait for thread to start execution 213 awaitTrue(started); 214 215 // thread should wait 216 int expected = JVMTI_THREAD_STATE_ALIVE | 217 JVMTI_THREAD_STATE_WAITING | 218 JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT | 219 JVMTI_THREAD_STATE_IN_OBJECT_WAIT; 220 await(thread, expected); 221 222 // notify so thread waits to re-enter monitor 223 synchronized (lock) { 224 lock.notifyAll(); 225 expected = JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER; 226 check(thread, expected); 227 228 // re-test with interrupt status set 229 thread.interrupt(); 230 check(thread, expected | JVMTI_THREAD_STATE_INTERRUPTED); 231 } 232 } finally { 233 thread.interrupt(); 234 thread.join(); 235 } 236 } 237 238 /** 239 * Test state of thread parked with LockSupport.park when pinned and not pinned. 240 */ 241 @ParameterizedTest 242 @ValueSource(booleans = { true, false }) 243 void testPark(boolean pinned) throws Exception { 244 var started = new AtomicBoolean(); 245 var done = new AtomicBoolean(); 246 var thread = Thread.ofVirtual().start(() -> { 247 started.set(true); 248 if (pinned) { 249 VThreadPinner.runPinned(() -> { 250 while (!done.get()) { 251 LockSupport.park(); 252 } 253 }); 254 } else { 255 while (!done.get()) { 256 LockSupport.park(); 257 } 258 } 259 }); 260 try { 261 // wait for thread to start execution 262 awaitTrue(started); 263 264 // thread should park 265 int expected = JVMTI_THREAD_STATE_ALIVE | 266 JVMTI_THREAD_STATE_WAITING | 267 JVMTI_THREAD_STATE_WAITING_INDEFINITELY | 268 JVMTI_THREAD_STATE_PARKED; 269 await(thread, expected); 270 } finally { 271 done.set(true); 272 LockSupport.unpark(thread); 273 thread.join(); 274 } 275 } 276 277 /** 278 * Test state of thread parked with LockSupport.parkNanos when pinned and not pinned. 279 */ 280 @ParameterizedTest 281 @ValueSource(booleans = { true, false }) 282 void testParkNanos(boolean pinned) throws Exception { 283 var started = new AtomicBoolean(); 284 var done = new AtomicBoolean(); 285 var thread = Thread.ofVirtual().start(() -> { 286 started.set(true); 287 if (pinned) { 288 VThreadPinner.runPinned(() -> { 289 while (!done.get()) { 290 LockSupport.parkNanos(Long.MAX_VALUE); 291 } 292 }); 293 } else { 294 while (!done.get()) { 295 LockSupport.parkNanos(Long.MAX_VALUE); 296 } 297 } 298 }); 299 try { 300 // wait for thread to start execution 301 awaitTrue(started); 302 303 // thread should park 304 int expected = JVMTI_THREAD_STATE_ALIVE | 305 JVMTI_THREAD_STATE_WAITING | 306 JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT | 307 JVMTI_THREAD_STATE_PARKED; 308 await(thread, expected); 309 } finally { 310 done.set(true); 311 LockSupport.unpark(thread); 312 thread.join(); 313 } 314 } 315 316 /** 317 * Waits for the boolean value to become true. 318 */ 319 private static void awaitTrue(AtomicBoolean ref) throws Exception { 320 while (!ref.get()) { 321 Thread.sleep(20); 322 } 323 } 324 325 /** 326 * Asserts that the given thread has the expected JVMTI state. 327 */ 328 private static void check(Thread thread, int expected) { 329 System.err.format(" expect state=0x%x (%s) ...%n", expected, jvmtiStateToString(expected)); 330 int state = jvmtiState(thread); 331 System.err.format(" thread state=0x%x (%s)%n", state, jvmtiStateToString(state)); 332 assertEquals(expected, state); 333 } 334 335 /** 336 * Waits indefinitely for the given thread to get to the target JVMTI state. 337 */ 338 private static void await(Thread thread, int targetState) throws Exception { 339 System.err.format(" await state=0x%x (%s) ...%n", targetState, jvmtiStateToString(targetState)); 340 int state = jvmtiState(thread); 341 System.err.format(" thread state=0x%x (%s)%n", state, jvmtiStateToString(state)); 342 while (state != targetState) { 343 assertTrue(thread.isAlive(), "Thread has terminated"); 344 Thread.sleep(20); 345 state = jvmtiState(thread); 346 System.err.format(" thread state=0x%x (%s)%n", state, jvmtiStateToString(state)); 347 } 348 } 349 350 private static final int JVMTI_THREAD_STATE_ALIVE = 0x0001; 351 private static final int JVMTI_THREAD_STATE_TERMINATED = 0x0002; 352 private static final int JVMTI_THREAD_STATE_RUNNABLE = 0x0004; 353 private static final int JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER = 0x0400; 354 private static final int JVMTI_THREAD_STATE_WAITING = 0x0080; 355 private static final int JVMTI_THREAD_STATE_WAITING_INDEFINITELY = 0x0010; 356 private static final int JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT = 0x0020; 357 private static final int JVMTI_THREAD_STATE_SLEEPING = 0x0040; 358 private static final int JVMTI_THREAD_STATE_IN_OBJECT_WAIT = 0x0100; 359 private static final int JVMTI_THREAD_STATE_PARKED = 0x0200; 360 private static final int JVMTI_THREAD_STATE_SUSPENDED = 0x100000; 361 private static final int JVMTI_THREAD_STATE_INTERRUPTED = 0x200000; 362 private static final int JVMTI_THREAD_STATE_IN_NATIVE = 0x400000; 363 364 private static native void init(); 365 private static native int jvmtiState(Thread thread); 366 367 private static String jvmtiStateToString(int state) { 368 StringJoiner sj = new StringJoiner(" | "); 369 if ((state & JVMTI_THREAD_STATE_ALIVE) != 0) 370 sj.add("JVMTI_THREAD_STATE_ALIVE"); 371 if ((state & JVMTI_THREAD_STATE_TERMINATED) != 0) 372 sj.add("JVMTI_THREAD_STATE_TERMINATED"); 373 if ((state & JVMTI_THREAD_STATE_RUNNABLE) != 0) 374 sj.add("JVMTI_THREAD_STATE_RUNNABLE"); 375 if ((state & JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER) != 0) 376 sj.add("JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER"); 377 if ((state & JVMTI_THREAD_STATE_WAITING) != 0) 378 sj.add("JVMTI_THREAD_STATE_WAITING"); 379 if ((state & JVMTI_THREAD_STATE_WAITING_INDEFINITELY) != 0) 380 sj.add("JVMTI_THREAD_STATE_WAITING_INDEFINITELY"); 381 if ((state & JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT) != 0) 382 sj.add("JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT"); 383 if ((state & JVMTI_THREAD_STATE_IN_OBJECT_WAIT) != 0) 384 sj.add("JVMTI_THREAD_STATE_IN_OBJECT_WAIT"); 385 if ((state & JVMTI_THREAD_STATE_PARKED) != 0) 386 sj.add("JVMTI_THREAD_STATE_PARKED"); 387 if ((state & JVMTI_THREAD_STATE_SUSPENDED) != 0) 388 sj.add("JVMTI_THREAD_STATE_SUSPENDED"); 389 if ((state & JVMTI_THREAD_STATE_INTERRUPTED) != 0) 390 sj.add("JVMTI_THREAD_STATE_INTERRUPTED"); 391 if ((state & JVMTI_THREAD_STATE_IN_NATIVE) != 0) 392 sj.add("JVMTI_THREAD_STATE_IN_NATIVE"); 393 String s = sj.toString(); 394 return s.isEmpty() ? "<empty>" : s; 395 } 396 }