< prev index next > test/jdk/java/lang/Thread/virtual/JfrEvents.java
Print this page
/**
* @test
* @summary Basic test for JFR jdk.VirtualThreadXXX events
* @requires vm.continuations
* @modules jdk.jfr java.base/java.lang:+open
! * @run junit/othervm JfrEvents
*/
import java.io.IOException;
import java.nio.file.Path;
- import java.time.Duration;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.LockSupport;
import java.util.stream.Collectors;
import jdk.jfr.EventType;
import jdk.jfr.Recording;
import jdk.jfr.consumer.RecordedEvent;
import jdk.jfr.consumer.RecordingFile;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class JfrEvents {
- private static final Object lock = new Object();
/**
* Test jdk.VirtualThreadStart and jdk.VirtualThreadEnd events.
*/
@Test
/**
* @test
* @summary Basic test for JFR jdk.VirtualThreadXXX events
* @requires vm.continuations
* @modules jdk.jfr java.base/java.lang:+open
! * @library /test/lib
+ * @run junit/othervm --enable-native-access=ALL-UNNAMED JfrEvents
*/
import java.io.IOException;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadFactory;
+ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.LockSupport;
+ import java.util.function.Consumer;
import java.util.stream.Collectors;
+ import java.util.stream.Stream;
import jdk.jfr.EventType;
import jdk.jfr.Recording;
import jdk.jfr.consumer.RecordedEvent;
import jdk.jfr.consumer.RecordingFile;
+ import jdk.test.lib.thread.VThreadPinner;
+ import jdk.test.lib.thread.VThreadPinner.ThrowingAction;
import org.junit.jupiter.api.Test;
+ import org.junit.jupiter.params.ParameterizedTest;
+ import org.junit.jupiter.params.provider.Arguments;
+ import org.junit.jupiter.params.provider.MethodSource;
import static org.junit.jupiter.api.Assertions.*;
class JfrEvents {
/**
* Test jdk.VirtualThreadStart and jdk.VirtualThreadEnd events.
*/
@Test
assertEquals(100, startCount);
assertEquals(100, endCount);
}
}
/**
* Test jdk.VirtualThreadPinned event.
*/
! @Test
! void testVirtualThreadPinned() throws Exception {
! Runnable[] parkers = new Runnable[] {
! () -> LockSupport.park(),
! () -> LockSupport.parkNanos(Duration.ofDays(1).toNanos())
! };
try (Recording recording = new Recording()) {
recording.enable("jdk.VirtualThreadPinned");
recording.start();
! try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
! for (Runnable parker : parkers) {
! // execute parking task in virtual thread
- var threadRef = new AtomicReference<Thread>();
- executor.submit(() -> {
- threadRef.set(Thread.currentThread());
- synchronized (lock) {
- parker.run(); // should pin carrier
- }
- });
-
- // wait for the task to start and the virtual thread to park
- Thread thread;
- while ((thread = threadRef.get()) == null) {
- Thread.sleep(10);
- }
try {
! Thread.State state = thread.getState();
! while (state != Thread.State.WAITING && state != Thread.State.TIMED_WAITING) {
! Thread.sleep(10);
! state = thread.getState();
! }
! } finally {
! LockSupport.unpark(thread);
}
}
} finally {
recording.stop();
}
Map<String, Integer> events = sumEvents(recording);
System.err.println(events);
! // should have a pinned event for each park
int pinnedCount = events.getOrDefault("jdk.VirtualThreadPinned", 0);
! assertEquals(parkers.length, pinnedCount);
}
}
/**
* Test jdk.VirtualThreadSubmitFailed event.
assertEquals(100, startCount);
assertEquals(100, endCount);
}
}
+ /**
+ * Arguments for testVirtualThreadPinned to test jdk.VirtualThreadPinned event.
+ * [0] label/description
+ * [1] the operation to park/wait
+ * [2] the Thread.State when parked/waiting
+ * [3] the action to unpark/notify the thread
+ */
+ static Stream<Arguments> pinnedCases() {
+ Object lock = new Object();
+
+ // park with native frame on stack
+ var finish1 = new AtomicBoolean();
+ var parkWhenPinned = Arguments.of(
+ "LockSupport.park when pinned",
+ (ThrowingAction) () -> {
+ VThreadPinner.runPinned(() -> {
+ while (!finish1.get()) {
+ LockSupport.park();
+ }
+ });
+ },
+ Thread.State.WAITING,
+ (Consumer<Thread>) t -> {
+ finish1.set(true);
+ LockSupport.unpark(t);
+ }
+ );
+
+ // timed park with native frame on stack
+ var finish2 = new AtomicBoolean();
+ var timedParkWhenPinned = Arguments.of(
+ "LockSupport.parkNanos when pinned",
+ (ThrowingAction) () -> {
+ VThreadPinner.runPinned(() -> {
+ while (!finish2.get()) {
+ LockSupport.parkNanos(Long.MAX_VALUE);
+ }
+ });
+ },
+ Thread.State.TIMED_WAITING,
+ (Consumer<Thread>) t -> {
+ finish2.set(true);
+ LockSupport.unpark(t);
+ }
+ );
+
+ // untimed Object.wait
+ var waitWhenPinned = Arguments.of(
+ "Object.wait",
+ (ThrowingAction) () -> {
+ synchronized (lock) {
+ lock.wait();
+ }
+ },
+ Thread.State.WAITING,
+ (Consumer<Thread>) t -> {
+ synchronized (lock) {
+ lock.notifyAll();
+ }
+ }
+ );
+
+ // timed Object.wait
+ var timedWaitWhenPinned = Arguments.of(
+ "Object.wait(millis)",
+ (ThrowingAction) () -> {
+ synchronized (lock) {
+ lock.wait(Long.MAX_VALUE);
+ }
+ },
+ Thread.State.TIMED_WAITING,
+ (Consumer<Thread>) t -> {
+ synchronized (lock) {
+ lock.notifyAll();
+ }
+ }
+ );
+
+ return Stream.of(parkWhenPinned, timedParkWhenPinned, waitWhenPinned, timedWaitWhenPinned);
+ }
+
/**
* Test jdk.VirtualThreadPinned event.
*/
! @ParameterizedTest
! @MethodSource("pinnedCases")
! void testVirtualThreadPinned(String label,
! ThrowingAction<Exception> parker,
! Thread.State expectedState,
! Consumer<Thread> unparker) throws Exception {
try (Recording recording = new Recording()) {
recording.enable("jdk.VirtualThreadPinned");
recording.start();
! try {
! var exception = new AtomicReference<Throwable>();
! var thread = Thread.ofVirtual().start(() -> {
try {
! parker.run();
! } catch (Throwable e) {
! exception.set(e);
! }
! });
! try {
! // wait for thread to park/wait
+ Thread.State state = thread.getState();
+ while (state != expectedState) {
+ assertTrue(state != Thread.State.TERMINATED, thread.toString());
+ Thread.sleep(10);
+ state = thread.getState();
}
+ } finally {
+ unparker.accept(thread);
+ thread.join();
+ assertNull(exception.get());
}
} finally {
recording.stop();
}
Map<String, Integer> events = sumEvents(recording);
System.err.println(events);
! // should have at least one pinned event
int pinnedCount = events.getOrDefault("jdk.VirtualThreadPinned", 0);
! assertTrue(pinnedCount >= 1, "Expected one or more events");
}
}
/**
* Test jdk.VirtualThreadSubmitFailed event.
< prev index next >