[K/N][runtime][interop] Refactoring to run cinterop in daemon
This commit is contained in:
@@ -110,6 +110,15 @@ JNIEXPORT jlong JNICALL Java_kotlinx_cinterop_JvmCallbacksKt_ffiTypeStruct0(JNIE
|
||||
return (jlong) res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: kotlinx_cinterop_JvmCallbacksKt
|
||||
* Method: ffiFreeTypeStruct0
|
||||
* Signature: (J)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_kotlinx_cinterop_JvmCallbacksKt_ffiFreeTypeStruct0(JNIEnv *env, jclass cls, jlong ptr) {
|
||||
if (ptr) free((void*)ptr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: kotlinx_cinterop_JvmCallbacksKt
|
||||
* Method: ffiCreateCif0
|
||||
@@ -120,6 +129,7 @@ JNIEXPORT jlong JNICALL Java_kotlinx_cinterop_JvmCallbacksKt_ffiCreateCif0(JNIEn
|
||||
if (res != NULL) {
|
||||
ffi_status status = ffi_prep_cif(res, FFI_DEFAULT_ABI, nArgs, (ffi_type*)rType, (ffi_type**)argTypes);
|
||||
if (status != FFI_OK) {
|
||||
free(res);
|
||||
if (status == FFI_BAD_TYPEDEF) {
|
||||
return -(jlong)1;
|
||||
} else if (status == FFI_BAD_ABI) {
|
||||
@@ -132,6 +142,15 @@ JNIEXPORT jlong JNICALL Java_kotlinx_cinterop_JvmCallbacksKt_ffiCreateCif0(JNIEn
|
||||
return (jlong) res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: kotlinx_cinterop_JvmCallbacksKt
|
||||
* Method: ffiFreeCif0
|
||||
* Signature: (J)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_kotlinx_cinterop_JvmCallbacksKt_ffiFreeCif0(JNIEnv *env, jclass cls, jlong ptr) {
|
||||
if (ptr) free((void*)ptr);
|
||||
}
|
||||
|
||||
static JavaVM *vm = NULL;
|
||||
|
||||
// Returns the JNI env which can be used by the caller.
|
||||
@@ -192,9 +211,10 @@ static void ffi_fun(ffi_cif *cif, void *ret, void **args, void *user_data) {
|
||||
* Method: ffiCreateClosure0
|
||||
* Signature: (JLjava/lang/Object;)J
|
||||
*/
|
||||
JNIEXPORT jlong JNICALL Java_kotlinx_cinterop_JvmCallbacksKt_ffiCreateClosure0(JNIEnv *env, jclass cls, jlong ffiCif, jobject userData) {
|
||||
JNIEXPORT jlong JNICALL Java_kotlinx_cinterop_JvmCallbacksKt_ffiCreateClosure0(JNIEnv *env, jclass cls, jlong ffiCif, jlong ffiClosure, jobject userData) {
|
||||
jobject userDataGlobalRef = (*env)->NewGlobalRef(env, userData);
|
||||
if (userDataGlobalRef == NULL) {
|
||||
*(ffi_closure**)ffiClosure = NULL;
|
||||
return (jlong)0;
|
||||
}
|
||||
|
||||
@@ -204,16 +224,36 @@ JNIEXPORT jlong JNICALL Java_kotlinx_cinterop_JvmCallbacksKt_ffiCreateClosure0(J
|
||||
void* res;
|
||||
ffi_closure *closure = ffi_closure_alloc(sizeof(ffi_closure), &res);
|
||||
if (closure == NULL) {
|
||||
(*env)->DeleteGlobalRef(env, userDataGlobalRef);
|
||||
*(ffi_closure**)ffiClosure = NULL;
|
||||
return (jlong)0;
|
||||
}
|
||||
ffi_status status = ffi_prep_closure_loc(closure, (ffi_cif*)ffiCif, ffi_fun, userDataPtr, res);
|
||||
if (status != FFI_OK) {
|
||||
(*env)->DeleteGlobalRef(env, userDataGlobalRef);
|
||||
ffi_closure_free(closure);
|
||||
*(ffi_closure**)ffiClosure = NULL;
|
||||
return -(jlong)1;
|
||||
}
|
||||
|
||||
*(ffi_closure**)ffiClosure = closure;
|
||||
return (jlong) res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: kotlinx_cinterop_JvmCallbacksKt
|
||||
* Method: ffiFreeClosure0
|
||||
* Signature: (J)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_kotlinx_cinterop_JvmCallbacksKt_ffiFreeClosure0(JNIEnv *env, jclass cls, jlong ptr) {
|
||||
if (ptr == NULL) return;
|
||||
ffi_closure *closure = (ffi_closure*)ptr;
|
||||
void* userDataPtr = closure->user_data;
|
||||
if (userDataPtr)
|
||||
(*env)->DeleteGlobalRef(env, (jobject)userDataPtr);
|
||||
ffi_closure_free(closure);
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: kotlinx_cinterop_JvmCallbacksKt
|
||||
* Method: newGlobalRef
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
package kotlinx.cinterop
|
||||
|
||||
import org.jetbrains.kotlin.konan.util.NativeMemoryAllocator
|
||||
import org.jetbrains.kotlin.konan.util.ThreadSafeDisposableHelper
|
||||
import sun.misc.Unsafe
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.function.LongConsumer
|
||||
import kotlin.reflect.KClass
|
||||
@@ -58,9 +61,60 @@ private fun getVariableCType(type: KType): CType<*>? {
|
||||
}
|
||||
}
|
||||
|
||||
private val structTypeCache = ConcurrentHashMap<Class<*>, CType<*>>()
|
||||
internal class Caches {
|
||||
val structTypeCache = ConcurrentHashMap<Class<*>, CType<*>>()
|
||||
val createdStaticFunctions = ConcurrentHashMap<FunctionSpec, CPointer<CFunction<*>>>()
|
||||
|
||||
private fun getStructCType(structClass: KClass<*>): CType<*> = structTypeCache.computeIfAbsent(structClass.java) {
|
||||
// TODO: No concurrent bag or something in Java?
|
||||
private val createdTypeStructs = mutableListOf<NativePtr>()
|
||||
private val createdCifs = mutableListOf<NativePtr>()
|
||||
private val createdClosures = mutableListOf<NativePtr>()
|
||||
|
||||
fun addTypeStruct(ptr: NativePtr) {
|
||||
synchronized(createdTypeStructs) { createdTypeStructs.add(ptr) }
|
||||
}
|
||||
|
||||
fun addCif(ptr: NativePtr) {
|
||||
synchronized(createdCifs) { createdCifs.add(ptr) }
|
||||
}
|
||||
|
||||
fun addClosure(ptr: NativePtr) {
|
||||
synchronized(createdClosures) { createdClosures.add(ptr) }
|
||||
}
|
||||
|
||||
fun disposeFfi() {
|
||||
createdTypeStructs.forEach { ffiFreeTypeStruct0(it) }
|
||||
createdCifs.forEach { ffiFreeCif0(it) }
|
||||
createdClosures.forEach { ffiFreeClosure0(it) }
|
||||
}
|
||||
}
|
||||
|
||||
@PublishedApi
|
||||
internal val jvmCallbacksDisposeHelper = ThreadSafeDisposableHelper(
|
||||
{
|
||||
NativeMemoryAllocator.init()
|
||||
Caches()
|
||||
},
|
||||
{
|
||||
try {
|
||||
it.disposeFfi()
|
||||
} finally {
|
||||
NativeMemoryAllocator.dispose()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
inline fun <R> usingJvmCInteropCallbacks(block: () -> R) = jvmCallbacksDisposeHelper.usingDisposable(block)
|
||||
|
||||
object JvmCInteropCallbacks {
|
||||
fun init() = jvmCallbacksDisposeHelper.create()
|
||||
fun dispose() = jvmCallbacksDisposeHelper.dispose()
|
||||
}
|
||||
|
||||
private val caches: Caches
|
||||
get() = jvmCallbacksDisposeHelper.holder ?: error("Caches hasn't been created")
|
||||
|
||||
private fun getStructCType(structClass: KClass<*>): CType<*> = caches.structTypeCache.computeIfAbsent(structClass.java) {
|
||||
// Note that struct classes are not supposed to be user-defined,
|
||||
// so they don't require to be checked strictly.
|
||||
|
||||
@@ -192,15 +246,13 @@ private fun isStatic(function: Function<*>): Boolean {
|
||||
}
|
||||
}
|
||||
|
||||
private data class FunctionSpec(val functionClass: Class<*>, val returnType: KType, val parameterTypes: List<KType>)
|
||||
|
||||
private val createdStaticFunctions = ConcurrentHashMap<FunctionSpec, CPointer<CFunction<*>>>()
|
||||
internal data class FunctionSpec(val functionClass: Class<*>, val returnType: KType, val parameterTypes: List<KType>)
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
@PublishedApi
|
||||
internal fun <F : Function<*>> staticCFunctionImpl(function: F, returnType: KType, vararg parameterTypes: KType): CPointer<CFunction<F>> {
|
||||
val spec = FunctionSpec(function.javaClass, returnType, parameterTypes.asList())
|
||||
return createdStaticFunctions.computeIfAbsent(spec) {
|
||||
return caches.createdStaticFunctions.computeIfAbsent(spec) {
|
||||
createStaticCFunction(function, spec)
|
||||
} as CPointer<CFunction<F>>
|
||||
}
|
||||
@@ -300,8 +352,8 @@ private inline fun ffiClosureImpl(
|
||||
*
|
||||
* This description omits the details that are irrelevant for the ABI.
|
||||
*/
|
||||
private abstract class CType<T> internal constructor(val ffiType: ffi_type) {
|
||||
internal constructor(ffiTypePtr: Long) : this(interpretPointed<ffi_type>(ffiTypePtr))
|
||||
internal abstract class CType<T> constructor(val ffiType: ffi_type) {
|
||||
constructor(ffiTypePtr: Long) : this(interpretPointed<ffi_type>(ffiTypePtr))
|
||||
abstract fun read(location: NativePtr): T
|
||||
abstract fun write(location: NativePtr, value: T): Unit
|
||||
}
|
||||
@@ -384,7 +436,7 @@ internal class ffi_type(rawPtr: NativePtr) : COpaque(rawPtr)
|
||||
/**
|
||||
* Reference to `ffi_cif` struct instance.
|
||||
*/
|
||||
internal class ffi_cif(rawPtr: NativePtr) : COpaque(rawPtr)
|
||||
private class ffi_cif(rawPtr: NativePtr) : COpaque(rawPtr)
|
||||
|
||||
private external fun ffiTypeVoid(): Long
|
||||
private external fun ffiTypeUInt8(): Long
|
||||
@@ -398,6 +450,7 @@ private external fun ffiTypeSInt64(): Long
|
||||
private external fun ffiTypePointer(): Long
|
||||
|
||||
private external fun ffiTypeStruct0(elements: Long): Long
|
||||
private external fun ffiFreeTypeStruct0(ptr: Long)
|
||||
|
||||
/**
|
||||
* Allocates and initializes `ffi_type` describing the struct.
|
||||
@@ -405,15 +458,19 @@ private external fun ffiTypeStruct0(elements: Long): Long
|
||||
* @param elements types of the struct elements
|
||||
*/
|
||||
private fun ffiTypeStruct(elementTypes: List<ffi_type>): ffi_type {
|
||||
val elements = persistentHeap.allocArrayOfPointersTo(*elementTypes.toTypedArray(), null)
|
||||
val elements = nativeHeap.allocArrayOfPointersTo(*elementTypes.toTypedArray(), null)
|
||||
val res = ffiTypeStruct0(elements.rawValue)
|
||||
if (res == 0L) {
|
||||
throw OutOfMemoryError()
|
||||
}
|
||||
|
||||
caches.addTypeStruct(res)
|
||||
|
||||
return interpretPointed(res)
|
||||
}
|
||||
|
||||
private external fun ffiCreateCif0(nArgs: Int, rType: Long, argTypes: Long): Long
|
||||
private external fun ffiFreeCif0(ptr: Long)
|
||||
|
||||
/**
|
||||
* Creates and prepares an `ffi_cif`.
|
||||
@@ -425,7 +482,7 @@ private external fun ffiCreateCif0(nArgs: Int, rType: Long, argTypes: Long): Lon
|
||||
*/
|
||||
private fun ffiCreateCif(returnType: ffi_type, paramTypes: List<ffi_type>): ffi_cif {
|
||||
val nArgs = paramTypes.size
|
||||
val argTypes = persistentHeap.allocArrayOfPointersTo(*paramTypes.toTypedArray(), null)
|
||||
val argTypes = nativeHeap.allocArrayOfPointersTo(*paramTypes.toTypedArray(), null)
|
||||
val res = ffiCreateCif0(nArgs, returnType.rawPtr, argTypes.rawValue)
|
||||
|
||||
when (res) {
|
||||
@@ -435,10 +492,13 @@ private fun ffiCreateCif(returnType: ffi_type, paramTypes: List<ffi_type>): ffi_
|
||||
-3L -> throw Error("libffi error occurred")
|
||||
}
|
||||
|
||||
caches.addCif(res)
|
||||
|
||||
return interpretPointed(res)
|
||||
}
|
||||
|
||||
private external fun ffiCreateClosure0(ffiCif: Long, userData: Any): Long
|
||||
private external fun ffiCreateClosure0(ffiCif: Long, ffiClosure: Long, userData: Any): Long
|
||||
private external fun ffiFreeClosure0(ptr: Long)
|
||||
|
||||
/**
|
||||
* Uses libffi to allocate a native function which will call [impl] when invoked.
|
||||
@@ -446,34 +506,27 @@ private external fun ffiCreateClosure0(ffiCif: Long, userData: Any): Long
|
||||
* @param ffiCif describes the type of the function to create
|
||||
*/
|
||||
private fun ffiCreateClosure(ffiCif: ffi_cif, impl: FfiClosureImpl): NativePtr {
|
||||
val res = ffiCreateClosure0(ffiCif.rawPtr, userData = impl)
|
||||
val ffiClosure = nativeHeap.alloc(Long.SIZE_BYTES, 8)
|
||||
|
||||
when (res) {
|
||||
0L -> throw OutOfMemoryError()
|
||||
-1L -> throw Error("libffi error occurred")
|
||||
try {
|
||||
val res = ffiCreateClosure0(ffiCif.rawPtr, ffiClosure.rawPtr, userData = impl)
|
||||
|
||||
when (res) {
|
||||
0L -> throw OutOfMemoryError()
|
||||
-1L -> throw Error("libffi error occurred")
|
||||
}
|
||||
|
||||
caches.addClosure(unsafe.getLong(ffiClosure.rawPtr))
|
||||
|
||||
return res
|
||||
} finally {
|
||||
nativeHeap.free(ffiClosure)
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// Callbacks are globally cached and outlive the memory allocated by nativeHeap,
|
||||
// which gets forcibly reclaimed at the end of the compiler invocation.
|
||||
// So use an ad hoc allocator that is not affected.
|
||||
// Note: this is mostly a workaround, but proper solution would require a significant rework of this machinery.
|
||||
private object persistentHeap : NativeFreeablePlacement {
|
||||
override fun alloc(size: Long, align: Int): NativePointed {
|
||||
return interpretOpaquePointed(
|
||||
nativeMemUtils.allocRaw(
|
||||
if (size == 0L) 1L else size, // It is a hack: `nativeMemUtils.allocRaw` can't allocate zero bytes
|
||||
align
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun free(mem: NativePtr) {
|
||||
nativeMemUtils.freeRaw(mem)
|
||||
}
|
||||
|
||||
private val unsafe = with(Unsafe::class.java.getDeclaredField("theUnsafe")) {
|
||||
isAccessible = true
|
||||
return@with this.get(null) as Unsafe
|
||||
}
|
||||
|
||||
private external fun newGlobalRef(any: Any): Long
|
||||
|
||||
Reference in New Issue
Block a user