-
Notifications
You must be signed in to change notification settings - Fork 768
Refactor HeapArgInfo to Support Recursive Downcalls #22370
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
base: master
Are you sure you want to change the base?
Conversation
@AditiS11 Please read through https://github.com/eclipse-omr/omr/blob/master/doc/CodingStandard.md |
Also please refer to https://github.com/eclipse-openj9/openj9/blob/master/CONTRIBUTING.md for opening PRs |
7d00d90
to
ee8865d
Compare
Please rename the title of the PR to something that describes the changes. Also, please explain in the commit message what you are trying to achieve. |
6e71ba7
to
bf1c4b8
Compare
333aee1
to
43cf086
Compare
sanity.functional and sanity.openjdk tests: https://hyc-runtimes-jenkins.swg-devops.com/view/OpenJ9%20-%20Personal/job/Pipeline-Build-Test-Personal/29426/ |
@@ -117,13 +119,17 @@ void append(Object base, long offset) { | |||
index += 1; | |||
} | |||
|
|||
boolean isFull() { | |||
return index >= bases.length; |
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.
this should be impossible if a new HeapArgInfo is created for each call
void clear() { | ||
Arrays.fill(bases, null); | ||
index = 0; | ||
} | ||
} | ||
|
||
private final ThreadLocal<HeapArgInfo> heapArgInfo; | ||
private final ThreadLocal<Deque<HeapArgInfo>> heapArgInfo; |
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.
this should be static
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.
And the thread local should be initialized here
@@ -507,7 +518,7 @@ public InternalDowncallHandler(MethodType functionMethodType, FunctionDescriptor | |||
/*[ENDIF] JAVA_SPEC_VERSION == 17 */ | |||
|
|||
/*[IF JAVA_SPEC_VERSION >= 22]*/ | |||
heapArgInfo = new ThreadLocal<>(); | |||
heapArgInfo = ThreadLocal.withInitial(ArrayDeque::new); |
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.
this should be removed
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.
Aditi, thanks for making this change.
The test in #21944 is labelled with "test excluded" which means it will not be run in the sanity.openjdk test suite. Can you also run the test on its own in a grinder to show its passing? Let me know if you have any questions on how to do this.
Once this change is in you will need to submit another pull request at https://github.com/adoptium/aqa-tests to re-enable TestBufferStack by removing this line before closing #21944.
Title nitpick: Instead of including implementation details here please include why HeapArgInfo is being refactored. I suggest something along the lines of "Refactor HeapArgInfo to Support Recursive Downcalls".
f11b808
to
728686d
Compare
sanity.functional and sanity.openjdk tests: https://hyc-runtimes-jenkins.swg-devops.com/view/OpenJ9%20-%20Personal/job/Pipeline-Build-Test-Personal/29534/ java/foreign/TestBufferStack: https://hyc-runtimes-jenkins.swg-devops.com/view/Test_grinder/job/Grinder/54089/ |
@@ -27,6 +27,8 @@ | |||
/*[ENDIF] JAVA_SPEC_VERSION >= 22 */ | |||
import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Deque; |
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.
Please move these imports inside JAVA_SPEC_VERSION >= 22 and order them alphabetically.
@@ -123,7 +125,8 @@ void clear() { | |||
} | |||
} | |||
|
|||
private final ThreadLocal<HeapArgInfo> heapArgInfo; | |||
private static final ThreadLocal<Deque<HeapArgInfo>> heapArgInfo = ThreadLocal.withInitial(ArrayDeque::new); | |||
private static final ThreadLocal<Boolean> flagCreateHeapArgInfo = ThreadLocal.withInitial(()->Boolean.TRUE); |
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.
Please add a comment either here or in runNativeMethod
describing the updated behavior of these two fields.
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.
nit: flag in flagCreateHeapArgInfo seems redundant, I would suggest shortening this to createHeapArgInfo or newHeapArgInfo.
@@ -323,18 +327,21 @@ private final long memSegmtOfPtrToLongArg(MemorySegment argValue, LinkerOptions | |||
* is captured so as to reset the internal index and avoid retaining the references | |||
* to the unreachable objects. | |||
*/ | |||
if (info != null) { | |||
info.clear(); | |||
if(!flagCreateHeapArgInfo.get() && !infoStack.isEmpty()) { |
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 second check !infoStack.isEmpty()
is not needed. If flagCreateHeapArgInfo is false there should be a stack entry. Also add a space before the first parenthesis.
} | ||
/*[ENDIF] JAVA_SPEC_VERSION >= 22 */ | ||
throw e; | ||
} | ||
|
||
long address = argValue.address(); | ||
/*[IF JAVA_SPEC_VERSION >= 22]*/ | ||
if (info == null) { | ||
|
||
if ((info == null) || flagCreateHeapArgInfo.get()) { |
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 (info == null)
check is not necessary since flagCreateHeapArgInfo
is true by default.
if (info != null) { | ||
info.clear(); | ||
Deque<HeapArgInfo> infoStack = heapArgInfo.get(); | ||
if(!flagCreateHeapArgInfo.get() && !infoStack.isEmpty()) { |
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.
Same applies here, the second check can be removed and please add a space after if.
if (flagCreateHeapArgInfo.get()) { | ||
info = null; | ||
pointerArgs = false; | ||
} |
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.
Put else on the same line.
@@ -936,8 +951,8 @@ Object runNativeMethod(Addressable downcallAddr, SegmentAllocator segmtAllocator | |||
* so as to reset the internal index and avoid retaining the references to the | |||
* unreachable objects. | |||
*/ | |||
if (info != null) { | |||
info.clear(); | |||
if (!infoStack.isEmpty() && pointerArgs) { |
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.
Don't need to check infoStack.isEmpty.
a2cebac
to
0b42010
Compare
@@ -123,7 +125,15 @@ void clear() { | |||
} | |||
} | |||
|
|||
private final ThreadLocal<HeapArgInfo> heapArgInfo; | |||
/* heapArgInfo holds a stack of HeapArgInfo structures. | |||
* createHeapArgInfo is a flag indicating whether a new entry should be created. |
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.
Please also describe when a new entry should be created.
@@ -123,7 +125,15 @@ void clear() { | |||
} | |||
} | |||
|
|||
private final ThreadLocal<HeapArgInfo> heapArgInfo; | |||
/* heapArgInfo holds a stack of HeapArgInfo structures. |
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.
Please also describe what the stack is for.
@@ -25,6 +25,8 @@ | |||
/*[IF JAVA_SPEC_VERSION >= 22]*/ | |||
import java.util.Arrays; | |||
/*[ENDIF] JAVA_SPEC_VERSION >= 22 */ | |||
import java.util.ArrayDeque; |
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 thought I saw this fixed before but these imports should be moved within JAVA_SPEC_VERSION >= 22.
/* heapArgInfo holds a per-thread stack of HeapArgInfo structures. | ||
* Each entry tracks Java references to heap-allocated memory segments | ||
* | ||
* createHeapArgInfo controls when a new entry should be created. |
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'm looking for this comment to explain more of the why and less of the what. As per the coding standards:
Comments are used to explain the intent of the code, not how it works. Well-written code shouldn't require comments to explain how it works. Comments should explain why it is necessary.
So for heapArgInfo you can just explain that its needed for recursive downcalls and for createHeapArgInfo that it is needed since there may be multiple pointer arguments for a downcall.
08ba733
to
22869c2
Compare
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.
Looks good to me. Can you please review @tajila ?
@AditiS11 I just noticed that the description is no longer correct, can you please update?
|
HeapArgInfo info; | ||
/* Check if current downcall has pointer arguments. */ | ||
boolean pointerArgs = true; | ||
if (createHeapArgInfo.get()) { |
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.
According to
* createHeapArgInfo controls when a new entry should be created.
* If true, a new HeapArgInfo is pushed for the current downcall.
* A single downcall may have multiple pointer arguments,
This would indicate that if if (createHeapArgInfo.get()) {
then pointerArgs = true;
?
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.
pointerArgs
is used to determine whether a HeapArgInfo entry exists for this downcall so it can be removed at the end of runNativeMethod after the downcall has completed.
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.
By defualt createHeapArgInfo
is set to true. If the downcall has pointer arguments then memSegmtOfPtrToLongArg
is called before runNative
where the flag will be set to false and a HeapArgInfo is pushed to the stack.
If createHeapArgInfo
is still true in runNative then it means the downcall has no pointer args hence pointerArgs
is set to false.
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.
Thanks for clarifying
* A single downcall may have multiple pointer arguments, | ||
* and all of them share the same entry. | ||
*/ | ||
private static final ThreadLocal<Deque<HeapArgInfo>> heapArgInfo = ThreadLocal.withInitial(ArrayDeque::new); |
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 this can be simplified:
-
Always create a
HeapArgInfo
in the entry to the MethodHandle (you'll need to find the entry point since the core handle is wrapped) and push it on to the local stack. And always pop it after the call irrespective of whether pointers are used or not. This removes the need forcreateHeapArgInfo
. -
In the constructor for
HeapArgInfo
do not allocate thebases
andoffsets
upfront, instead check and allocate whenappend()
is called. -
All the
if (info == null) {
checks can be removed. However, theif (info != null) {
still need to be kept.
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.
(you'll need to find the entry point since the core handle is wrapped)
For this part, might be easiest to warp the bounded handle in another handle that just create the heapArgInfo
and passes the args along. This can be done with MethodHandles.foldArguments(boundHandle, createHeapArginfoHandle)
. You'll need to bind this
(lookup.bind(this,
) to it so you can access the threadlocals etc.
The createHeapArginfoHandle
should match the args of the boundHandle
so that the initial arguments are just passed through.
return isFfiProtoOn ? getNativeMHWithInvokeCache(boundMH) : boundMH; | ||
/*[ELSE] JAVA_SPEC_VERSION >= 22 */ | ||
return boundMH; | ||
MethodHandle pushHeapArgInfoMH = MethodHandles.lookup().findStatic(InternalDowncallHandler.class, "pushHeapArgInfo", MethodType.methodType(void.class, InternalDowncallHandler.class)).bindTo(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.
can you create this in the same place as the others around line 530?
EDIT: the handle that is, you can still do foldArgs here.
@@ -914,8 +935,8 @@ Object runNativeMethod(Addressable downcallAddr, SegmentAllocator segmtAllocator | |||
returnStateMemBase, | |||
/*[ENDIF] JAVA_SPEC_VERSION >= 24 */ | |||
/*[IF JAVA_SPEC_VERSION >= 22]*/ | |||
(info != null) ? info.bases : null, | |||
(info != null) ? info.offsets : null, | |||
(info != null) ? info.getBases() : null, |
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.
Is. it possible that info == null
here? if not you can just pass bases
and offsets
without the null check
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.
It can't be null. Removed the null check.
|
||
HeapArgInfo(int size) { | ||
bases = new Object[size]; | ||
offsets = new long[size]; | ||
bases = null; |
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.
Please be consistent with use of this
. The index
field can also be private.
@@ -324,18 +346,14 @@ private final long memSegmtOfPtrToLongArg(MemorySegment argValue, LinkerOptions | |||
* to the unreachable objects. | |||
*/ | |||
if (info != null) { |
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.
There is no need for this line since each downcall will have a HeapArgInfo entry. Same for the other exception cases.
d067dfa
to
7970d41
Compare
@theresa-m Please take another look |
@@ -27,6 +27,8 @@ | |||
/*[ENDIF] JAVA_SPEC_VERSION >= 22 */ | |||
import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Deque; |
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 imports moved out of JAVA_SPEC_VERSION >= 22. Otherwise looks good.
Fix for Issue eclipse-openj9#21944 Fix for failing java/foreign/TestBufferStack.java test. Replaced single HeapArgInfo with a thread-local Deque. To track multiple argument sets for recursions. For every downcall a new entry is pushed onto the stack. The entry is removed from the stack after downcall completion.
7970d41
to
62cad9e
Compare
jenkins test sanity,extended.functional plinux jdk25 |
Fix for Issue #21944
Fix for failing java/foreign/TestBufferStack.java test.
Replaced single HeapArgInfo with a thread-local Deque.
To track multiple argument sets for recursions.
For every downcall a new entry is pushed onto the stack.
The entry is removed from the stack after downcall completion.