45 import java.util.stream.Stream;
46
47 import jdk.jfr.EventType;
48 import jdk.jfr.Recording;
49 import jdk.jfr.consumer.RecordedEvent;
50 import jdk.jfr.consumer.RecordingFile;
51
52 import jdk.test.lib.thread.VThreadPinner;
53 import jdk.test.lib.thread.VThreadRunner; // ensureParallelism requires jdk.management
54 import jdk.test.lib.thread.VThreadScheduler;
55 import org.junit.jupiter.api.Test;
56 import org.junit.jupiter.api.BeforeAll;
57 import org.junit.jupiter.params.ParameterizedTest;
58 import org.junit.jupiter.params.provider.ValueSource;
59 import static org.junit.jupiter.api.Assertions.*;
60
61 class JfrEvents {
62
63 @BeforeAll
64 static void setup() {
65 int minParallelism = 2;
66 if (Thread.currentThread().isVirtual()) {
67 minParallelism++;
68 }
69 VThreadRunner.ensureParallelism(minParallelism);
70 }
71
72 /**
73 * Test jdk.VirtualThreadStart and jdk.VirtualThreadEnd events.
74 */
75 @Test
76 void testVirtualThreadStartAndEnd() throws Exception {
77 try (Recording recording = new Recording()) {
78 recording.enable("jdk.VirtualThreadStart");
79 recording.enable("jdk.VirtualThreadEnd");
80
81 // execute 100 tasks, each in their own virtual thread
82 recording.start();
83 ThreadFactory factory = Thread.ofVirtual().factory();
84 try (var executor = Executors.newThreadPerTaskExecutor(factory)) {
85 for (int i = 0; i < 100; i++) {
86 executor.submit(() -> { });
87 }
88 Thread.sleep(1000); // give time for thread end events to be recorded
89 } finally {
123 }
124 }
125 });
126 });
127
128 try {
129 // wait for thread to start and park
130 awaitTrue(started);
131 await(vthread, timed ? Thread.State.TIMED_WAITING : Thread.State.WAITING);
132 } finally {
133 done.set(true);
134 LockSupport.unpark(vthread);
135 vthread.join();
136 recording.stop();
137 }
138
139 assertContainsPinnedEvent(recording, vthread);
140 }
141 }
142
143 /**
144 * Test jdk.VirtualThreadSubmitFailed event.
145 */
146 @Test
147 void testVirtualThreadSubmitFailed() throws Exception {
148 try (Recording recording = new Recording()) {
149 recording.enable("jdk.VirtualThreadSubmitFailed");
150
151 recording.start();
152 try (ExecutorService pool = Executors.newCachedThreadPool()) {
153 Executor scheduler = task -> pool.execute(task);
154
155 // create virtual thread that uses custom scheduler
156 ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler);
157
158 // start a thread
159 Thread thread = factory.newThread(LockSupport::park);
160 thread.start();
161
162 // wait for thread to park
|
45 import java.util.stream.Stream;
46
47 import jdk.jfr.EventType;
48 import jdk.jfr.Recording;
49 import jdk.jfr.consumer.RecordedEvent;
50 import jdk.jfr.consumer.RecordingFile;
51
52 import jdk.test.lib.thread.VThreadPinner;
53 import jdk.test.lib.thread.VThreadRunner; // ensureParallelism requires jdk.management
54 import jdk.test.lib.thread.VThreadScheduler;
55 import org.junit.jupiter.api.Test;
56 import org.junit.jupiter.api.BeforeAll;
57 import org.junit.jupiter.params.ParameterizedTest;
58 import org.junit.jupiter.params.provider.ValueSource;
59 import static org.junit.jupiter.api.Assertions.*;
60
61 class JfrEvents {
62
63 @BeforeAll
64 static void setup() {
65 // need at least two carriers to test pinning
66 VThreadRunner.ensureParallelism(2);
67 }
68
69 /**
70 * Test jdk.VirtualThreadStart and jdk.VirtualThreadEnd events.
71 */
72 @Test
73 void testVirtualThreadStartAndEnd() throws Exception {
74 try (Recording recording = new Recording()) {
75 recording.enable("jdk.VirtualThreadStart");
76 recording.enable("jdk.VirtualThreadEnd");
77
78 // execute 100 tasks, each in their own virtual thread
79 recording.start();
80 ThreadFactory factory = Thread.ofVirtual().factory();
81 try (var executor = Executors.newThreadPerTaskExecutor(factory)) {
82 for (int i = 0; i < 100; i++) {
83 executor.submit(() -> { });
84 }
85 Thread.sleep(1000); // give time for thread end events to be recorded
86 } finally {
120 }
121 }
122 });
123 });
124
125 try {
126 // wait for thread to start and park
127 awaitTrue(started);
128 await(vthread, timed ? Thread.State.TIMED_WAITING : Thread.State.WAITING);
129 } finally {
130 done.set(true);
131 LockSupport.unpark(vthread);
132 vthread.join();
133 recording.stop();
134 }
135
136 assertContainsPinnedEvent(recording, vthread);
137 }
138 }
139
140 /**
141 * Test jdk.VirtualThreadPinned event when blocking on monitor while pinned.
142 */
143 @Test
144 void testBlockWhenPinned() throws Exception {
145 try (Recording recording = new Recording()) {
146 recording.enable("jdk.VirtualThreadPinned");
147 recording.start();
148
149 Object lock = new Object();
150
151 var started = new AtomicBoolean();
152 var vthread = Thread.ofVirtual().unstarted(() -> {
153 VThreadPinner.runPinned(() -> {
154 started.set(true);
155 synchronized (lock) { }
156 });
157 });
158
159 try {
160 synchronized (lock) {
161 vthread.start();
162 // wait for thread to start and block
163 awaitTrue(started);
164 await(vthread, Thread.State.BLOCKED);
165 }
166 } finally {
167 vthread.join();
168 recording.stop();
169 }
170
171 assertContainsPinnedEvent(recording, vthread);
172 }
173 }
174
175 /**
176 * Test jdk.VirtualThreadPinned event when waiting with Object.wait while pinned.
177 */
178 @ParameterizedTest
179 @ValueSource(booleans = { true, false })
180 void testObjectWaitWhenPinned(boolean timed) throws Exception {
181 try (Recording recording = new Recording()) {
182 recording.enable("jdk.VirtualThreadPinned");
183 recording.start();
184
185 Object lock = new Object();
186
187 var started = new AtomicBoolean();
188 var vthread = Thread.startVirtualThread(() -> {
189 VThreadPinner.runPinned(() -> {
190 started.set(true);
191 synchronized (lock) {
192 try {
193 if (timed) {
194 lock.wait(Long.MAX_VALUE);
195 } else {
196 lock.wait();
197 }
198 } catch (InterruptedException e) {
199 fail();
200 }
201 }
202 });
203 });
204
205 try {
206 // wait for thread to start and wait
207 awaitTrue(started);
208 await(vthread, timed ? Thread.State.TIMED_WAITING : Thread.State.WAITING);
209 } finally {
210 synchronized (lock) {
211 lock.notifyAll();
212 }
213 vthread.join();
214 recording.stop();
215 }
216
217 assertContainsPinnedEvent(recording, vthread);
218 }
219 }
220
221 /**
222 * Test jdk.VirtualThreadPinned event when parking in a class initializer.
223 */
224 @Test
225 void testParkInClassInitializer() throws Exception {
226 class TestClass {
227 static {
228 LockSupport.park();
229 }
230 static void m() {
231 // do nothing
232 }
233 }
234
235 try (Recording recording = new Recording()) {
236 recording.enable("jdk.VirtualThreadPinned");
237 recording.start();
238
239 var started = new AtomicBoolean();
240 Thread vthread = Thread.startVirtualThread(() -> {
241 started.set(true);
242 TestClass.m();
243 });
244
245 try {
246 // wait for it to start and park
247 awaitTrue(started);
248 await(vthread, Thread.State.WAITING);
249 } finally {
250 LockSupport.unpark(vthread);
251 vthread.join();
252 recording.stop();
253 }
254
255 assertContainsPinnedEvent(recording, vthread);
256 }
257 }
258
259 /**
260 * Test jdk.VirtualThreadPinned event when blocking on monitor in a class initializer.
261 */
262 @Test
263 void testBlockInClassInitializer() throws Exception {
264 class LockHolder {
265 static final Object lock = new Object();
266 }
267 class TestClass {
268 static {
269 synchronized (LockHolder.lock) { }
270 }
271 static void m() {
272 // no nothing
273 }
274 }
275
276 try (Recording recording = new Recording()) {
277 recording.enable("jdk.VirtualThreadPinned");
278 recording.start();
279
280 var started = new AtomicBoolean();
281 Thread vthread = Thread.ofVirtual().unstarted(() -> {
282 started.set(true);
283 TestClass.m();
284 });
285
286 try {
287 synchronized (LockHolder.lock) {
288 vthread.start();
289 // wait for thread to start and block
290 awaitTrue(started);
291 await(vthread, Thread.State.BLOCKED);
292 }
293 } finally {
294 vthread.join();
295 recording.stop();
296 }
297
298 assertContainsPinnedEvent(recording, vthread);
299 }
300 }
301
302 /**
303 * Test jdk.VirtualThreadPinned event when waiting for a class initializer.
304 */
305 @Test
306 void testWaitingForClassInitializer() throws Exception {
307 class TestClass {
308 static {
309 LockSupport.park();
310 }
311 static void m() {
312 // do nothing
313 }
314 }
315
316 try (Recording recording = new Recording()) {
317 recording.enable("jdk.VirtualThreadPinned");
318 recording.start();
319
320 var started1 = new AtomicBoolean();
321 var started2 = new AtomicBoolean();
322
323 Thread vthread1 = Thread.ofVirtual().unstarted(() -> {
324 started1.set(true);
325 TestClass.m();
326 });
327 Thread vthread2 = Thread.ofVirtual().unstarted(() -> {
328 started2.set(true);
329 TestClass.m();
330 });
331
332 try {
333 // start first virtual thread and wait for it to start + park
334 vthread1.start();
335 awaitTrue(started1);
336 await(vthread1, Thread.State.WAITING);
337
338 // start second virtual thread and wait for it to start
339 vthread2.start();
340 awaitTrue(started2);
341
342 // give time for second virtual thread to wait on the MutexLocker
343 Thread.sleep(3000);
344
345 } finally {
346 LockSupport.unpark(vthread1);
347 vthread1.join();
348 vthread2.join();
349 recording.stop();
350 }
351
352 // the recording should have a pinned event for vthread2
353 assertContainsPinnedEvent(recording, vthread2);
354 }
355 }
356
357 /**
358 * Test jdk.VirtualThreadSubmitFailed event.
359 */
360 @Test
361 void testVirtualThreadSubmitFailed() throws Exception {
362 try (Recording recording = new Recording()) {
363 recording.enable("jdk.VirtualThreadSubmitFailed");
364
365 recording.start();
366 try (ExecutorService pool = Executors.newCachedThreadPool()) {
367 Executor scheduler = task -> pool.execute(task);
368
369 // create virtual thread that uses custom scheduler
370 ThreadFactory factory = VThreadScheduler.virtualThreadFactory(scheduler);
371
372 // start a thread
373 Thread thread = factory.newThread(LockSupport::park);
374 thread.start();
375
376 // wait for thread to park
|