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