Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jdk.VirtualThreadPinned event not caught

Given this code, that tries to listen to the event jdk.VirtualThreadPinned when a virtual thread is pinned:

import static org.junit.Assert.assertTrue;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import jdk.jfr.Event;
import jdk.jfr.Name;
import jdk.jfr.consumer.RecordingStream;
import org.junit.Test;

public class JFRTest {

    @Test
    public void pinnedVirtualThreads() throws InterruptedException {
        CountDownLatch stopListening = new CountDownLatch(1);
        CountDownLatch startListening = new CountDownLatch(1);
        try (RecordingStream recordingStream = new RecordingStream()) {
            // Create and start the recording stream
            startRecordingStream(recordingStream, startListening, stopListening);
    
            // Make sure JFR listener is running before continue
            while (true) {
                new StartEvent().commit();
                if (startListening.await(1, TimeUnit.SECONDS)) {
                    break;
                }
            }
    
            // Simulate blocking operation
            Thread vt = Thread.ofVirtual().start(() -> {
                synchronized (this) {
                    System.out.println("Virtual Thread is pinned");
                }
            });
            vt.join();
        }
        // Wait till the jdk.VirtualThreadPinned triggers
        assertTrue("jdk.VirtualThreadPinned was not sent", stopListening.await(5, TimeUnit.SECONDS));
    }
    
    private void startRecordingStream(RecordingStream recordingStream, CountDownLatch startListening, CountDownLatch stopListening) {
        // Enable the jdk.VirtualThreadPinned event
        recordingStream.enable("jdk.VirtualThreadPinned").withStackTrace();

        // Notify listener is running after receiving the StartEvent
        recordingStream.onEvent("StartEvent", event -> {
            System.out.println("Received " + event);
            startListening.countDown();
        });

        // Set an event handler
        recordingStream.onEvent("jdk.VirtualThreadPinned", event -> {
            System.out.println("VirtualThreadPinned event detected!");
            System.out.println("Timestamp: " + event);
            stopListening.countDown();
        });

        // Start the recording stream
        recordingStream.startAsync();
    }

    @Name("StartEvent")
    private static class StartEvent extends Event {
    }
}

This is the output:

[INFO] Running com.example.general.JFRTest
Received StartEvent {
  startTime = 10:37:46.830 (2024-06-12)
  eventThread = "main" (javaThreadId = 1)
  stackTrace = [
    com.example.general.JFRTest.pinnedVirtualThreads() line: 25
    jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Object, Object[]) line: 103
    java.lang.reflect.Method.invoke(Object, Object[]) line: 580
    org.junit.runners.model.FrameworkMethod$1.runReflectiveCall() line: 59
    org.junit.internal.runners.model.ReflectiveCallable.run() line: 12
  ]
}


Virtual Thread is pinned
[ERROR] Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 6.215 s <<< FAILURE! - in com.example.general.JFRTest
[ERROR] com.example.general.JFRTest.pinnedVirtualThreads  Time elapsed: 6.187 s  <<< FAILURE!
java.lang.AssertionError: jdk.VirtualThreadPinned was not sent

As you can see, this part should trigger the event jdk.VirtualThreadPinned, but it is not happening. On the other hand, the other event StartEvent is caught:

Thread vt = Thread.ofVirtual().start(() -> {
    synchronized (this) {
        System.out.println("Virtual Thread is pinned");
    }
});

I don't know why.

This is my JDK version is: 21.0.3+7-LTS-152

like image 606
ravenskater Avatar asked Oct 30 '25 03:10

ravenskater


1 Answers

jdk.VirtualThreadPinned is only fired when the runtime tries to park a thread that is pinned. You can trigger this for instance by calling Thread.sleep with the virtual thread runnable:

Thread vt = Thread.ofVirtual().start(() -> {
    synchronized (lock) {
        System.out.println("Virtual Thread is pinned");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
});

You also need to make sure the event stream is flushed, otherwise your callback for the jdk.VirtualThreadPinned event will never be called. You can do this by calling .stop() on the recording stream (close()-ing the stream stops the recording abruptly, and doesn't wait for all events to be consumed). For instance, right before the end of the try block:

    vt.join();
    recordingStream.stop();
} // end of try

With those 2 changes, I see the event being fired:

VirtualThreadPinned event detected!
Timestamp: jdk.VirtualThreadPinned {
  startTime = 10:28:32.075 (2024-06-12)
  duration = 998 ms
  eventThread = "" (javaThreadId = 37, virtual)
  stackTrace = [
    java.lang.VirtualThread.parkOnCarrierThread(boolean, long) line: 689
    java.lang.VirtualThread.parkNanos(long) line: 648
    java.lang.VirtualThread.sleepNanos(long) line: 812
    java.lang.Thread.sleepNanos(long) line: 489
    java.lang.Thread.sleep(long) line: 522
    ...
  ]
}
like image 99
Jorn Vernee Avatar answered Nov 01 '25 19:11

Jorn Vernee



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!