From 353f82f9e61b7cf72a90538a8f9105903595dc05 Mon Sep 17 00:00:00 2001 From: Igor Chevdar Date: Fri, 26 Nov 2021 14:05:59 +0500 Subject: [PATCH] Revert "[K/N][runtime][interop] Refactoring to run cinterop in daemon" This reverts commit 47858126da7de5ef8c8cec81ac1cb2dfd8fdba73. --- .../native/interop/indexer/IndexerTests.kt | 16 --- .../Runtime/src/callbacks/c/callbacks.c | 42 +----- .../kotlin/kotlinx/cinterop/JvmCallbacks.kt | 125 +++++------------- .../kotlin/native/interop/gen/jvm/main.kt | 32 ++--- kotlin-native/backend.native/build.gradle | 15 --- .../jetbrains/kotlin/backend/konan/Context.kt | 11 +- .../kotlin/backend/konan/KonanDriver.kt | 16 +-- .../kotlin/backend/konan/ToplevelPhases.kt | 3 +- .../backend/konan/llvm/BitcodePhases.kt | 10 ++ kotlin-native/common/build.gradle.kts | 4 - kotlin-native/common/src/env/cpp/Env.cpp | 23 ---- kotlin-native/common/src/env/headers/Env.h | 18 --- .../src/main/kotlin/NativeMemoryAllocator.kt | 66 ++------- .../jetbrains/kotlin/cli/utilities/main.kt | 10 +- 14 files changed, 88 insertions(+), 303 deletions(-) delete mode 100644 kotlin-native/common/src/env/cpp/Env.cpp delete mode 100644 kotlin-native/common/src/env/headers/Env.h diff --git a/kotlin-native/Interop/Indexer/src/test/kotlin/org/jetbrains/kotlin/native/interop/indexer/IndexerTests.kt b/kotlin-native/Interop/Indexer/src/test/kotlin/org/jetbrains/kotlin/native/interop/indexer/IndexerTests.kt index d87e8500186..33fa6be8fad 100644 --- a/kotlin-native/Interop/Indexer/src/test/kotlin/org/jetbrains/kotlin/native/interop/indexer/IndexerTests.kt +++ b/kotlin-native/Interop/Indexer/src/test/kotlin/org/jetbrains/kotlin/native/interop/indexer/IndexerTests.kt @@ -5,29 +5,13 @@ package org.jetbrains.kotlin.native.interop.indexer -import kotlinx.cinterop.JvmCInteropCallbacks -import org.jetbrains.kotlin.konan.util.NativeMemoryAllocator import java.io.File -import kotlin.test.AfterTest -import kotlin.test.BeforeTest open class IndexerTests { init { System.load(System.getProperty("kotlin.native.llvm.libclang")) } - @BeforeTest - fun init() { - NativeMemoryAllocator.init() - JvmCInteropCallbacks.init() - } - - @AfterTest - fun dispose() { - JvmCInteropCallbacks.dispose() - NativeMemoryAllocator.dispose() - } - class TempFiles(name: String) { private val tempRootDir = System.getProperty("kotlin.native.interop.indexer.temp") ?: System.getProperty("java.io.tmpdir") ?: "." diff --git a/kotlin-native/Interop/Runtime/src/callbacks/c/callbacks.c b/kotlin-native/Interop/Runtime/src/callbacks/c/callbacks.c index 5ae6fa4e306..d187d71a426 100644 --- a/kotlin-native/Interop/Runtime/src/callbacks/c/callbacks.c +++ b/kotlin-native/Interop/Runtime/src/callbacks/c/callbacks.c @@ -110,15 +110,6 @@ 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 @@ -129,7 +120,6 @@ 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) { @@ -142,15 +132,6 @@ 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. @@ -211,10 +192,9 @@ 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, jlong ffiClosure, jobject userData) { +JNIEXPORT jlong JNICALL Java_kotlinx_cinterop_JvmCallbacksKt_ffiCreateClosure0(JNIEnv *env, jclass cls, jlong ffiCif, jobject userData) { jobject userDataGlobalRef = (*env)->NewGlobalRef(env, userData); if (userDataGlobalRef == NULL) { - *(ffi_closure**)ffiClosure = NULL; return (jlong)0; } @@ -224,36 +204,16 @@ 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 diff --git a/kotlin-native/Interop/Runtime/src/jvm/kotlin/kotlinx/cinterop/JvmCallbacks.kt b/kotlin-native/Interop/Runtime/src/jvm/kotlin/kotlinx/cinterop/JvmCallbacks.kt index 9264b5138e3..3f6b9d5acf8 100644 --- a/kotlin-native/Interop/Runtime/src/jvm/kotlin/kotlinx/cinterop/JvmCallbacks.kt +++ b/kotlin-native/Interop/Runtime/src/jvm/kotlin/kotlinx/cinterop/JvmCallbacks.kt @@ -16,9 +16,6 @@ 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 @@ -61,60 +58,9 @@ private fun getVariableCType(type: KType): CType<*>? { } } -internal class Caches { - val structTypeCache = ConcurrentHashMap, CType<*>>() - val createdStaticFunctions = ConcurrentHashMap>>() +private val structTypeCache = ConcurrentHashMap, CType<*>>() - // TODO: No concurrent bag or something in Java? - private val createdTypeStructs = mutableListOf() - private val createdCifs = mutableListOf() - private val createdClosures = mutableListOf() - - 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 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) { +private fun getStructCType(structClass: KClass<*>): CType<*> = structTypeCache.computeIfAbsent(structClass.java) { // Note that struct classes are not supposed to be user-defined, // so they don't require to be checked strictly. @@ -246,13 +192,15 @@ private fun isStatic(function: Function<*>): Boolean { } } -internal data class FunctionSpec(val functionClass: Class<*>, val returnType: KType, val parameterTypes: List) +private data class FunctionSpec(val functionClass: Class<*>, val returnType: KType, val parameterTypes: List) + +private val createdStaticFunctions = ConcurrentHashMap>>() @Suppress("UNCHECKED_CAST") @PublishedApi internal fun > staticCFunctionImpl(function: F, returnType: KType, vararg parameterTypes: KType): CPointer> { val spec = FunctionSpec(function.javaClass, returnType, parameterTypes.asList()) - return caches.createdStaticFunctions.computeIfAbsent(spec) { + return createdStaticFunctions.computeIfAbsent(spec) { createStaticCFunction(function, spec) } as CPointer> } @@ -352,8 +300,8 @@ private inline fun ffiClosureImpl( * * This description omits the details that are irrelevant for the ABI. */ -internal abstract class CType constructor(val ffiType: ffi_type) { - constructor(ffiTypePtr: Long) : this(interpretPointed(ffiTypePtr)) +private abstract class CType internal constructor(val ffiType: ffi_type) { + internal constructor(ffiTypePtr: Long) : this(interpretPointed(ffiTypePtr)) abstract fun read(location: NativePtr): T abstract fun write(location: NativePtr, value: T): Unit } @@ -436,7 +384,7 @@ internal class ffi_type(rawPtr: NativePtr) : COpaque(rawPtr) /** * Reference to `ffi_cif` struct instance. */ -private class ffi_cif(rawPtr: NativePtr) : COpaque(rawPtr) +internal class ffi_cif(rawPtr: NativePtr) : COpaque(rawPtr) private external fun ffiTypeVoid(): Long private external fun ffiTypeUInt8(): Long @@ -450,7 +398,6 @@ 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. @@ -458,19 +405,15 @@ private external fun ffiFreeTypeStruct0(ptr: Long) * @param elements types of the struct elements */ private fun ffiTypeStruct(elementTypes: List): ffi_type { - val elements = nativeHeap.allocArrayOfPointersTo(*elementTypes.toTypedArray(), null) + val elements = persistentHeap.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`. @@ -482,7 +425,7 @@ private external fun ffiFreeCif0(ptr: Long) */ private fun ffiCreateCif(returnType: ffi_type, paramTypes: List): ffi_cif { val nArgs = paramTypes.size - val argTypes = nativeHeap.allocArrayOfPointersTo(*paramTypes.toTypedArray(), null) + val argTypes = persistentHeap.allocArrayOfPointersTo(*paramTypes.toTypedArray(), null) val res = ffiCreateCif0(nArgs, returnType.rawPtr, argTypes.rawValue) when (res) { @@ -492,13 +435,10 @@ private fun ffiCreateCif(returnType: ffi_type, paramTypes: List): ffi_ -3L -> throw Error("libffi error occurred") } - caches.addCif(res) - return interpretPointed(res) } -private external fun ffiCreateClosure0(ffiCif: Long, ffiClosure: Long, userData: Any): Long -private external fun ffiFreeClosure0(ptr: Long) +private external fun ffiCreateClosure0(ffiCif: Long, userData: Any): Long /** * Uses libffi to allocate a native function which will call [impl] when invoked. @@ -506,27 +446,34 @@ private external fun ffiFreeClosure0(ptr: Long) * @param ffiCif describes the type of the function to create */ private fun ffiCreateClosure(ffiCif: ffi_cif, impl: FfiClosureImpl): NativePtr { - val ffiClosure = nativeHeap.alloc(Long.SIZE_BYTES, 8) + val res = ffiCreateClosure0(ffiCif.rawPtr, userData = impl) - 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) + when (res) { + 0L -> throw OutOfMemoryError() + -1L -> throw Error("libffi error occurred") } + + return res } -private val unsafe = with(Unsafe::class.java.getDeclaredField("theUnsafe")) { - isAccessible = true - return@with this.get(null) as Unsafe +// 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 external fun newGlobalRef(any: Any): Long diff --git a/kotlin-native/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/main.kt b/kotlin-native/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/main.kt index f73e9c0b1fa..85833c45010 100644 --- a/kotlin-native/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/main.kt +++ b/kotlin-native/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/main.kt @@ -16,7 +16,6 @@ package org.jetbrains.kotlin.native.interop.gen.jvm -import kotlinx.cinterop.usingJvmCInteropCallbacks import org.jetbrains.kotlin.konan.TempFiles import org.jetbrains.kotlin.konan.exec.Command import org.jetbrains.kotlin.konan.util.DefFile @@ -34,7 +33,6 @@ import org.jetbrains.kotlin.konan.target.CompilerOutputKind import org.jetbrains.kotlin.konan.target.Distribution import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.konan.util.KonanHomeProvider -import org.jetbrains.kotlin.konan.util.usingNativeMemoryAllocator import org.jetbrains.kotlin.library.KotlinLibrary import org.jetbrains.kotlin.library.packageFqName import org.jetbrains.kotlin.library.resolver.TopologicalLibraryOrder @@ -64,22 +62,22 @@ fun main(args: Array) { val arguments = FullCInteropArguments() arguments.argParser.parse(args) val flavorName = arguments.flavor - processCLibSafe(flavorName, arguments, InternalInteropOptions(arguments.generated, arguments.natives)) + processCLib(flavorName, arguments, InternalInteropOptions(arguments.generated, arguments.natives)) } fun interop( flavor: String, args: Array, additionalArgs: InternalInteropOptions -): Array? = when (flavor) { - "jvm", "native" -> { - val cinteropArguments = CInteropArguments() - cinteropArguments.argParser.parse(args) - val platform = KotlinPlatform.values().single { it.name.equals(flavor, ignoreCase = true) } - processCLibSafe(platform, cinteropArguments, additionalArgs) - } - "wasm" -> processIdlLib(args, additionalArgs) - else -> error("Unexpected flavor") -} +): Array? = when(flavor) { + "jvm", "native" -> { + val cinteropArguments = CInteropArguments() + cinteropArguments.argParser.parse(args) + val platform = KotlinPlatform.values().single { it.name.equals(flavor, ignoreCase = true) } + processCLib(platform, cinteropArguments, additionalArgs) + } + "wasm" -> processIdlLib(args, additionalArgs) + else -> error("Unexpected flavor") + } // Options, whose values are space-separated and can be escaped. val escapedOptions = setOf("-compilerOpts", "-linkerOpts", "-compiler-options", "-linker-options") @@ -205,14 +203,6 @@ private fun findFilesByGlobs(roots: List, globs: List): Map? { val ktGenRoot = additionalArgs.generated diff --git a/kotlin-native/backend.native/build.gradle b/kotlin-native/backend.native/build.gradle index cbc145f2752..b8533c8f70a 100644 --- a/kotlin-native/backend.native/build.gradle +++ b/kotlin-native/backend.native/build.gradle @@ -64,19 +64,6 @@ kotlinNativeInterop { pkg 'org.jetbrains.kotlin.backend.konan.files' } - - - env { - linker 'clang++' - linkOutputs ":kotlin-native:common:${hostName}Env" - - headers fileTree('../common/src/env/headers') { - include '**/*.h' - include '**/*.hpp' - } - - pkg 'org.jetbrains.kotlin.backend.konan.env' - } } @@ -141,7 +128,6 @@ dependencies { compilerApi kotlinNativeInterop['llvm'].configuration compilerApi kotlinNativeInterop['files'].configuration - compilerApi kotlinNativeInterop['env'].configuration cli_bcApi sourceSets.compiler.output @@ -156,7 +142,6 @@ jar { from sourceSets.cli_bc.output, sourceSets.compiler.output, sourceSets.filesInteropStubs.output, - sourceSets.envInteropStubs.output, sourceSets.llvmInteropStubs.output dependsOn ':kotlin-native:runtime:hostRuntime', 'external_jars' diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt index 1e247350975..77443e0bd71 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt @@ -50,11 +50,12 @@ import org.jetbrains.kotlin.ir.declarations.lazy.IrLazyClass import org.jetbrains.kotlin.ir.symbols.IrSymbol import org.jetbrains.kotlin.ir.symbols.impl.IrFieldSymbolImpl import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.konan.library.KonanLibraryLayout import org.jetbrains.kotlin.konan.target.Architecture import org.jetbrains.kotlin.konan.target.KonanTarget +import org.jetbrains.kotlin.konan.util.disposeNativeMemoryAllocator import org.jetbrains.kotlin.library.SerializedIrModule import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyExternal -import org.jetbrains.kotlin.konan.library.KonanLibraryLayout internal class InlineFunctionOriginInfo(val irFile: IrFile, val startOffset: Int, val endOffset: Int) @@ -382,6 +383,14 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) { llvmDisposed = true } + private var nativeMemFreed = false + + fun freeNativeMem() { + if (nativeMemFreed) return + disposeNativeMemoryAllocator() + nativeMemFreed = true + } + val cStubsManager = CStubsManager(config.target) val coverage = CoverageManager(this) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanDriver.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanDriver.kt index 25a823f0bd8..bcd513dad64 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanDriver.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanDriver.kt @@ -5,14 +5,12 @@ package org.jetbrains.kotlin.backend.konan -import kotlinx.cinterop.usingJvmCInteropCallbacks import org.jetbrains.kotlin.analyzer.AnalysisResult import org.jetbrains.kotlin.backend.common.phaser.CompilerPhase import org.jetbrains.kotlin.backend.common.phaser.invokeToplevel import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.config.languageVersionSettings -import org.jetbrains.kotlin.konan.util.usingNativeMemoryAllocator import org.jetbrains.kotlin.utils.addToStdlib.cast fun runTopLevelPhases(konanConfig: KonanConfig, environment: KotlinCoreEnvironment) { @@ -32,13 +30,13 @@ fun runTopLevelPhases(konanConfig: KonanConfig, environment: KotlinCoreEnvironme if (!context.frontendPhase()) return - usingNativeMemoryAllocator { - usingJvmCInteropCallbacks { - try { - toplevelPhase.cast>().invokeToplevel(context.phaseConfig, context, Unit) - } finally { - context.disposeLlvm() - } + try { + toplevelPhase.cast>().invokeToplevel(context.phaseConfig, context, Unit) + } finally { + try { + context.disposeLlvm() + } finally { + context.freeNativeMem() } } } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt index 7e305b321af..04c1dbe0ba6 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt @@ -556,7 +556,8 @@ val toplevelPhase: CompilerPhase<*, Unit, Unit> = namedUnitPhase( unitSink() ) then objectFilesPhase then - linkerPhase + linkerPhase then + freeNativeMemPhase ) internal fun PhaseConfig.disableIf(phase: AnyNamedPhase, condition: Boolean) { diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt index 95ccf7839eb..bdbfdf49b46 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt @@ -79,6 +79,16 @@ internal val disposeLLVMPhase = namedUnitPhase( } ) +internal val freeNativeMemPhase = namedUnitPhase( + name = "FreeNativeMem", + description = "Free native memory used by interop", + lower = object : CompilerPhase { + override fun invoke(phaseConfig: PhaseConfig, phaserState: PhaserState, context: Context, input: Unit) { + context.freeNativeMem() + } + } +) + internal val RTTIPhase = makeKonanModuleOpPhase( name = "RTTI", description = "RTTI generation", diff --git a/kotlin-native/common/build.gradle.kts b/kotlin-native/common/build.gradle.kts index 7c98c54ca68..0e88c79583d 100644 --- a/kotlin-native/common/build.gradle.kts +++ b/kotlin-native/common/build.gradle.kts @@ -11,16 +11,12 @@ bitcode { create("files"){ dependsOn(":kotlin-native:dependencies:update") } - create("env"){ - dependsOn(":kotlin-native:dependencies:update") - } } val hostName: String by project val build by tasks.registering { dependsOn("${hostName}Files") - dependsOn("${hostName}Env") } val clean by tasks.registering { diff --git a/kotlin-native/common/src/env/cpp/Env.cpp b/kotlin-native/common/src/env/cpp/Env.cpp deleted file mode 100644 index c542182e72e..00000000000 --- a/kotlin-native/common/src/env/cpp/Env.cpp +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2010-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license - * that can be found in the LICENSE file. - */ -#include "Env.h" - -#ifdef _WIN32 - -#include - -void setEnv(const char* name, const char* value) { - SetEnvironmentVariableA(name, value); -} - -#else - -#include - -void setEnv(const char* name, const char* value) { - setenv(name, value, 1); -} - -#endif \ No newline at end of file diff --git a/kotlin-native/common/src/env/headers/Env.h b/kotlin-native/common/src/env/headers/Env.h deleted file mode 100644 index 1624204a215..00000000000 --- a/kotlin-native/common/src/env/headers/Env.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright 2010-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license - * that can be found in the LICENSE file. - */ -#ifndef COMMON_ENV_H -#define COMMON_ENV_H - -#ifdef __cplusplus -extern "C" { -#endif - -void setEnv(const char* name, const char* value); - -#ifdef __cplusplus -} -#endif - -#endif // COMMON_ENV_H \ No newline at end of file diff --git a/kotlin-native/utilities/basic-utils/src/main/kotlin/NativeMemoryAllocator.kt b/kotlin-native/utilities/basic-utils/src/main/kotlin/NativeMemoryAllocator.kt index 29cd94b0552..6684b72b5b3 100644 --- a/kotlin-native/utilities/basic-utils/src/main/kotlin/NativeMemoryAllocator.kt +++ b/kotlin-native/utilities/basic-utils/src/main/kotlin/NativeMemoryAllocator.kt @@ -7,50 +7,14 @@ package org.jetbrains.kotlin.konan.util import sun.misc.Unsafe -class ThreadSafeDisposableHelper(create: () -> T, private val dispose: (T) -> Unit) { - private val create_ = create - - var holder: T? = null - private set - - private var counter = 0 - private val lock = Any() - - fun create() { - synchronized(lock) { - if (counter++ == 0) { - check(holder == null) - holder = create_() - } - } - } - - fun dispose() { - synchronized(lock) { - if (--counter == 0) { - dispose(holder!!) - holder = null - } - } - } - - inline fun usingDisposable(block: () -> R): R { - create() - return try { - block() - } finally { - dispose() - } - } -} - -@PublishedApi -internal val allocatorDisposeHelper = ThreadSafeDisposableHelper({ NativeMemoryAllocator() }, { it.freeAll() }) - -inline fun usingNativeMemoryAllocator(block: () -> R) = allocatorDisposeHelper.usingDisposable(block) - +private val allocatorHolder = ThreadLocal() val nativeMemoryAllocator: NativeMemoryAllocator - get() = allocatorDisposeHelper.holder ?: error("Native memory allocator hasn't been created") + get() = allocatorHolder.get() ?: NativeMemoryAllocator().also { allocatorHolder.set(it) } + +fun disposeNativeMemoryAllocator() { + allocatorHolder.get()?.freeAll() + allocatorHolder.remove() +} // 256 buckets for sizes <= 2048 padded to 8 // 256 buckets for sizes <= 64KB padded to 256 @@ -68,23 +32,15 @@ private const val ChunkHeaderSize = 2 * Int.SIZE_BYTES // chunk size + alignment private const val RawChunkSize: Long = 4L * 1024 * 1024 class NativeMemoryAllocator { - companion object { - fun init() = allocatorDisposeHelper.create() - fun dispose() = allocatorDisposeHelper.dispose() - } - private fun alignUp(x: Long, align: Int) = (x + align - 1) and (align - 1).toLong().inv() private fun alignUp(x: Int, align: Int) = (x + align - 1) and (align - 1).inv() private val smallChunks = LongArray(ChunkBucketSize) private val mediumChunks = LongArray(ChunkBucketSize) private val bigChunks = LongArray(ChunkBucketSize) - private val lock = Any() - - fun alloc(size: Long, align: Int) = synchronized(lock) { allocNoLock(size, align) } // Chunk layout: [chunk size,...padding...,diff to start,aligned data start,.....data.....] - private fun allocNoLock(size: Long, align: Int): Long { + fun alloc(size: Long, align: Int): Long { val totalChunkSize = ChunkHeaderSize + size + align val ptr = ChunkHeaderSize + when { totalChunkSize <= MaxSmallSize -> allocFromFreeList(totalChunkSize.toInt(), SmallChunksSizeAlignment, smallChunks) @@ -135,9 +91,7 @@ class NativeMemoryAllocator { return (rawChunks.last() + rawOffset).also { rawOffset += size } } - fun free(mem: Long) = synchronized(lock) { freeNoLock(mem) } - - private fun freeNoLock(mem: Long) { + fun free(mem: Long) { val chunkStart = mem - ChunkHeaderSize - unsafe.getInt(mem - Int.SIZE_BYTES) val chunkSize = unsafe.getInt(chunkStart) when { @@ -148,7 +102,7 @@ class NativeMemoryAllocator { } } - internal fun freeAll() { + fun freeAll() { for (i in 0 until ChunkBucketSize) { smallChunks[i] = 0L mediumChunks[i] = 0L diff --git a/kotlin-native/utilities/cli-runner/src/main/kotlin/org/jetbrains/kotlin/cli/utilities/main.kt b/kotlin-native/utilities/cli-runner/src/main/kotlin/org/jetbrains/kotlin/cli/utilities/main.kt index 5847bca519b..f5c3d9e2d7d 100644 --- a/kotlin-native/utilities/cli-runner/src/main/kotlin/org/jetbrains/kotlin/cli/utilities/main.kt +++ b/kotlin-native/utilities/cli-runner/src/main/kotlin/org/jetbrains/kotlin/cli/utilities/main.kt @@ -8,8 +8,6 @@ import org.jetbrains.kotlin.native.interop.gen.defFileDependencies import org.jetbrains.kotlin.cli.bc.main as konancMain import org.jetbrains.kotlin.cli.klib.main as klibMain import org.jetbrains.kotlin.cli.bc.mainNoExitWithGradleRenderer as konancMainForGradle -import org.jetbrains.kotlin.backend.konan.env.setEnv -import org.jetbrains.kotlin.konan.util.usingNativeMemoryAllocator private fun mainImpl(args: Array, konancMain: (Array) -> Unit) { val utilityName = args[0] @@ -61,11 +59,5 @@ private fun mainImpl(args: Array, konancMain: (Array) -> Unit) { fun main(args: Array) = mainImpl(args, ::konancMain) -private fun setupClangEnv() { - setEnv("LIBCLANG_DISABLE_CRASH_RECOVERY", "1") -} +fun daemonMain(args: Array) = mainImpl(args, ::konancMainForGradle) -fun daemonMain(args: Array) = usingNativeMemoryAllocator { - setupClangEnv() // For in-process invocation have to setup proper environment manually. - mainImpl(args, ::konancMainForGradle) -} \ No newline at end of file