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