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 }