Skip to content

Commit 15195e6

Browse files
author
Alex Menkov
committed
8310066: Improve test coverage for JVMTI GetThreadState on carrier and mounted vthread
Reviewed-by: sspitsyn, cjplummer
1 parent 6cb9ec3 commit 15195e6

File tree

2 files changed

+543
-0
lines changed

2 files changed

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

0 commit comments

Comments
 (0)