-
Notifications
You must be signed in to change notification settings - Fork 1.7k
[GR-57064] Lazy virtual thread JFR registration and epoch generation caching #9570
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
graalvmbot
merged 4 commits into
oracle:master
from
roberttoyonaga:vthread-registration
Apr 24, 2025
Merged
Changes from 1 commit
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -43,6 +43,7 @@ public class JfrTraceIdEpoch { | |
private static final long EPOCH_1_BIT = 0b10; | ||
|
||
private boolean epoch; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, good idea. I'll remove that. |
||
private int epochGeneration; | ||
|
||
@Fold | ||
public static JfrTraceIdEpoch getInstance() { | ||
|
@@ -57,6 +58,7 @@ public JfrTraceIdEpoch() { | |
public void changeEpoch() { | ||
assert VMOperation.isInProgressAtSafepoint(); | ||
epoch = !epoch; | ||
epochGeneration++; | ||
} | ||
|
||
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) | ||
|
@@ -74,6 +76,11 @@ public boolean currentEpoch() { | |
return epoch; | ||
} | ||
|
||
@Uninterruptible(reason = "Avoid epoch changing while checking generation.", callerMustBe = true) | ||
public int currentEpochGeneration() { | ||
return epochGeneration; | ||
} | ||
|
||
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) | ||
public boolean previousEpoch() { | ||
return !epoch; | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't this break once we leave uninterruptible code?
getThreadId(...)
, which registers avthread
for epoch 0 inmaybeRegisterVirtualThread
getThreadId(...)
even though the vthread was only registered for epoch 0One example where I think that this might happen:
JavaMonitorQueuedSynchronizer.ConditionNode.notifierJfrTid
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the situation you describe is actually ok. Once the epoch changes, new event emissions from "thread A" will fail the epoch generation check, and registration will be re-done. Since
getThreadID()
is called for every event emission, the important thing is that gathering event info withgetThreadID()
and the writing of the event data to the JFR buffer are done within the same block of uninterruptible code, which does indeed happen (ex. allemit0
methods).Calls to
getThreadID()
outside of the event emission path (ex. withJavaMonitorQueuedSynchronizer.ConditionNode.notifierJfrTid
) result in unnecessary registration checks, but shouldn't break anything.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, so you essentially assume that
getThreadID()
will be called again for thread A once the thread ID is actually used in a JFR event. However, I don't think that this assumption is valid. Here is one example:ConditionNode
.ConditionNode.notifierJfrTid
.ConditionNode.notifierJfrTid
(i.e., the ID of thread A) when emitting aJavaMonitorWaitEvent
. However, thread A is not registered for the current epoch.Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok I see. I mistakenly thought we always used
JfrNativeEventWriter.putThread(JfrNativeEventWriterData , Thread )
when dealing with threads. But in that case we use a the plain TID directly.I think we'll have to switch to always using
putThread
whenever dealing with the JFRThread
type. What do you think of this?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The downside is that we'll have to remember to give careful consideration to emitting JFR
Thread
data.I'm not sure there's a better place to put the registration check though. And we'll need to check at some point in order to copy Hotspot and do lazy registration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've just pushed a commit to update all the places we write thread IDs as JFR event data to use either
putThread(Thread)
orputThread(long)
. This should ensure that threads are always registered if they get referenced from event data. The downside is that we need to remember to useputThread
.Another unfortunate thing is that we can only do checks with
putThread(Thread)
because with only the TID we have no way of knowing whether the thread is virtual and cannot accessTarget_java_lang_VirtualThread.jfrGeneration
.