-
Notifications
You must be signed in to change notification settings - Fork 769
Open
Labels
comp:vmproject:panamaUsed to track Project Panama related workUsed to track Project Panama related work
Description
While looking into one of the test case failure within jextract [1], I found that when a memory segment allocated using Arena.ofConfined()
[2] was used to pass to make a downcall, JVM did not throw java.lang.WrongThreadException
as expected. Here is the simple unit test to reproduce this behavior.
1. Java Test Case : ConfinedMemorySegmentDowncallTest.java
import java.lang.foreign.Arena;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.ValueLayout;
import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.Linker;
import java.lang.foreign.SymbolLookup;
import static java.lang.foreign.ValueLayout.ADDRESS;
import static java.lang.foreign.ValueLayout.JAVA_BOOLEAN;
import static java.lang.foreign.ValueLayout.JAVA_BYTE;
import static java.lang.foreign.ValueLayout.JAVA_CHAR;
import static java.lang.foreign.ValueLayout.JAVA_DOUBLE;
import static java.lang.foreign.ValueLayout.JAVA_FLOAT;
import static java.lang.foreign.ValueLayout.JAVA_INT;
import static java.lang.foreign.ValueLayout.JAVA_LONG;
import static java.lang.foreign.ValueLayout.JAVA_SHORT;
import java.lang.invoke.MethodHandle;
public class ConfinedMemorySegmentDowncallTest {
static {
System.loadLibrary("test");
}
private static Linker linker = Linker.nativeLinker();
private static final SymbolLookup nativeLibLookup = SymbolLookup.loaderLookup();
private static final SymbolLookup defaultLibLookup = linker.defaultLookup();
private static Arena confinedArena;
public static MemorySegment segment;
public static void makeDownCall() throws Throwable {
FunctionDescriptor fd = FunctionDescriptor.of(JAVA_INT, ADDRESS, JAVA_INT);
MemorySegment functionSymbol = nativeLibLookup.find("addIntsFromArray").get();
MethodHandle mh = linker.downcallHandle(functionSymbol, fd);
int result = (int)mh.invokeExact(segment, 5);
System.out.println(Thread.currentThread().getName()+" result = "+result);
}
public static void main(String[] string) {
try {
confinedArena = Arena.ofConfined();
segment = confinedArena.allocate(ValueLayout.JAVA_INT, 5);
for (int j=0; j < 5; j++) {
segment.set(ValueLayout.JAVA_INT, j * ValueLayout.JAVA_INT.byteSize(), (j+1)*10);
}
makeDownCall();
} catch (Throwable ex) {
ex.printStackTrace();
}
Thread thread = new Thread(() -> {
try {
makeDownCall();
} catch (Throwable ex) {
ex.printStackTrace();
}
});
thread.start();
}
}
2. Native C Code
``` int addIntsFromArray(int *ptr , int elements) { int sum = 0; for (int i=0; i < elements; i++) { sum += ptr[i]; } return sum; } ```3. Compile and build test
``` $ /bin/javac ConfinedMemorySegmentDowncallTest.java $ gcc -shared -fPIC -o libtest.so test.c ```Output using Temurin
<temuring-jdk-24>/bin/java --enable-native-access=ALL-UNNAMED -Djava.library.path=`pwd` -cp . ConfinedMemorySegmentDowncallTest
main result = 150
java.lang.WrongThreadException: Attempted access outside owning thread
at java.base/jdk.internal.foreign.MemorySessionImpl.wrongThread(MemorySessionImpl.java:322)
at java.base/jdk.internal.misc.ScopedMemoryAccess$ScopedAccessError.newRuntimeException(ScopedMemoryAccess.java:114)
at java.base/jdk.internal.foreign.MemorySessionImpl.checkValidState(MemorySessionImpl.java:217)
at java.base/jdk.internal.foreign.ConfinedSession.acquire0(ConfinedSession.java:53)
at ConfinedMemorySegmentDowncallTest.makeDownCall(ConfinedMemorySegmentDowncallTest.java:31)
at ConfinedMemorySegmentDowncallTest.lambda$main$0(ConfinedMemorySegmentDowncallTest.java:50)
at java.base/java.lang.Thread.run(Thread.java:1447)
Output using OpenJ9
<openj9-jdk24>/bin/java --enable-native-access=ALL-UNNAMED -Djava.library.path=`pwd` -cp . ConfinedMemorySegmentDowncallTest
main result = 150
Thread-0 result = 150
Following code behaves same with OpenJ9 and Temuring build (i.e, I see both OpenJ9 and Temurin throwing WrongThreadAcception when other thread then owner thread is trying to read from it.
ConfinedMemorySegmentTest.java
``` import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout;public class ConfinedMemorySegmentTest {
private static Arena confinedArena;
public static MemorySegment segment;
public static void readSegment(int size) throws Throwable {
for(int j = 0; j < size; j++) {
int value = segment.get(ValueLayout.JAVA_INT, j * ValueLayout.JAVA_INT.byteSize());
System.out.println(Thread.currentThread().getName()+" value read = "+value);
}
}
public static void main(String[] string) {
try {
confinedArena = Arena.ofConfined();
segment = confinedArena.allocate(ValueLayout.JAVA_INT, 5);
for (int j=0; j < 5; j++) {
segment.set(ValueLayout.JAVA_INT, j * ValueLayout.JAVA_INT.byteSize(), (j+1)*10);
}
readSegment(5);
} catch (Throwable ex) {
ex.printStackTrace();
}
Thread thread = new Thread(() -> {
try {
readSegment(5);
} catch (Throwable ex) {
ex.printStackTrace();
}
});
thread.start();
}
}
</details>
[1]. https://github.com/openjdk/jextract/blob/267a0abe441ba9bf2da3cfebd3dfd05c26bf464c/test/jtreg/generator/reachableException/TestReachableException.java#L61
[2]. https://github.com/ibmruntimes/openj9-openjdk-jdk24/blob/905cf12f2c9fa3c4515234d5f07d14d9d4356cfb/src/java.base/share/classes/java/lang/foreign/Arena.java#L256-L258
Metadata
Metadata
Assignees
Labels
comp:vmproject:panamaUsed to track Project Panama related workUsed to track Project Panama related work