Skip to content
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

JDK-8244601: Cleanup support for upcall handles #154

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/hotspot/share/prims/upcallStubs.cpp
Expand Up @@ -25,7 +25,7 @@
#include "runtime/jniHandles.inline.hpp"
#include "runtime/interfaceSupport.inline.hpp"

JVM_ENTRY(static jboolean, UH_FreeUpcallStub(JNIEnv *env, jobject _unused, jlong addr))
JVM_ENTRY(static jboolean, UH_FreeUpcallStub0(JNIEnv *env, jobject _unused, jlong addr))
//acquire code cache lock
MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
//find code blob
Expand All @@ -47,7 +47,7 @@ JVM_END

// These are the native methods on jdk.internal.foreign.NativeInvoker.
static JNINativeMethod UH_methods[] = {
{CC "freeUpcallStub", CC "(J)Z", FN_PTR(UH_FreeUpcallStub)}
{CC "freeUpcallStub0", CC "(J)Z", FN_PTR(UH_FreeUpcallStub0)}
};

/**
Expand Down
Expand Up @@ -74,25 +74,17 @@ public interface SystemABI {
MethodHandle downcallHandle(MemoryAddress symbol, MethodType type, FunctionDescriptor function);

/**
* Obtain the pointer to a native stub which
* can be used to upcall into a given method handle.
* Allocates a native stub segment which contains executable code to upcall into a given method handle.
* As such, the base address of the returned stub segment can be passed to other foreign functions
* (as a function pointer). The returned segment is <em>not</em> thread-confined, and it only features
* the {@link MemorySegment#CLOSE} access mode. When the returned segment is closed,
* the corresponding native stub will be deallocated.
*
* @param target the target method handle.
* @param function the function descriptor.
* @return the upcall symbol.
* @return the native stub segment.
*/
MemoryAddress upcallStub(MethodHandle target, FunctionDescriptor function);

/**
* Frees an upcall stub given it's memory address.
*
* @param address the memory address of the upcall stub, returned from
* {@link SystemABI#upcallStub(MethodHandle, FunctionDescriptor)}.
* @throws IllegalArgumentException if the given address is not a valid upcall stub address.
*/
default void freeUpcallStub(MemoryAddress address) {
UpcallStubs.freeUpcallStub(address);
}
MemorySegment upcallStub(MethodHandle target, FunctionDescriptor function);

/**
* Returns the name of this ABI.
Expand Down
Expand Up @@ -25,26 +25,29 @@
package jdk.internal.foreign.abi;

import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemorySegment;
import jdk.internal.foreign.MemoryAddressImpl;
import jdk.internal.foreign.NativeMemorySegmentImpl;

public class UpcallStubs {

public static MemoryAddress upcallAddress(UpcallHandler handler) {
long addr = handler.entryPoint();
return MemoryAddress.ofLong(addr);
}
public static MemorySegment upcallAddress(UpcallHandler handler) {
long stubAddress = handler.entryPoint();
return NativeMemorySegmentImpl.makeNativeSegmentUnchecked(MemoryAddress.ofLong(stubAddress),
0, null, () -> freeUpcallStub(stubAddress), null)
.withAccessModes(MemorySegment.CLOSE);
};

public static void freeUpcallStub(MemoryAddress address) {
MemoryAddressImpl ma = (MemoryAddressImpl) address;
if (ma.unsafeGetBase() != null || !freeUpcallStub(ma.unsafeGetOffset())) {
throw new IllegalArgumentException("Not a stub address: " + address);
private static void freeUpcallStub(long stubAddress) {
if (!freeUpcallStub0(stubAddress)) {
throw new IllegalStateException("Not a stub address: " + stubAddress);
}
}

// natives

// returns true if the stub was found (and freed)
private static native boolean freeUpcallStub(long addr);
private static native boolean freeUpcallStub0(long addr);

private static native void registerNatives();
static {
Expand Down
Expand Up @@ -28,6 +28,7 @@
import jdk.incubator.foreign.FunctionDescriptor;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.SystemABI;
import jdk.internal.foreign.abi.*;

Expand All @@ -54,7 +55,7 @@ public MethodHandle downcallHandle(MemoryAddress symbol, MethodType type, Functi
}

@Override
public MemoryAddress upcallStub(MethodHandle target, FunctionDescriptor function) {
public MemorySegment upcallStub(MethodHandle target, FunctionDescriptor function) {
return UpcallStubs.upcallAddress(CallArranger.arrangeUpcall(target, target.type(), function));
}

Expand Down
Expand Up @@ -27,6 +27,7 @@
import jdk.incubator.foreign.FunctionDescriptor;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.SystemABI;
import jdk.internal.foreign.abi.*;

Expand Down Expand Up @@ -59,7 +60,7 @@ public MethodHandle downcallHandle(MemoryAddress symbol, MethodType type, Functi
}

@Override
public MemoryAddress upcallStub(MethodHandle target, FunctionDescriptor function) {
public MemorySegment upcallStub(MethodHandle target, FunctionDescriptor function) {
return UpcallStubs.upcallAddress(CallArranger.arrangeUpcall(target, target.type(), function));
}

Expand Down
Expand Up @@ -27,6 +27,7 @@
import jdk.incubator.foreign.FunctionDescriptor;
import jdk.incubator.foreign.MemoryAddress;
import jdk.incubator.foreign.MemoryLayout;
import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.SystemABI;
import jdk.internal.foreign.abi.x64.sysv.ArgumentClassImpl;
import jdk.internal.foreign.abi.*;
Expand Down Expand Up @@ -61,7 +62,7 @@ public MethodHandle downcallHandle(MemoryAddress symbol, MethodType type, Functi
}

@Override
public MemoryAddress upcallStub(MethodHandle target, FunctionDescriptor function) {
public MemorySegment upcallStub(MethodHandle target, FunctionDescriptor function) {
return UpcallStubs.upcallAddress(CallArranger.arrangeUpcall(target, target.type(), function));
}

Expand Down
7 changes: 1 addition & 6 deletions test/jdk/java/foreign/CallGeneratorHelper.java
Expand Up @@ -333,12 +333,7 @@ static void cleanup(Object arg) {
if (arg instanceof MemoryAddress) {
cleanup(((MemoryAddress)arg).segment());
} else if (arg instanceof MemorySegment) {
try {
((MemorySegment) arg).close();
} catch (UnsupportedOperationException e) {
assertTrue(e.getMessage().contains("Required access mode"));
// fine, NOTHING segment for upcall stubs
}
((MemorySegment) arg).close();
}
}

Expand Down
6 changes: 3 additions & 3 deletions test/jdk/java/foreign/StdLibTest.java
Expand Up @@ -308,9 +308,9 @@ int[] qsort(int[] arr) throws Throwable {
.forEach(i -> intArrHandle.set(nativeArr.baseAddress(), i, arr[i]));

//call qsort
MemoryAddress qsortUpcallAddr = abi.upcallStub(qsortCompar.bindTo(nativeArr), qsortComparFunction);
qsort.invokeExact(nativeArr.baseAddress(), seq.elementCount().getAsLong(), C_INT.byteSize(), qsortUpcallAddr);
abi.freeUpcallStub(qsortUpcallAddr);
try (MemorySegment qsortUpcallStub = abi.upcallStub(qsortCompar.bindTo(nativeArr), qsortComparFunction)) {
qsort.invokeExact(nativeArr.baseAddress(), seq.elementCount().getAsLong(), C_INT.byteSize(), qsortUpcallStub.baseAddress());
}

//convert back to Java array
return LongStream.range(0, arr.length)
Expand Down
25 changes: 17 additions & 8 deletions test/jdk/java/foreign/TestUpcall.java
Expand Up @@ -41,6 +41,8 @@
import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.SystemABI;
import jdk.incubator.foreign.ValueLayout;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

import java.lang.invoke.MethodHandle;
Expand All @@ -63,8 +65,6 @@ public class TestUpcall extends CallGeneratorHelper {

static LibraryLookup lib = LibraryLookup.ofLibrary("TestUpcall");
static SystemABI abi = SystemABI.getSystemABI();
static final MemoryAddress dummyAddress;
static final Cleaner cleaner = Cleaner.create();

static MethodHandle DUMMY;
static MethodHandle PASS_AND_SAVE;
Expand All @@ -73,14 +73,22 @@ public class TestUpcall extends CallGeneratorHelper {
try {
DUMMY = MethodHandles.lookup().findStatic(TestUpcall.class, "dummy", MethodType.methodType(void.class));
PASS_AND_SAVE = MethodHandles.lookup().findStatic(TestUpcall.class, "passAndSave", MethodType.methodType(Object.class, Object[].class, AtomicReference.class));

dummyAddress = abi.upcallStub(DUMMY, FunctionDescriptor.ofVoid());
cleaner.register(dummyAddress, () -> abi.freeUpcallStub(dummyAddress));
} catch (Throwable ex) {
throw new IllegalStateException(ex);
}
}

static MemoryAddress dummyAddress;

@BeforeClass
void setup() {
dummyAddress = abi.upcallStub(DUMMY, FunctionDescriptor.ofVoid()).baseAddress();
}

@AfterClass
void teardown() {
dummyAddress.segment().close();
}

@Test(dataProvider="functions", dataProviderClass=CallGeneratorHelper.class)
public void testUpcalls(String fName, Ret ret, List<ParamType> paramTypes, List<StructFieldType> fields) throws Throwable {
Expand All @@ -96,7 +104,9 @@ public void testUpcalls(String fName, Ret ret, List<ParamType> paramTypes, List<
returnChecks.forEach(c -> c.accept(res));
}
for (Object arg : args) {
cleanup(arg);
if (arg != dummyAddress) {
cleanup(arg);
}
}
}

Expand Down Expand Up @@ -168,8 +178,7 @@ static MemoryAddress makeCallback(Ret ret, List<ParamType> params, List<StructFi
FunctionDescriptor func = ret != Ret.VOID
? FunctionDescriptor.of(firstlayout, paramLayouts)
: FunctionDescriptor.ofVoid(paramLayouts);
MemoryAddress stub = abi.upcallStub(mh, func);
cleaner.register(stub, () -> abi.freeUpcallStub(stub));
MemoryAddress stub = abi.upcallStub(mh, func).baseAddress();
return stub;
}

Expand Down
31 changes: 11 additions & 20 deletions test/jdk/java/foreign/TestUpcallStubs.java
Expand Up @@ -39,6 +39,7 @@
import java.lang.invoke.VarHandle;

import static jdk.incubator.foreign.MemoryLayouts.JAVA_INT;
import static org.testng.Assert.assertFalse;

public class TestUpcallStubs {

Expand All @@ -54,40 +55,30 @@ public class TestUpcallStubs {
}
}

private static MemoryAddress getStub() {
private static MemorySegment getStub() {
return abi.upcallStub(MH_dummy, FunctionDescriptor.ofVoid());
}

@Test(expectedExceptions = UnsupportedOperationException.class)
public void testNoAccess() {
MemoryAddress stub = getStub();
try {
try (MemorySegment stub = getStub()) {
VarHandle vh = JAVA_INT.varHandle(int.class);
vh.set(stub, 10);
} finally {
abi.freeUpcallStub(stub);
vh.set(stub.baseAddress(), 10);
}
}

@Test
public void testFree() {
MemoryAddress stub = getStub();
abi.freeUpcallStub(stub);
MemorySegment stub = getStub();
stub.close();
assertFalse(stub.isAlive());
}

@Test(expectedExceptions = IllegalArgumentException.class,
expectedExceptionsMessageRegExp = ".*Not a stub address: .*")
@Test(expectedExceptions = IllegalStateException.class)
public void testAlreadyFreed() {
MemoryAddress stub = getStub();
abi.freeUpcallStub(stub);
abi.freeUpcallStub(stub); // should fail
}

@Test(expectedExceptions = IllegalArgumentException.class,
expectedExceptionsMessageRegExp = ".*Not a stub address: .*",
dataProvider = "badAddresses")
public void testCanNotFree(MemoryAddress ma) {
abi.freeUpcallStub(ma);
MemorySegment stub = getStub();
stub.close();
stub.close(); // should fail
}

@DataProvider
Expand Down