diff --git a/kotlin-native/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/NativeMem.kt b/kotlin-native/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/NativeMem.kt index 3dd23a8867f..6697a37844c 100644 --- a/kotlin-native/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/NativeMem.kt +++ b/kotlin-native/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/NativeMem.kt @@ -17,6 +17,7 @@ package kotlinx.cinterop import kotlin.native.* +import kotlin.native.internal.GCUnsafeCall import kotlin.native.internal.Intrinsic import kotlin.native.internal.TypedIntrinsic import kotlin.native.internal.IntrinsicType @@ -158,10 +159,10 @@ public fun CPointer.toKString(): String = this.toKStringFromUtf16() public fun CPointer.toKString(): String = this.toKStringFromUtf16() -@SymbolName("Kotlin_interop_malloc") +@GCUnsafeCall("Kotlin_interop_malloc") private external fun malloc(size: Long, align: Int): NativePtr -@SymbolName("Kotlin_interop_free") +@GCUnsafeCall("Kotlin_interop_free") private external fun cfree(ptr: NativePtr) @TypedIntrinsic(IntrinsicType.INTEROP_READ_BITS) diff --git a/kotlin-native/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/NativeStableRef.kt b/kotlin-native/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/NativeStableRef.kt index 872427ebd41..eded7d5a25c 100644 --- a/kotlin-native/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/NativeStableRef.kt +++ b/kotlin-native/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/NativeStableRef.kt @@ -16,13 +16,14 @@ package kotlinx.cinterop import kotlin.native.* +import kotlin.native.internal.GCUnsafeCall -@SymbolName("Kotlin_Interop_createStablePointer") +@GCUnsafeCall("Kotlin_Interop_createStablePointer") internal external fun createStablePointer(any: Any): COpaquePointer -@SymbolName("Kotlin_Interop_disposeStablePointer") +@GCUnsafeCall("Kotlin_Interop_disposeStablePointer") internal external fun disposeStablePointer(pointer: COpaquePointer) @PublishedApi -@SymbolName("Kotlin_Interop_derefStablePointer") +@GCUnsafeCall("Kotlin_Interop_derefStablePointer") internal external fun derefStablePointer(pointer: COpaquePointer): Any diff --git a/kotlin-native/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/NativeUtils.kt b/kotlin-native/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/NativeUtils.kt index ccf17aca9e6..0eebf60f2ad 100644 --- a/kotlin-native/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/NativeUtils.kt +++ b/kotlin-native/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/NativeUtils.kt @@ -16,13 +16,14 @@ package kotlinx.cinterop +import kotlin.native.internal.GCUnsafeCall import kotlin.native.internal.Intrinsic import kotlin.native.internal.TypedIntrinsic import kotlin.native.internal.IntrinsicType internal fun encodeToUtf8(str: String): ByteArray = str.encodeToByteArray() -@SymbolName("Kotlin_CString_toKStringFromUtf8Impl") +@GCUnsafeCall("Kotlin_CString_toKStringFromUtf8Impl") internal external fun CPointer.toKStringFromUtf8Impl(): String @TypedIntrinsic(IntrinsicType.INTEROP_BITS_TO_FLOAT) diff --git a/kotlin-native/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/Pinning.kt b/kotlin-native/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/Pinning.kt index 2b69bbabf05..d8ce8a1ad8e 100644 --- a/kotlin-native/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/Pinning.kt +++ b/kotlin-native/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/Pinning.kt @@ -16,6 +16,7 @@ package kotlinx.cinterop import kotlin.native.* +import kotlin.native.internal.GCUnsafeCall data class Pinned internal constructor(private val stablePtr: COpaquePointer) { @@ -92,38 +93,38 @@ private inline fun T.usingPinned( } } -@SymbolName("Kotlin_Arrays_getByteArrayAddressOfElement") +@GCUnsafeCall("Kotlin_Arrays_getByteArrayAddressOfElement") private external fun ByteArray.addressOfElement(index: Int): CPointer -@SymbolName("Kotlin_Arrays_getStringAddressOfElement") +@GCUnsafeCall("Kotlin_Arrays_getStringAddressOfElement") private external fun String.addressOfElement(index: Int): CPointer -@SymbolName("Kotlin_Arrays_getCharArrayAddressOfElement") +@GCUnsafeCall("Kotlin_Arrays_getCharArrayAddressOfElement") private external fun CharArray.addressOfElement(index: Int): CPointer -@SymbolName("Kotlin_Arrays_getShortArrayAddressOfElement") +@GCUnsafeCall("Kotlin_Arrays_getShortArrayAddressOfElement") private external fun ShortArray.addressOfElement(index: Int): CPointer -@SymbolName("Kotlin_Arrays_getIntArrayAddressOfElement") +@GCUnsafeCall("Kotlin_Arrays_getIntArrayAddressOfElement") private external fun IntArray.addressOfElement(index: Int): CPointer -@SymbolName("Kotlin_Arrays_getLongArrayAddressOfElement") +@GCUnsafeCall("Kotlin_Arrays_getLongArrayAddressOfElement") private external fun LongArray.addressOfElement(index: Int): CPointer -@SymbolName("Kotlin_Arrays_getByteArrayAddressOfElement") +@GCUnsafeCall("Kotlin_Arrays_getByteArrayAddressOfElement") private external fun UByteArray.addressOfElement(index: Int): CPointer -@SymbolName("Kotlin_Arrays_getShortArrayAddressOfElement") +@GCUnsafeCall("Kotlin_Arrays_getShortArrayAddressOfElement") private external fun UShortArray.addressOfElement(index: Int): CPointer -@SymbolName("Kotlin_Arrays_getIntArrayAddressOfElement") +@GCUnsafeCall("Kotlin_Arrays_getIntArrayAddressOfElement") private external fun UIntArray.addressOfElement(index: Int): CPointer -@SymbolName("Kotlin_Arrays_getLongArrayAddressOfElement") +@GCUnsafeCall("Kotlin_Arrays_getLongArrayAddressOfElement") private external fun ULongArray.addressOfElement(index: Int): CPointer -@SymbolName("Kotlin_Arrays_getFloatArrayAddressOfElement") +@GCUnsafeCall("Kotlin_Arrays_getFloatArrayAddressOfElement") private external fun FloatArray.addressOfElement(index: Int): CPointer -@SymbolName("Kotlin_Arrays_getDoubleArrayAddressOfElement") +@GCUnsafeCall("Kotlin_Arrays_getDoubleArrayAddressOfElement") private external fun DoubleArray.addressOfElement(index: Int): CPointer diff --git a/kotlin-native/backend.native/build.gradle b/kotlin-native/backend.native/build.gradle index 3528d2c8550..6eab077c6e5 100644 --- a/kotlin-native/backend.native/build.gradle +++ b/kotlin-native/backend.native/build.gradle @@ -194,6 +194,7 @@ targetList.each { target -> '-Xmulti-platform', '-Xopt-in=kotlin.RequiresOptIn', '-Xinline-classes', '-Xopt-in=kotlin.contracts.ExperimentalContracts', '-Xopt-in=kotlin.ExperimentalMultiplatform', + '-Xopt-in=kotlin.native.internal.InternalForKotlinNative', '-Xallow-result-return-type', *commonSrc.toList(), *testAnnotationCommon.toList(), diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanFqNames.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanFqNames.kt index b4e33339db6..04e0db9bc6e 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanFqNames.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanFqNames.kt @@ -32,4 +32,5 @@ object KonanFqNames { val objCMethod = FqName("kotlinx.cinterop.ObjCMethod") val hasFinalizer = FqName("kotlin.native.internal.HasFinalizer") val hasFreezeHook = FqName("kotlin.native.internal.HasFreezeHook") + val gcUnsafeCall = FqName("kotlin.native.internal.GCUnsafeCall") } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/cgen/CBridgeGenUtils.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/cgen/CBridgeGenUtils.kt index fe3a2d93725..bd1e546f2be 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/cgen/CBridgeGenUtils.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/cgen/CBridgeGenUtils.kt @@ -9,10 +9,7 @@ import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.builders.* -import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin -import org.jetbrains.kotlin.ir.declarations.IrFunction -import org.jetbrains.kotlin.ir.declarations.IrValueParameter -import org.jetbrains.kotlin.ir.declarations.IrVariable +import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl import org.jetbrains.kotlin.ir.expressions.IrExpression @@ -73,10 +70,11 @@ internal class KotlinBridgeBuilder( cName: String, stubs: KotlinStubs, isExternal: Boolean, - foreignExceptionMode: ForeignExceptionMode.Mode + foreignExceptionMode: ForeignExceptionMode.Mode, + origin: IrDeclarationOrigin ) { private var counter = 0 - private val bridge: IrFunction = createKotlinBridge(startOffset, endOffset, cName, stubs, isExternal, foreignExceptionMode) + private val bridge: IrFunction = createKotlinBridge(startOffset, endOffset, cName, stubs, isExternal, foreignExceptionMode, origin) val irBuilder: IrBuilderWithScope = irBuilder(stubs.irBuiltIns, bridge.symbol).at(startOffset, endOffset) fun addParameter(type: IrType): IrValueParameter { @@ -110,12 +108,13 @@ private fun createKotlinBridge( cBridgeName: String, stubs: KotlinStubs, isExternal: Boolean, - foreignExceptionMode: ForeignExceptionMode.Mode + foreignExceptionMode: ForeignExceptionMode.Mode, + origin: IrDeclarationOrigin ): IrFunction { val bridge = IrFunctionImpl( startOffset, endOffset, - IrDeclarationOrigin.DEFINED, + origin, IrSimpleFunctionSymbolImpl(), Name.identifier(cBridgeName), DescriptorVisibilities.PRIVATE, @@ -151,7 +150,9 @@ internal class KotlinCBridgeBuilder( isKotlinToC: Boolean, foreignExceptionMode: ForeignExceptionMode.Mode = ForeignExceptionMode.default ) { - private val kotlinBridgeBuilder = KotlinBridgeBuilder(startOffset, endOffset, cName, stubs, isExternal = isKotlinToC, foreignExceptionMode) + private val origin: CBridgeOrigin = if (isKotlinToC) CBridgeOrigin.KOTLIN_TO_C_BRIDGE else CBridgeOrigin.C_TO_KOTLIN_BRIDGE + + private val kotlinBridgeBuilder = KotlinBridgeBuilder(startOffset, endOffset, cName, stubs, isExternal = isKotlinToC, foreignExceptionMode, origin) private val cBridgeBuilder = CFunctionBuilder() val kotlinIrBuilder: IrBuilderWithScope get() = kotlinBridgeBuilder.irBuilder @@ -251,3 +252,8 @@ internal class CCallBuilder { append(')') } } + +sealed class CBridgeOrigin(name: String): IrDeclarationOriginImpl(name, isSynthetic = true) { + object KOTLIN_TO_C_BRIDGE: CBridgeOrigin("KOTLIN_TO_C_BRIDGE") + object C_TO_KOTLIN_BRIDGE: CBridgeOrigin("C_TO_KOTLIN_BRIDGE") +} diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt index e945ff8d847..3ec4c4e8e84 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt @@ -291,6 +291,8 @@ fun IrConstructorCall.getAnnotationValueOrNull(name: String): T? { fun IrFunction.externalSymbolOrThrow(): String? { annotations.findAnnotation(RuntimeNames.symbolNameAnnotation)?.let { return it.getAnnotationStringValue() } + annotations.findAnnotation(KonanFqNames.gcUnsafeCall)?.let { return it.getAnnotationStringValue("callee") } + if (annotations.hasAnnotation(KonanFqNames.objCMethod)) return null if (annotations.hasAnnotation(KonanFqNames.typedIntrinsic)) return null @@ -299,7 +301,7 @@ fun IrFunction.externalSymbolOrThrow(): String? { if (origin == InternalAbi.INTERNAL_ABI_ORIGIN) return null - throw Error("external function ${this.longName} must have @TypedIntrinsic, @SymbolName or @ObjCMethod annotation") + throw Error("external function ${this.longName} must have @TypedIntrinsic, @SymbolName, @GCUnsafeCall or @ObjCMethod annotation") } val IrFunction.isBuiltInOperator get() = origin == IrBuiltIns.BUILTIN_OPERATOR diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt index 8388d41b9d9..15d6e5b5d80 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt @@ -9,6 +9,7 @@ package org.jetbrains.kotlin.backend.konan.llvm import kotlinx.cinterop.* import llvm.* import org.jetbrains.kotlin.backend.konan.* +import org.jetbrains.kotlin.backend.konan.cgen.CBridgeOrigin import org.jetbrains.kotlin.backend.konan.descriptors.ClassGlobalHierarchyInfo import org.jetbrains.kotlin.backend.konan.llvm.objc.* import org.jetbrains.kotlin.konan.target.CompilerOutputKind @@ -16,6 +17,8 @@ import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.backend.konan.ir.* +import org.jetbrains.kotlin.backend.konan.llvm.ThreadState.Native +import org.jetbrains.kotlin.backend.konan.llvm.ThreadState.Runnable import org.jetbrains.kotlin.descriptors.konan.CompiledKlibModuleOrigin import org.jetbrains.kotlin.ir.expressions.IrDelegatingConstructorCall import org.jetbrains.kotlin.ir.expressions.IrGetObjectValue @@ -91,6 +94,10 @@ internal sealed class ExceptionHandler { } } +internal enum class ThreadState { + Native, Runnable +} + val LLVMValueRef.name:String? get() = LLVMGetValueName(this)?.toKString() @@ -341,7 +348,7 @@ internal class StackLocalsManagerImpl( internal class FunctionGenerationContext(val function: LLVMValueRef, val codegen: CodeGenerator, startLocation: LocationInfo?, - endLocation: LocationInfo?, + private val endLocation: LocationInfo?, internal val irFunction: IrFunction? = null): ContextUtils { override val context = codegen.context @@ -528,6 +535,19 @@ internal class FunctionGenerationContext(val function: LLVMValueRef, //-------------------------------------------------------------------------// + fun switchThreadState(state: ThreadState) { + check(context.memoryModel == MemoryModel.EXPERIMENTAL) { + "Thread state switching is allowed in the experimental memory model only." + } + check(!forbidRuntime) { + "Attempt to switch the thread state when runtime is forbidden" + } + when (state) { + Native -> call(context.llvm.Kotlin_mm_switchThreadStateNative, emptyList()) + Runnable -> call(context.llvm.Kotlin_mm_switchThreadStateRunnable, emptyList()) + }.let {} // Force exhaustive. + } + fun call(llvmFunction: LLVMValueRef, args: List, resultLifetime: Lifetime = Lifetime.IRRELEVANT, exceptionHandler: ExceptionHandler = ExceptionHandler.None, @@ -755,7 +775,7 @@ internal class FunctionGenerationContext(val function: LLVMValueRef, return LLVMBuildExtractElement(builder, vector, index, name)!! } - fun filteringExceptionHandler(codeContext: CodeContext, foreignExceptionMode: ForeignExceptionMode.Mode): ExceptionHandler { + fun filteringExceptionHandler(codeContext: CodeContext, foreignExceptionMode: ForeignExceptionMode.Mode, switchThreadState: Boolean): ExceptionHandler { val lpBlock = basicBlockInFunction("filteringExceptionHandler", position()?.start) val wrapExceptionMode = context.config.target.family.isAppleFamily && @@ -769,6 +789,10 @@ internal class FunctionGenerationContext(val function: LLVMValueRef, } LLVMAddClause(landingpad, LLVMConstNull(kInt8Ptr)) + if (switchThreadState) { + switchThreadState(Runnable) + } + val fatalForeignExceptionBlock = basicBlock("fatalForeignException", position()?.start) val forwardKotlinExceptionBlock = basicBlock("forwardKotlinException", position()?.start) @@ -829,9 +853,7 @@ internal class FunctionGenerationContext(val function: LLVMValueRef, } } - fun kotlinExceptionHandler( - block: FunctionGenerationContext.(exception: LLVMValueRef) -> Unit - ): ExceptionHandler { + fun kotlinExceptionHandler(block: FunctionGenerationContext.(exception: LLVMValueRef) -> Unit): ExceptionHandler { val lpBlock = basicBlock("kotlinExceptionHandler", position()?.end) appendingTo(lpBlock) { @@ -1248,6 +1270,14 @@ internal class FunctionGenerationContext(val function: LLVMValueRef, if (isObjectType(returnType!!)) { returnSlot = LLVMGetParam(function, numParameters(function.type) - 1) } + + if (context.memoryModel == MemoryModel.EXPERIMENTAL && + irFunction?.origin == CBridgeOrigin.C_TO_KOTLIN_BRIDGE) { + check(!forbidRuntime) { "Attempt to switch the thread state when runtime is forbidden" } + positionAtEnd(prologueBb) + switchThreadState(Runnable) + } + positionAtEnd(localsInitBb) slotsPhi = phi(kObjHeaderPtrPtr) // Is removed by DCE trivially, if not needed. @@ -1307,8 +1337,7 @@ internal class FunctionGenerationContext(val function: LLVMValueRef, returnType == voidType -> { releaseVars() assert(returnSlot == null) - if (!forbidRuntime && context.memoryModel == MemoryModel.EXPERIMENTAL) - call(context.llvm.Kotlin_mm_safePointFunctionEpilogue, emptyList()) + handleEpilogueForExperimentalMM(context.llvm.Kotlin_mm_safePointFunctionEpilogue) LLVMBuildRetVoid(builder) } returns.isNotEmpty() -> { @@ -1318,8 +1347,7 @@ internal class FunctionGenerationContext(val function: LLVMValueRef, updateReturnRef(returnPhi, returnSlot!!) } releaseVars() - if (!forbidRuntime && context.memoryModel == MemoryModel.EXPERIMENTAL) - call(context.llvm.Kotlin_mm_safePointFunctionEpilogue, emptyList()) + handleEpilogueForExperimentalMM(context.llvm.Kotlin_mm_safePointFunctionEpilogue) LLVMBuildRet(builder, returnPhi) } // Do nothing, all paths throw. @@ -1360,8 +1388,7 @@ internal class FunctionGenerationContext(val function: LLVMValueRef, } releaseVars() - if (!forbidRuntime && context.memoryModel == MemoryModel.EXPERIMENTAL) - call(context.llvm.Kotlin_mm_safePointExceptionUnwind, emptyList()) + handleEpilogueForExperimentalMM(context.llvm.Kotlin_mm_safePointExceptionUnwind) LLVMBuildResume(builder, landingpad) } @@ -1371,6 +1398,18 @@ internal class FunctionGenerationContext(val function: LLVMValueRef, slotsPhi = null } + private fun handleEpilogueForExperimentalMM(safePointFunction: LLVMValueRef) { + if (context.memoryModel == MemoryModel.EXPERIMENTAL) { + if (!forbidRuntime) { + call(safePointFunction, emptyList()) + } + if (irFunction?.origin == CBridgeOrigin.C_TO_KOTLIN_BRIDGE) { + check(!forbidRuntime) { "Generating a bridge when runtime is forbidden" } + switchThreadState(Native) + } + } + } + private val kotlinExceptionRtti: ConstPointer get() = constPointer(importGlobal( "_ZTI18ExceptionObjHolder", // typeinfo for ObjHolder diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt index c43cfc95434..e2e055d3f10 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt @@ -487,6 +487,9 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) { val getObjCKotlinTypeInfo by lazy { importRtFunction("GetObjCKotlinTypeInfo") } val missingInitImp by lazy { importRtFunction("MissingInitImp") } + val Kotlin_mm_switchThreadStateNative by lazy { importRtFunction("Kotlin_mm_switchThreadStateNative") } + val Kotlin_mm_switchThreadStateRunnable by lazy { importRtFunction("Kotlin_mm_switchThreadStateRunnable") } + val Kotlin_Interop_DoesObjectConformToProtocol by lazyRtFunction val Kotlin_Interop_IsObjectKindOfClass by lazyRtFunction diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt index a1a0b369176..a3087913a5f 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt @@ -12,6 +12,7 @@ import org.jetbrains.kotlin.backend.common.ir.allParameters import org.jetbrains.kotlin.backend.common.ir.allParametersCount import org.jetbrains.kotlin.backend.common.lower.inline.InlinerExpressionLocationHint import org.jetbrains.kotlin.backend.konan.* +import org.jetbrains.kotlin.backend.konan.cgen.CBridgeOrigin import org.jetbrains.kotlin.backend.konan.descriptors.* import org.jetbrains.kotlin.backend.konan.ir.* import org.jetbrains.kotlin.backend.konan.llvm.coverage.LLVMCoverageInstrumentation @@ -673,7 +674,8 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map, resultLifetime: Lifetime): LLVMValueRef { + check(!function.isTypedIntrinsic) + val needsNativeThreadState = function.needsNativeThreadState val exceptionHandler = function.annotations.findAnnotation(RuntimeNames.filterExceptions)?.let { val foreignExceptionMode = ForeignExceptionMode.byValue(it.getAnnotationValueOrNull("mode")) - functionGenerationContext.filteringExceptionHandler(currentCodeContext, foreignExceptionMode) + functionGenerationContext.filteringExceptionHandler(currentCodeContext, foreignExceptionMode, needsNativeThreadState) } ?: currentCodeContext.exceptionHandler + if (needsNativeThreadState) { + functionGenerationContext.switchThreadState(ThreadState.Native) + } + val result = call(llvmFunction, args, resultLifetime, exceptionHandler) - if (!function.isSuspend && function.returnType.isNothing()) { - functionGenerationContext.unreachable() + + when { + !function.isSuspend && function.returnType.isNothing() -> + functionGenerationContext.unreachable() + needsNativeThreadState -> + functionGenerationContext.switchThreadState(ThreadState.Runnable) } if (LLVMGetReturnType(getFunctionType(llvmFunction)) == voidType) { diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanMangler.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanMangler.kt index bb1c78033e2..0169c9c5415 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanMangler.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanMangler.kt @@ -28,6 +28,10 @@ abstract class AbstractKonanIrMangler(private val withReturnType: Boolean) : IrB // Treat any `@SymbolName` declaration as exported. return true } + if (annotations.hasAnnotation(KonanFqNames.gcUnsafeCall)) { + // Treat any `@GCUnsafeCall` declaration as exported. + return true + } if (annotations.hasAnnotation(RuntimeNames.exportForCppRuntime)) { // Treat any `@ExportForCppRuntime` declaration as exported. return true @@ -99,6 +103,10 @@ abstract class AbstractKonanDescriptorMangler : DescriptorBasedKotlinManglerImpl // Treat any `@SymbolName` declaration as exported. return true } + if (annotations.hasAnnotation(KonanFqNames.gcUnsafeCall)) { + // Treat any `@GCUnsafeCall` declaration as exported. + return true + } if (annotations.hasAnnotation(RuntimeNames.exportForCppRuntime)) { // Treat any `@ExportForCppRuntime` declaration as exported. return true diff --git a/kotlin-native/backend.native/tests/build.gradle b/kotlin-native/backend.native/tests/build.gradle index 5f65a2ac657..1bb19f003ad 100644 --- a/kotlin-native/backend.native/tests/build.gradle +++ b/kotlin-native/backend.native/tests/build.gradle @@ -3830,6 +3830,11 @@ createInterop("cppSkia") { it.defFile 'interop/cpp/skia.def' } +createInterop("threadStates") { + it.defFile "interop/threadStates/threadStates.def" + it.extraOpts "-Xcompile-source", "$projectDir/interop/threadStates/threadStates.cpp" +} + if (PlatformInfo.isAppleTarget(project)) { createInterop("objcSmoke") { it.defFile 'interop/objc/objcSmoke.def' @@ -4117,6 +4122,20 @@ interopTest("interop_callbacksAndVarargs") { interop = 'ccallbacksAndVarargs' } +interopTest("interop_threadStates") { + disabled = (project.testTarget == 'wasm32') || // No interop for wasm yet. + !isExperimentalMM // No thread state switching in the legacy MM. + source = "interop/threadStates/threadStates.kt" + interop = "threadStates" +} + +interopTest("interop_threadStates_callbacksWithExceptions") { + disabled = (project.testTarget == 'wasm32') || // No interop for wasm yet. + !isExperimentalMM // No thread state switching in the legacy MM. + source = "interop/threadStates/callbacksWithExceptions.kt" + interop = "threadStates" +} + interopTest("interop_withSpaces") { disabled = (project.testTarget == 'wasm32') // No interop for wasm yet. interop ='withSpaces' diff --git a/kotlin-native/backend.native/tests/interop/threadStates/callbacksWithExceptions.kt b/kotlin-native/backend.native/tests/interop/threadStates/callbacksWithExceptions.kt new file mode 100644 index 00000000000..8747a403834 --- /dev/null +++ b/kotlin-native/backend.native/tests/interop/threadStates/callbacksWithExceptions.kt @@ -0,0 +1,95 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +import kotlin.native.internal.Debugging +import kotlin.test.* +import kotlinx.cinterop.staticCFunction +import threadStates.* + +fun main() { + callbackWithException() + callbackWithFinally() + callbackWithFinallyNoCatch() + nestedCallbackWithException() + nestedCallbackWithFinally() +} + +fun assertRunnableThreadState() { + assertTrue(Debugging.isThreadStateRunnable) +} + +class CustomException() : Exception() + +fun throwException() { + assertRunnableThreadState() + throw CustomException() +} + +fun callbackWithException() { + try { + runCallback(staticCFunction(::throwException)) + } catch (e: CustomException) { + assertRunnableThreadState() + return + } catch (e: Throwable) { + assertRunnableThreadState() + fail("Wrong exception type: ${e.message}") + } + fail("No exception thrown") +} + +fun callbackWithFinally() { + try { + runCallback(staticCFunction(::throwException)) + } catch (e: CustomException) { + assertRunnableThreadState() + return + } finally { + assertRunnableThreadState() + } + fail("No exception thrown") +} + +fun callbackWithFinallyNoCatch() { + try { + try { + runCallback(staticCFunction(::throwException)) + } finally { + assertRunnableThreadState() + } + assertRunnableThreadState() + } catch (_: CustomException) {} +} + +fun nestedCallbackWithException() { + try { + runCallback(staticCFunction { -> + assertRunnableThreadState() + runCallback(staticCFunction(::throwException)) + }) + } catch (e: CustomException) { + assertRunnableThreadState() + return + } catch (e: Throwable) { + assertRunnableThreadState() + fail("Wrong exception type: ${e.message}") + } + fail("No exception thrown") +} + +fun nestedCallbackWithFinally() { + try { + runCallback(staticCFunction { -> + assertRunnableThreadState() + runCallback(staticCFunction(::throwException)) + }) + } catch (e: CustomException) { + assertRunnableThreadState() + return + } finally { + assertRunnableThreadState() + } + fail("No exception thrown") +} \ No newline at end of file diff --git a/kotlin-native/backend.native/tests/interop/threadStates/threadStates.cpp b/kotlin-native/backend.native/tests/interop/threadStates/threadStates.cpp new file mode 100644 index 00000000000..32a82e7c2b1 --- /dev/null +++ b/kotlin-native/backend.native/tests/interop/threadStates/threadStates.cpp @@ -0,0 +1,20 @@ +#include +#include +#include + +// Implemented in the runtime for test purposes. +extern "C" bool Kotlin_Debugging_isThreadStateNative(); + +extern "C" void assertNativeThreadState() { + if (!Kotlin_Debugging_isThreadStateNative()) { + printf("Incorrect thread state. Expected native thread state."); + abort(); + } +} + +extern "C" void runInNewThread(void(*callback)(void)) { + std::thread t([callback]() { + callback(); + }); + t.join(); +} \ No newline at end of file diff --git a/kotlin-native/backend.native/tests/interop/threadStates/threadStates.def b/kotlin-native/backend.native/tests/interop/threadStates/threadStates.def new file mode 100644 index 00000000000..8cb14cdc4bb --- /dev/null +++ b/kotlin-native/backend.native/tests/interop/threadStates/threadStates.def @@ -0,0 +1,20 @@ +language = C + +--- + +#include + +void assertNativeThreadState(); + +void runCallback(void(*callback)(void)) { + assertNativeThreadState(); + callback(); + assertNativeThreadState(); +} + +int32_t answer() { + assertNativeThreadState(); + return 42; +} + +void runInNewThread(void(*callback)(void)); \ No newline at end of file diff --git a/kotlin-native/backend.native/tests/interop/threadStates/threadStates.kt b/kotlin-native/backend.native/tests/interop/threadStates/threadStates.kt new file mode 100644 index 00000000000..7f94f2964e7 --- /dev/null +++ b/kotlin-native/backend.native/tests/interop/threadStates/threadStates.kt @@ -0,0 +1,59 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +import kotlin.native.internal.Debugging +import kotlin.test.* +import kotlinx.cinterop.* +import threadStates.* + +fun main() { + nativeCall() + callback() + nestedCalls() + directStaticCFunctionCall() + // TODO: Support runtime initialization for callbacks + // see: https://youtrack.jetbrains.com/issue/KT-44283 + // callbackOnSeparateThread() +} + +fun assertRunnableThreadState() { + assertTrue(Debugging.isThreadStateRunnable) +} + +fun nativeCall() { + answer() + assertRunnableThreadState() +} + +fun callback() { + runCallback(staticCFunction { -> + assertRunnableThreadState() + }) + assertRunnableThreadState() +} + +fun nestedCalls() { + runCallback(staticCFunction { -> + assertRunnableThreadState() + answer() + Unit + }) + assertRunnableThreadState() +} + +fun directStaticCFunctionCall() { + val funPtr = staticCFunction { -> + assertRunnableThreadState() + } + assertRunnableThreadState() + funPtr() + assertRunnableThreadState() +} + +fun callbackOnSeparateThread() { + runInNewThread(staticCFunction { -> + assertRunnableThreadState() + }) +} diff --git a/kotlin-native/runtime/src/debug/cpp/KDebug.cpp b/kotlin-native/runtime/src/debug/cpp/KDebug.cpp index a535034522b..86b4c668c6c 100644 --- a/kotlin-native/runtime/src/debug/cpp/KDebug.cpp +++ b/kotlin-native/runtime/src/debug/cpp/KDebug.cpp @@ -110,8 +110,12 @@ int Konan_DebugBufferSizeWithObjectImpl(KRef obj) { // Auxilary function which can be called by developer/debugger to inspect an object. int32_t Konan_DebugObjectToUtf8ArrayImpl(KRef obj, char* buffer, int32_t bufferSize) { + // We need the runnable thread state to call the Kotlin function 'KonanObjectToUtf8Array' and operate with it's result. + // But the current thread can be in any state when this function is called by the debugger. So we use the reentrant state switch. + kotlin::ThreadStateGuard guard(kotlin::ThreadState::kRunnable, /* reentrant */ true); ObjHolder stringHolder; - auto data = KonanObjectToUtf8Array(obj, stringHolder.slot())->array(); + // Kotlin call. + ArrayHeader* data = KonanObjectToUtf8Array(obj, stringHolder.slot())->array(); if (data == nullptr) return 0; if (bufferSize < 1) return 0; KInt toCopy = data->count_ > static_cast(bufferSize - 1) ? bufferSize - 1 : data->count_; diff --git a/kotlin-native/runtime/src/gc/common/cpp/MarkAndSweepUtilsMarkTest.cpp b/kotlin-native/runtime/src/gc/common/cpp/MarkAndSweepUtilsMarkTest.cpp index dc3c5492750..edf2214d390 100644 --- a/kotlin-native/runtime/src/gc/common/cpp/MarkAndSweepUtilsMarkTest.cpp +++ b/kotlin-native/runtime/src/gc/common/cpp/MarkAndSweepUtilsMarkTest.cpp @@ -6,6 +6,7 @@ #include "MarkAndSweepUtils.hpp" #include +#include #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -142,6 +143,7 @@ public: } private: + kotlin::ScopedMemoryInit memoryInit; ScopedMarkTraits markTraits_; }; diff --git a/kotlin-native/runtime/src/gc/common/cpp/MarkAndSweepUtilsSweepTest.cpp b/kotlin-native/runtime/src/gc/common/cpp/MarkAndSweepUtilsSweepTest.cpp index 76ee7a5f7e9..2992eb2dfc4 100644 --- a/kotlin-native/runtime/src/gc/common/cpp/MarkAndSweepUtilsSweepTest.cpp +++ b/kotlin-native/runtime/src/gc/common/cpp/MarkAndSweepUtilsSweepTest.cpp @@ -3,6 +3,7 @@ * that can be found in the LICENSE file. */ +#include #include "MarkAndSweepUtils.hpp" #include "gmock/gmock.h" @@ -232,6 +233,8 @@ public: testing::MockFunction& finalizerHook() { return finalizerHooks_.finalizerHook(); } private: + // TODO: Provide a common base class for all unit tests that require memory initializtion. + kotlin::ScopedMemoryInit memoryInit; FinalizerHooksTestSupport finalizerHooks_; GC::ThreadData gcThreadData_; ObjectFactory objectFactory_; diff --git a/kotlin-native/runtime/src/legacymm/cpp/Memory.cpp b/kotlin-native/runtime/src/legacymm/cpp/Memory.cpp index 687738caded..7c7300859d0 100644 --- a/kotlin-native/runtime/src/legacymm/cpp/Memory.cpp +++ b/kotlin-native/runtime/src/legacymm/cpp/Memory.cpp @@ -3576,6 +3576,10 @@ RUNTIME_NOTHROW void DisposeStablePointer(KNativePtr pointer) { disposeStablePointer(pointer); } +RUNTIME_NOTHROW void DisposeStablePointerFor(MemoryState* memoryState, KNativePtr pointer) { + DisposeStablePointer(pointer); +} + OBJ_GETTER(DerefStablePointer, KNativePtr pointer) { RETURN_RESULT_OF(derefStablePointer, pointer); } @@ -3716,7 +3720,7 @@ ALWAYS_INLINE ObjHeader* ExceptionObjHolder::GetExceptionObject() noexcept { } #endif -ALWAYS_INLINE kotlin::ThreadState kotlin::SwitchThreadState(MemoryState* thread, ThreadState newState) noexcept { +ALWAYS_INLINE kotlin::ThreadState kotlin::SwitchThreadState(MemoryState* thread, ThreadState newState, bool reentrant) noexcept { // no-op, used by the new MM only. return ThreadState::kRunnable; } @@ -3727,4 +3731,9 @@ ALWAYS_INLINE void kotlin::AssertThreadState(MemoryState* thread, ThreadState ex MemoryState* kotlin::mm::GetMemoryState() { return ::memoryState; +} + +kotlin::ThreadState kotlin::GetThreadState(MemoryState* thread) noexcept { + // Assume that we are always in the Runnable thread state. + return ThreadState::kRunnable; } \ No newline at end of file diff --git a/kotlin-native/runtime/src/legacymm/cpp/TestSupport.cpp b/kotlin-native/runtime/src/legacymm/cpp/TestSupport.cpp new file mode 100644 index 00000000000..2408ccd4162 --- /dev/null +++ b/kotlin-native/runtime/src/legacymm/cpp/TestSupport.cpp @@ -0,0 +1,14 @@ +/* + * 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 "TestSupport.hpp" + +extern "C" void Kotlin_TestSupport_AssertClearGlobalState() { + // Nothing to do. Supported for the new MM only. +} + +void kotlin::DeinitMemoryForTests(MemoryState* memoryState) { + DeinitMemory(memoryState, false); +} \ No newline at end of file diff --git a/kotlin-native/runtime/src/main/cpp/Cleaner.cpp b/kotlin-native/runtime/src/main/cpp/Cleaner.cpp index 5d624433aeb..e68936f1500 100644 --- a/kotlin-native/runtime/src/main/cpp/Cleaner.cpp +++ b/kotlin-native/runtime/src/main/cpp/Cleaner.cpp @@ -66,26 +66,30 @@ RUNTIME_NOTHROW void DisposeCleaner(KRef thiz) { void ShutdownCleaners(bool executeScheduledCleaners) { KInt worker = 0; - do { - worker = atomicGet(&globalCleanerWorker); - RuntimeAssert(worker != kCleanerWorkerShutdown, "Cleaner worker must not be shutdown twice"); - if (worker == kCleanerWorkerUninitialized) { - if (!compareAndSet(&globalCleanerWorker, kCleanerWorkerUninitialized, kCleanerWorkerShutdown)) { + { + // This loop may spin waiting for a proper worker state. Switch to the native thread state. + kotlin::ThreadStateGuard guard(kotlin::ThreadState::kNative); + do { + worker = atomicGet(&globalCleanerWorker); + RuntimeAssert(worker != kCleanerWorkerShutdown, "Cleaner worker must not be shutdown twice"); + if (worker == kCleanerWorkerUninitialized) { + if (!compareAndSet(&globalCleanerWorker, kCleanerWorkerUninitialized, kCleanerWorkerShutdown)) { + // Someone is trying to initialize the worker. Try again. + continue; + } + // worker was never initialized. Just return. + return; + } + if (worker == kCleanerWorkerInitializing) { // Someone is trying to initialize the worker. Try again. continue; } - // worker was never initialized. Just return. - return; - } - if (worker == kCleanerWorkerInitializing) { - // Someone is trying to initialize the worker. Try again. - continue; - } - // Worker is in some proper state. - break; + // Worker is in some proper state. + break; - } while (true); + } while (true); + } RuntimeAssert(worker > 0, "Cleaner worker must be fully initialized here"); @@ -96,6 +100,8 @@ void ShutdownCleaners(bool executeScheduledCleaners) { extern "C" KInt Kotlin_CleanerImpl_getCleanerWorker() { KInt worker = 0; + // This loop may spin waiting for a proper worker state. Switch to the native thread state. + kotlin::ThreadStateGuard guard(kotlin::ThreadState::kNative); do { worker = atomicGet(&globalCleanerWorker); RuntimeAssert(worker != kCleanerWorkerShutdown, "Cleaner worker must not have been shutdown"); @@ -104,7 +110,7 @@ extern "C" KInt Kotlin_CleanerImpl_getCleanerWorker() { // Someone else is trying to initialize the worker. Try again. continue; } - worker = Kotlin_CleanerImpl_createCleanerWorker(); + worker = kotlin::CallWithThreadState(Kotlin_CleanerImpl_createCleanerWorker); if (!compareAndSet(&globalCleanerWorker, kCleanerWorkerInitializing, worker)) { RuntimeCheck(false, "Someone interrupted worker initializing"); } diff --git a/kotlin-native/runtime/src/main/cpp/CleanerTest.cpp b/kotlin-native/runtime/src/main/cpp/CleanerTest.cpp index fa5746142b7..75ed0ad4ba1 100644 --- a/kotlin-native/runtime/src/main/cpp/CleanerTest.cpp +++ b/kotlin-native/runtime/src/main/cpp/CleanerTest.cpp @@ -15,13 +15,20 @@ #include "TestSupportCompilerGenerated.hpp" #include "Types.h" +using namespace kotlin; using testing::_; // TODO: Also test disposal. (This requires extracting Worker interface) -TEST(CleanerTest, ConcurrentCreation) { - ResetCleanerWorkerForTests(); +class CleanerTest : public testing::Test { +public: + CleanerTest() {} + ~CleanerTest() { + ResetCleanerWorkerForTests(); + } +}; +TEST_F(CleanerTest, ConcurrentCreation) { constexpr int threadCount = kotlin::kDefaultThreadCount; constexpr KInt workerId = 42; @@ -34,6 +41,8 @@ TEST(CleanerTest, ConcurrentCreation) { KStdVector> futures; for (int i = 0; i < threadCount; ++i) { auto future = std::async(std::launch::async, [&startedThreads, &allowRunning]() { + // Thread state switching requires initilized memory subsystem. + ScopedMemoryInit init; atomicAdd(&startedThreads, 1); while (!atomicGet(&allowRunning)) { } @@ -53,29 +62,28 @@ TEST(CleanerTest, ConcurrentCreation) { EXPECT_THAT(values, testing::Each(workerId)); } -TEST(CleanerTest, ShutdownWithoutCreation) { - ResetCleanerWorkerForTests(); +TEST_F(CleanerTest, ShutdownWithoutCreation) { + RunInNewThread([]() { + auto createCleanerWorkerMock = ScopedCreateCleanerWorkerMock(); + auto shutdownCleanerWorkerMock = ScopedShutdownCleanerWorkerMock(); - auto createCleanerWorkerMock = ScopedCreateCleanerWorkerMock(); - auto shutdownCleanerWorkerMock = ScopedShutdownCleanerWorkerMock(); - - EXPECT_CALL(*createCleanerWorkerMock, Call()).Times(0); - EXPECT_CALL(*shutdownCleanerWorkerMock, Call(_, _)).Times(0); - ShutdownCleaners(true); + EXPECT_CALL(*createCleanerWorkerMock, Call()).Times(0); + EXPECT_CALL(*shutdownCleanerWorkerMock, Call(_, _)).Times(0); + ShutdownCleaners(true); + }); } -TEST(CleanerTest, ShutdownWithCreation) { - ResetCleanerWorkerForTests(); +TEST_F(CleanerTest, ShutdownWithCreation) { + RunInNewThread([]() { + constexpr KInt workerId = 42; + constexpr bool executeScheduledCleaners = true; - constexpr KInt workerId = 42; - constexpr bool executeScheduledCleaners = true; + auto createCleanerWorkerMock = ScopedCreateCleanerWorkerMock(); + auto shutdownCleanerWorkerMock = ScopedShutdownCleanerWorkerMock(); + EXPECT_CALL(*createCleanerWorkerMock, Call()).WillOnce(testing::Return(workerId)); + Kotlin_CleanerImpl_getCleanerWorker(); - auto createCleanerWorkerMock = ScopedCreateCleanerWorkerMock(); - auto shutdownCleanerWorkerMock = ScopedShutdownCleanerWorkerMock(); - - EXPECT_CALL(*createCleanerWorkerMock, Call()).WillOnce(testing::Return(workerId)); - Kotlin_CleanerImpl_getCleanerWorker(); - - EXPECT_CALL(*shutdownCleanerWorkerMock, Call(workerId, executeScheduledCleaners)); - ShutdownCleaners(executeScheduledCleaners); + EXPECT_CALL(*shutdownCleanerWorkerMock, Call(workerId, executeScheduledCleaners)); + ShutdownCleaners(executeScheduledCleaners); + }); } diff --git a/kotlin-native/runtime/src/main/cpp/Console.cpp b/kotlin-native/runtime/src/main/cpp/Console.cpp index e4b33ca42c8..076feff8de5 100644 --- a/kotlin-native/runtime/src/main/cpp/Console.cpp +++ b/kotlin-native/runtime/src/main/cpp/Console.cpp @@ -27,36 +27,44 @@ extern "C" { // io/Console.kt void Kotlin_io_Console_print(KString message) { - if (message->type_info() != theStringTypeInfo) { - ThrowClassCastException(message->obj(), theStringTypeInfo); - } - // TODO: system stdout must be aware about UTF-8. - const KChar* utf16 = CharArrayAddressOfElementAt(message, 0); - KStdString utf8; - utf8.reserve(message->count_); - // Replace incorrect sequences with a default codepoint (see utf8::with_replacement::default_replacement) - utf8::with_replacement::utf16to8(utf16, utf16 + message->count_, back_inserter(utf8)); - konan::consoleWriteUtf8(utf8.c_str(), utf8.size()); + if (message->type_info() != theStringTypeInfo) { + ThrowClassCastException(message->obj(), theStringTypeInfo); + } + // TODO: system stdout must be aware about UTF-8. + const KChar* utf16 = CharArrayAddressOfElementAt(message, 0); + KStdString utf8; + utf8.reserve(message->count_); + // Replace incorrect sequences with a default codepoint (see utf8::with_replacement::default_replacement) + utf8::with_replacement::utf16to8(utf16, utf16 + message->count_, back_inserter(utf8)); + + kotlin::ThreadStateGuard guard(kotlin::ThreadState::kNative); + konan::consoleWriteUtf8(utf8.c_str(), utf8.size()); } void Kotlin_io_Console_println(KString message) { - Kotlin_io_Console_print(message); + Kotlin_io_Console_print(message); #ifndef KONAN_ANDROID - // On Android single print produces logcat entry, so no need in linefeed. - Kotlin_io_Console_println0(); + // On Android single print produces logcat entry, so no need in linefeed. + Kotlin_io_Console_println0(); #endif } void Kotlin_io_Console_println0() { - konan::consoleWriteUtf8("\n", 1); + kotlin::ThreadStateGuard guard(kotlin::ThreadState::kNative); + konan::consoleWriteUtf8("\n", 1); } OBJ_GETTER0(Kotlin_io_Console_readLine) { - char data[4096]; - if (konan::consoleReadUtf8(data, sizeof(data)) < 0) { - RETURN_OBJ(nullptr); - } - RETURN_RESULT_OF(CreateStringFromCString, data); + char data[4096]; + int32_t result; + { + kotlin::ThreadStateGuard guard(kotlin::ThreadState::kNative); + result = konan::consoleReadUtf8(data, sizeof(data)); + } + if (result < 0) { + RETURN_OBJ(nullptr); + } + RETURN_RESULT_OF(CreateStringFromCString, data); } } // extern "C" diff --git a/kotlin-native/runtime/src/main/cpp/Exceptions.cpp b/kotlin-native/runtime/src/main/cpp/Exceptions.cpp index bbe5e15eff8..51992afa7ac 100644 --- a/kotlin-native/runtime/src/main/cpp/Exceptions.cpp +++ b/kotlin-native/runtime/src/main/cpp/Exceptions.cpp @@ -91,6 +91,9 @@ _Unwind_Reason_Code unwindCallback( #else _Unwind_Ptr address = _Unwind_GetIP(context); #endif + // We run the unwinding process in the native thread state. But setting a next element + // requires writing to a Kotlin array which must be performed in the runnable thread state. + kotlin::ThreadStateGuard guard(kotlin::ThreadState::kRunnable); backtrace->setNextElement(address); return _URC_NO_REASON; @@ -100,7 +103,7 @@ _Unwind_Reason_Code unwindCallback( THREAD_LOCAL_VARIABLE bool disallowSourceInfo = false; #if !OMIT_BACKTRACE && !USE_GCC_UNWIND -SourceInfo getSourceInfo(KConstRef stackTrace, int index) { +SourceInfo getSourceInfo(KConstRef stackTrace, int32_t index) { return disallowSourceInfo ? SourceInfo { .fileName = nullptr, .lineNumber = -1, .column = -1 } : Kotlin_getSourceInfo(*PrimitiveArrayAddressOfElementAt(stackTrace->array(), index)); @@ -112,6 +115,7 @@ SourceInfo getSourceInfo(KConstRef stackTrace, int index) { // TODO: this implementation is just a hack, e.g. the result is inexact; // however it is better to have an inexact stacktrace than not to have any. NO_INLINE OBJ_GETTER0(Kotlin_getCurrentStackTrace) { + using namespace kotlin; #if OMIT_BACKTRACE return AllocArrayInstance(theNativePtrArrayTypeInfo, 0, OBJ_RESULT); #else @@ -119,17 +123,17 @@ NO_INLINE OBJ_GETTER0(Kotlin_getCurrentStackTrace) { constexpr int kSkipFrames = 2; #if USE_GCC_UNWIND int depth = 0; - _Unwind_Backtrace(depthCountCallback, &depth); + CallWithThreadState(_Unwind_Backtrace, depthCountCallback, static_cast(&depth)); Backtrace result(depth, kSkipFrames); if (result.obj()->array()->count_ > 0) { - _Unwind_Backtrace(unwindCallback, &result); + CallWithThreadState(_Unwind_Backtrace, unwindCallback, static_cast(&result)); } RETURN_OBJ(result.obj()); #else const int maxSize = 32; void* buffer[maxSize]; - int size = backtrace(buffer, maxSize); + int size = kotlin::CallWithThreadState(backtrace, buffer, maxSize); if (size < kSkipFrames) return AllocArrayInstance(theNativePtrArrayTypeInfo, 0, OBJ_RESULT); @@ -144,6 +148,7 @@ NO_INLINE OBJ_GETTER0(Kotlin_getCurrentStackTrace) { } OBJ_GETTER(GetStackTraceStrings, KConstRef stackTrace) { + using namespace kotlin; #if OMIT_BACKTRACE ObjHeader* result = AllocArrayInstance(theArrayTypeInfo, 1, OBJ_RESULT); ObjHolder holder; @@ -151,14 +156,14 @@ OBJ_GETTER(GetStackTraceStrings, KConstRef stackTrace) { UpdateHeapRef(ArrayAddressOfElementAt(result->array(), 0), holder.obj()); return result; #else - uint32_t size = stackTrace->array()->count_; + int32_t size = static_cast(stackTrace->array()->count_); ObjHolder resultHolder; ObjHeader* strings = AllocArrayInstance(theArrayTypeInfo, size, resultHolder.slot()); #if USE_GCC_UNWIND - for (uint32_t index = 0; index < size; ++index) { + for (int32_t index = 0; index < size; ++index) { KNativePtr address = Kotlin_NativePtrArray_get(stackTrace, index); char symbol[512]; - if (!AddressToSymbol((const void*) address, symbol, sizeof(symbol))) { + if (!CallWithThreadState(AddressToSymbol, (const void*) address, symbol, sizeof(symbol))) { // Make empty string: symbol[0] = '\0'; } @@ -170,11 +175,12 @@ OBJ_GETTER(GetStackTraceStrings, KConstRef stackTrace) { } #else if (size > 0) { - char **symbols = backtrace_symbols(PrimitiveArrayAddressOfElementAt(stackTrace->array(), 0), size); + char **symbols = CallWithThreadState( + backtrace_symbols, PrimitiveArrayAddressOfElementAt(stackTrace->array(), 0), size); RuntimeCheck(symbols != nullptr, "Not enough memory to retrieve the stacktrace"); - for (uint32_t index = 0; index < size; ++index) { - auto sourceInfo = getSourceInfo(stackTrace, index); + for (int32_t index = 0; index < size; ++index) { + auto sourceInfo = CallWithThreadState(getSourceInfo, stackTrace, index); const char* symbol = symbols[index]; const char* result; char line[1024]; diff --git a/kotlin-native/runtime/src/main/cpp/Memory.h b/kotlin-native/runtime/src/main/cpp/Memory.h index 8d777df87ac..3773379de46 100644 --- a/kotlin-native/runtime/src/main/cpp/Memory.h +++ b/kotlin-native/runtime/src/main/cpp/Memory.h @@ -243,10 +243,13 @@ void LeaveFrame(ObjHeader** start, int parameters, int count) RUNTIME_NOTHROW; // checks if subgraph referenced by given root is disjoint from the rest of // object graph, i.e. no external references exists. bool ClearSubgraphReferences(ObjHeader* root, bool checked) RUNTIME_NOTHROW; -// Creates stable pointer out of the object. +// Creates a stable pointer out of the object. void* CreateStablePointer(ObjHeader* obj) RUNTIME_NOTHROW; -// Disposes stable pointer to the object. +// Disposes a stable pointer to the object. void DisposeStablePointer(void* pointer) RUNTIME_NOTHROW; +// Disposes a stable pointer to the object. +// Accepts a MemoryState, thus can be called from deinitiliazation methods, when TLS is already deallocated. +void DisposeStablePointerFor(MemoryState* memoryState, void* pointer) RUNTIME_NOTHROW; // Translate stable pointer to object reference. OBJ_GETTER(DerefStablePointer, void*) RUNTIME_NOTHROW; // Move stable pointer ownership. @@ -394,8 +397,14 @@ enum class ThreadState { kRunnable, kNative }; +ThreadState GetThreadState(MemoryState* thread) noexcept; + +inline ThreadState GetThreadState() noexcept { + return GetThreadState(mm::GetMemoryState()); +} + // Switches the state of the given thread to `newState` and returns the previous thread state. -ALWAYS_INLINE ThreadState SwitchThreadState(MemoryState* thread, ThreadState newState) noexcept; +ALWAYS_INLINE ThreadState SwitchThreadState(MemoryState* thread, ThreadState newState, bool reentrant = false) noexcept; // Asserts that the given thread is in the given state. ALWAYS_INLINE void AssertThreadState(MemoryState* thread, ThreadState expected) noexcept; @@ -409,35 +418,27 @@ ALWAYS_INLINE inline void AssertThreadState(ThreadState expected) noexcept { class ThreadStateGuard final : private Pinned { public: // Set the state for the given thread. - ThreadStateGuard(MemoryState* thread, ThreadState state) noexcept : thread_(thread) { - oldState_ = SwitchThreadState(thread_, state); + ThreadStateGuard(MemoryState* thread, ThreadState state, bool reentrant = false) noexcept : thread_(thread), reentrant_(reentrant) { + oldState_ = SwitchThreadState(thread_, state, reentrant_); } // Sets the state for the current thread. - explicit ThreadStateGuard(ThreadState state) noexcept - : ThreadStateGuard(mm::GetMemoryState(), state) {}; + explicit ThreadStateGuard(ThreadState state, bool reentrant = false) noexcept + : ThreadStateGuard(mm::GetMemoryState(), state, reentrant) {}; ~ThreadStateGuard() noexcept { - SwitchThreadState(thread_, oldState_); + SwitchThreadState(thread_, oldState_, reentrant_); } private: MemoryState* thread_; ThreadState oldState_; + bool reentrant_; }; -// Calls the given function in the `Runnable` thread state. -template -ALWAYS_INLINE inline R CallKotlin(R(*kotlinFunction)(Args...), Args... args) { - ThreadStateGuard guard(ThreadState::kRunnable); - return kotlinFunction(std::forward(args)...); -} - -// Calls the given function in the `Runnable` thread state. The function must be marked as RUNTIME_NORETURN. -// If the function returns, behaviour is undefined. -template -ALWAYS_INLINE RUNTIME_NORETURN inline void CallKotlinNoReturn(void(*noreturnKotlinFunction)(Args...), Args... args) { - CallKotlin(noreturnKotlinFunction, std::forward(args)...); - RuntimeFail("The function must not return"); +template +ALWAYS_INLINE inline R CallWithThreadState(R(*function)(Args...), Args... args) { + ThreadStateGuard guard(state); + return function(std::forward(args)...); } } // namespace kotlin diff --git a/kotlin-native/runtime/src/main/cpp/Runtime.cpp b/kotlin-native/runtime/src/main/cpp/Runtime.cpp index 2e44c794952..00998d5431a 100644 --- a/kotlin-native/runtime/src/main/cpp/Runtime.cpp +++ b/kotlin-native/runtime/src/main/cpp/Runtime.cpp @@ -105,7 +105,7 @@ RuntimeState* initRuntime() { case DESTROY_RUNTIME_LEGACY: compareAndSwap(&globalRuntimeStatus, kGlobalRuntimeUninitialized, kGlobalRuntimeRunning); result->memoryState = InitMemory(false); // The argument will be ignored for legacy DestroyRuntimeMode - result->worker = WorkerInit(true); + result->worker = WorkerInit(result->memoryState, true); firstRuntime = atomicAdd(&aliveRuntimesCount, 1) == 1; if (CurrentMemoryModel == MemoryModel::kExperimental) { RuntimeCheck(firstRuntime, "Experimental MM does not support multiple mutator threads yet"); @@ -124,7 +124,7 @@ RuntimeState* initRuntime() { RuntimeCheck(firstRuntime, "Experimental MM does not support multiple mutator threads yet"); } result->memoryState = InitMemory(firstRuntime); - result->worker = WorkerInit(true); + result->worker = WorkerInit(result->memoryState, true); } InitOrDeinitGlobalVariables(ALLOC_THREAD_LOCAL_GLOBALS, result->memoryState); @@ -370,4 +370,12 @@ void Kotlin_Debugging_setForceCheckedShutdown(KBoolean value) { g_forceCheckedShutdown = value; } +KBoolean Kotlin_Debugging_isThreadStateRunnable() { + return kotlin::GetThreadState() == kotlin::ThreadState::kRunnable; +} + +KBoolean Kotlin_Debugging_isThreadStateNative() { + return kotlin::GetThreadState() == kotlin::ThreadState::kNative; +} + } // extern "C" diff --git a/kotlin-native/runtime/src/main/cpp/TestSupport.hpp b/kotlin-native/runtime/src/main/cpp/TestSupport.hpp index 66035f44a5f..08fcf540b8f 100644 --- a/kotlin-native/runtime/src/main/cpp/TestSupport.hpp +++ b/kotlin-native/runtime/src/main/cpp/TestSupport.hpp @@ -21,13 +21,16 @@ constexpr int kDefaultThreadCount = 10; constexpr int kDefaultThreadCount = 100; #endif +inline MemoryState* InitMemoryForTests() { return InitMemory(false); } +void DeinitMemoryForTests(MemoryState* memoryState); + // Scopely initializes the memory subsystem of the current thread for tests. -class ScopedRuntimeInit : private kotlin::Pinned { +class ScopedMemoryInit : private kotlin::Pinned { public: - ScopedRuntimeInit() : memoryState_(InitMemory(false)) {} - ~ScopedRuntimeInit() { + ScopedMemoryInit() : memoryState_(InitMemoryForTests()) {} + ~ScopedMemoryInit() { ClearMemoryForTests(memoryState()); - DeinitMemory(memoryState_, false); + DeinitMemoryForTests(memoryState()); } MemoryState* memoryState() { return memoryState_; } @@ -38,7 +41,7 @@ private: // Runs the given function in a separate thread with minimally initialized runtime. inline void RunInNewThread(std::function f) { std::thread([&f]() { - ScopedRuntimeInit init; + ScopedMemoryInit init; f(init.memoryState()); }).join(); } diff --git a/kotlin-native/runtime/src/main/cpp/Worker.cpp b/kotlin-native/runtime/src/main/cpp/Worker.cpp index 004a4b20d65..d4c3e6f97bb 100644 --- a/kotlin-native/runtime/src/main/cpp/Worker.cpp +++ b/kotlin-native/runtime/src/main/cpp/Worker.cpp @@ -36,6 +36,8 @@ #include "Types.h" #include "Worker.h" +using namespace kotlin; + extern "C" { RUNTIME_NORETURN void ThrowWorkerInvalidState(); @@ -150,7 +152,26 @@ class Worker { pthread_t thread() const { return thread_; } + MemoryState* memoryState() { return memoryState_; } + private: + void setThread(pthread_t thread) { + // For workers started using the Worker API, we set thread_ in startEventLoop when calling pthread_create. + // But we also set thread_ in WorkerInit to handle the main thread and threads calling Kotlin from native code. + // In this case, thread id will be set twice for workers started by the Worker API. + // This assert takes this into account. + RuntimeAssert(thread_ == 0 || pthread_equal(thread_, thread), + "Cannot overwrite thread id for worker with id=%d", id()); + thread_ = thread; + } + + void setMemoryState(MemoryState* state) { + RuntimeAssert(memoryState_ == nullptr, "MemoryState is already set for the worker with id=%d", id()); + memoryState_ = state; + } + + friend Worker* WorkerInit(MemoryState* memoryState, KBoolean errorReporting); + KInt id_; WorkerKind kind_; KStdDeque queue_; @@ -164,6 +185,9 @@ class Worker { bool errorReporting_; bool terminated_ = false; pthread_t thread_ = 0; + // MemoryState for worker's thread. + // We set it in WorkerInit and use to correctly switch thread states in woker's destructor. + MemoryState* memoryState_ = nullptr; }; #endif // WITH_WORKERS @@ -184,17 +208,40 @@ KNativePtr transfer(ObjHolder* holder, KInt mode) { return result; } -class Locker { - public: - explicit Locker(pthread_mutex_t* lock) : lock_(lock) { - pthread_mutex_lock(lock_); - } - ~Locker() { - pthread_mutex_unlock(lock_); - } +void waitInNativeState(pthread_cond_t* cond, pthread_mutex_t* mutex) { + CallWithThreadState(pthread_cond_wait, cond, mutex); +} - private: - pthread_mutex_t* lock_; +void waitInNativeState(pthread_cond_t* cond, + pthread_mutex_t* mutex, + uint64_t timeoutNanoseconds, + uint64_t* microsecondsPassed = nullptr) { + CallWithThreadState(WaitOnCondVar, cond, mutex, timeoutNanoseconds, microsecondsPassed); +} + +class Locker { +public: + explicit Locker(pthread_mutex_t* lock, bool switchThreadState = true) : lock_(lock) { + if (switchThreadState) { + kotlin::ThreadStateGuard guard(kotlin::ThreadState::kNative); + pthread_mutex_lock(lock_); + } else { + // We may need to create a locker when the current thread is already unregistered in the memory subsystem. + // For such cases we allow locking without switching or checking the thread state. + pthread_mutex_lock(lock_); + } + } + Locker(pthread_mutex_t* lock, MemoryState* memoryState) : lock_(lock) { + kotlin::ThreadStateGuard guard(memoryState, kotlin::ThreadState::kNative); + pthread_mutex_lock(lock_); + } + + ~Locker() { + pthread_mutex_unlock(lock_); + } + +private: + pthread_mutex_t* lock_; }; class Future { @@ -222,7 +269,7 @@ class Future { OBJ_GETTER0(consumeResultUnlocked) { Locker locker(&lock_); while (state_ == SCHEDULED) { - pthread_cond_wait(&cond_, &lock_); + waitInNativeState(&cond_, &lock_); } // TODO: maybe use message from exception? if (state_ == THROWN) @@ -234,7 +281,7 @@ class Future { void storeResultUnlocked(KNativePtr result, bool ok); - void cancelUnlocked(); + void cancelUnlocked(MemoryState* memoryState); // Those are called with the lock taken. KInt state() const { return state_; } @@ -294,7 +341,11 @@ class State { void destroyWorkerUnlocked(Worker* worker) { { - Locker locker(&lock_); + // We call destroyWorkerUnlocked from runtimeDeinit when TLS may be already deallocated, + // so we have to use the pointer to MemoryState saved in the worker instance. + RuntimeAssert(pthread_equal(worker->thread(), pthread_self()), + "Worker destruction must be executed by the worker thread."); + Locker locker(&lock_, worker->memoryState()); auto id = worker->id(); auto it = workers_.find(id); if (it != workers_.end()) { @@ -404,7 +455,6 @@ class State { auto it = futures_.find(id); if (it == futures_.end()) ThrowWorkerInvalidState(); future = it->second; - } KRef result = future->consumeResultUnlocked(OBJ_RESULT); @@ -424,12 +474,12 @@ class State { OBJ_GETTER(getWorkerNameUnlocked, KInt id) { ObjHolder nameHolder; { - Locker locker(&lock_); - auto it = workers_.find(id); - if (it == workers_.end()) { - ThrowWorkerInvalidState(); - } - DerefStablePointer(it->second->name(), nameHolder.slot()); + Locker locker(&lock_); + auto it = workers_.find(id); + if (it == workers_.end()) { + ThrowWorkerInvalidState(); + } + DerefStablePointer(it->second->name(), nameHolder.slot()); } RETURN_OBJ(nameHolder.obj()); } @@ -439,12 +489,12 @@ class State { if (version != currentVersion_) return false; if (millis < 0) { - pthread_cond_wait(&cond_, &lock_); + waitInNativeState(&cond_, &lock_); return true; } uint64_t nsDelta = millis * 1000000LL; - WaitOnCondVar(&cond_, &lock_, nsDelta); + waitInNativeState(&cond_, &lock_, nsDelta); return true; } @@ -456,6 +506,14 @@ class State { pthread_cond_broadcast(&cond_); } + void signalAnyFuture(MemoryState* memoryState) { + { + Locker locker(&lock_, memoryState); + currentVersion_++; + } + pthread_cond_broadcast(&cond_); + } + KInt versionToken() { Locker locker(&lock_); return currentVersion_; @@ -466,7 +524,9 @@ class State { KInt nextFutureId() { return currentFutureId_++; } void destroyWorkerThreadDataUnlocked(KInt id) { - Locker locker(&lock_); + // We destroy worker data when its thread is already unresigtered from the memory subsystem, + // so there is no need to wrap the lock with a thread state switch. + Locker locker(&lock_, false); auto it = terminating_native_workers_.find(id); if (it == terminating_native_workers_.end()) return; // If this worker was not joined, detach it to free resources. @@ -495,6 +555,7 @@ class State { } } + ThreadStateGuard guard(ThreadState::kNative); for (auto worker : workersToWait) { pthread_join(worker.second, nullptr); } @@ -561,14 +622,14 @@ void Future::storeResultUnlocked(KNativePtr result, bool ok) { theState()->signalAnyFuture(); } -void Future::cancelUnlocked() { +void Future::cancelUnlocked(MemoryState* memoryState) { { - Locker locker(&lock_); + Locker locker(&lock_, memoryState); state_ = CANCELLED; result_ = nullptr; pthread_cond_broadcast(&cond_); } - theState()->signalAnyFuture(); + theState()->signalAnyFuture(memoryState); } // Defined in RuntimeUtils.kt. @@ -672,7 +733,7 @@ KBoolean processQueue(KInt id) { } KBoolean park(KInt id, KLong timeoutMicroseconds, KBoolean process) { - ThrowWorkerUnsupported(); + ThrowWorkerUnsupported(); } KInt currentWorker() { @@ -704,7 +765,7 @@ OBJ_GETTER(attachObjectGraphInternal, KNativePtr stable) { } KNativePtr detachObjectGraphInternal(KInt transferMode, KRef producer) { - ThrowWorkerUnsupported(); + ThrowWorkerUnsupported(); } #endif // WITH_WORKERS @@ -719,11 +780,17 @@ KInt GetWorkerId(Worker* worker) { #endif // WITH_WORKERS } -Worker* WorkerInit(KBoolean errorReporting) { +Worker* WorkerInit(MemoryState* memoryState, KBoolean errorReporting) { #if WITH_WORKERS - if (::g_worker != nullptr) return ::g_worker; - Worker* worker = theState()->addWorkerUnlocked(errorReporting != 0, nullptr, WorkerKind::kOther); - ::g_worker = worker; + Worker* worker; + if (::g_worker != nullptr) { + worker = ::g_worker; + } else { + worker = theState()->addWorkerUnlocked(errorReporting != 0, nullptr, WorkerKind::kOther); + ::g_worker = worker; + } + worker->setThread(pthread_self()); + worker->setMemoryState(memoryState); return worker; #else return nullptr; @@ -745,7 +812,7 @@ void WorkerDestroyThreadDataIfNeeded(KInt id) { void WaitNativeWorkersTermination() { #if WITH_WORKERS - theState()->waitNativeWorkersTerminationUnlocked(true, [](KInt worker) { return true; }); + theState()->waitNativeWorkersTerminationUnlocked(true, [](KInt worker) { return true; }); #endif } @@ -766,36 +833,40 @@ bool WorkerSchedule(KInt id, KNativePtr jobStablePtr) { #if WITH_WORKERS Worker::~Worker() { + RuntimeAssert(pthread_equal(thread(), pthread_self()), + "Worker destruction must be executed by the worker thread."); // Cleanup jobs in the queue. for (auto job : queue_) { - switch (job.kind) { - case JOB_REGULAR: - DisposeStablePointer(job.regularJob.argument); - job.regularJob.future->cancelUnlocked(); - break; - case JOB_EXECUTE_AFTER: { - // TODO: what do we do here? Shall we execute them? - DisposeStablePointer(job.executeAfter.operation); - break; + switch (job.kind) { + case JOB_REGULAR: + DisposeStablePointerFor(memoryState_, job.regularJob.argument); + job.regularJob.future->cancelUnlocked(memoryState_); + break; + case JOB_EXECUTE_AFTER: { + // TODO: what do we do here? Shall we execute them? + DisposeStablePointerFor(memoryState_, job.executeAfter.operation); + break; + } + case JOB_TERMINATE: { + // TODO: any more processing here? + job.terminationRequest.future->cancelUnlocked(memoryState_); + break; + } + case JOB_NONE: { + RuntimeCheck(false, "Cannot be in queue"); + break; + } } - case JOB_TERMINATE: { - // TODO: any more processing here? - job.terminationRequest.future->cancelUnlocked(); - break; - } - case JOB_NONE: { - RuntimeCheck(false, "Cannot be in queue"); - break; - } - } } for (auto job : delayed_) { - RuntimeAssert(job.kind == JOB_EXECUTE_AFTER, "Must be delayed"); - DisposeStablePointer(job.executeAfter.operation); + RuntimeAssert(job.kind == JOB_EXECUTE_AFTER, "Must be delayed"); + DisposeStablePointerFor(memoryState_, job.executeAfter.operation); } - if (name_ != nullptr) DisposeStablePointer(name_); + if (name_ != nullptr) { + DisposeStablePointerFor(memoryState_, name_); + } pthread_mutex_destroy(&lock_); pthread_cond_destroy(&cond_); @@ -892,12 +963,12 @@ bool Worker::waitForQueueLocked(KLong timeoutMicroseconds, KLong* remaining) { closestToRunMicroseconds = 10LL * 1000 * 1000 * 1000 * 1000; uint64_t nsDelta = closestToRunMicroseconds * 1000LL; uint64_t microsecondsPassed = 0; - WaitOnCondVar(&cond_, &lock_, nsDelta, remaining ? µsecondsPassed : nullptr); + waitInNativeState(&cond_, &lock_, nsDelta, remaining ? µsecondsPassed : nullptr); if (remaining) { *remaining = timeoutMicroseconds - microsecondsPassed; } } else { - pthread_cond_wait(&cond_, &lock_); + waitInNativeState(&cond_, &lock_); if (remaining) *remaining = 0; } if (timeoutMicroseconds >= 0) return queue_.size() != 0; @@ -928,8 +999,6 @@ bool Worker::park(KLong timeoutMicroseconds, bool process) { JobKind Worker::processQueueElement(bool blocking) { GC_CollectorCallback(this); - ObjHolder argumentHolder; - ObjHolder resultHolder; if (terminated_) return JOB_TERMINATE; Job job = getJob(blocking); switch (job.kind) { @@ -965,9 +1034,11 @@ JobKind Worker::processQueueElement(bool blocking) { break; } case JOB_REGULAR: { - KRef argument = AdoptStablePointer(job.regularJob.argument, argumentHolder.slot()); KNativePtr result = nullptr; bool ok = true; + ObjHolder argumentHolder; + ObjHolder resultHolder; + KRef argument = AdoptStablePointer(job.regularJob.argument, argumentHolder.slot()); try { #if KONAN_OBJC_INTEROP konan::AutoreleasePool autoreleasePool; @@ -976,14 +1047,14 @@ JobKind Worker::processQueueElement(bool blocking) { argumentHolder.clear(); // Transfer the result. result = transfer(&resultHolder, job.regularJob.transferMode); - } catch (ExceptionObjHolder& e) { - ok = false; - if (errorReporting()) - ReportUnhandledException(e.GetExceptionObject()); - } - // Notify the future. - job.regularJob.future->storeResultUnlocked(result, ok); - break; + } catch (ExceptionObjHolder& e) { + ok = false; + if (errorReporting()) + ReportUnhandledException(e.GetExceptionObject()); + } + // Notify the future. + job.regularJob.future->storeResultUnlocked(result, ok); + break; } default: { RuntimeCheck(false, "Must be exhaustive"); diff --git a/kotlin-native/runtime/src/main/cpp/Worker.h b/kotlin-native/runtime/src/main/cpp/Worker.h index bb99194b61a..963517bbea0 100644 --- a/kotlin-native/runtime/src/main/cpp/Worker.h +++ b/kotlin-native/runtime/src/main/cpp/Worker.h @@ -8,7 +8,7 @@ class Worker; KInt GetWorkerId(Worker* worker); -Worker* WorkerInit(KBoolean errorReporting); +Worker* WorkerInit(MemoryState* memoryState, KBoolean errorReporting); void WorkerDeinit(Worker* worker); // Clean up all associated thread state, if this was a native worker. void WorkerDestroyThreadDataIfNeeded(KInt id); diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/Any.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/Any.kt index c3b4b927e02..6e09de81f04 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/Any.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/Any.kt @@ -7,6 +7,7 @@ package kotlin import kotlin.native.identityHashCode import kotlin.native.internal.ExportTypeInfo +import kotlin.native.internal.GCUnsafeCall /** * The root of the Kotlin class hierarchy. Every Kotlin class has [Any] as a superclass. @@ -25,7 +26,7 @@ public open class Any { * * Read more about [equality](https://kotlinlang.org/docs/reference/equality.html) in Kotlin. */ - @SymbolName("Kotlin_Any_equals") + @GCUnsafeCall("Kotlin_Any_equals") external public open operator fun equals(other: Any?): Boolean /** diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/Array.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/Array.kt index 58eecb7e931..6e621e97490 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/Array.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/Array.kt @@ -7,6 +7,7 @@ package kotlin import kotlin.native.internal.ExportForCompiler import kotlin.native.internal.ExportTypeInfo +import kotlin.native.internal.GCUnsafeCall import kotlin.native.internal.PointsTo /** @@ -53,7 +54,7 @@ public final class Array { * * If the [index] is out of bounds of this array, throws an [IndexOutOfBoundsException]. */ - @SymbolName("Kotlin_Array_get") + @GCUnsafeCall("Kotlin_Array_get") @PointsTo(0x000, 0x000, 0x002) // ret -> this.intestines external public operator fun get(index: Int): T @@ -66,7 +67,7 @@ public final class Array { * * If the [index] is out of bounds of this array, throws an [IndexOutOfBoundsException]. */ - @SymbolName("Kotlin_Array_set") + @GCUnsafeCall("Kotlin_Array_set") @PointsTo(0x300, 0x000, 0x000) // this.intestines -> value external public operator fun set(index: Int, value: T): Unit @@ -77,7 +78,7 @@ public final class Array { return IteratorImpl(this) } - @SymbolName("Kotlin_Array_getArrayLength") + @GCUnsafeCall("Kotlin_Array_getArrayLength") external private fun getArrayLength(): Int } diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/Arrays.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/Arrays.kt index 638019f3594..4f16ffbf415 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/Arrays.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/Arrays.kt @@ -11,6 +11,7 @@ package kotlin import kotlin.internal.PureReifiable import kotlin.native.internal.ExportTypeInfo import kotlin.native.internal.IntrinsicType +import kotlin.native.internal.GCUnsafeCall import kotlin.native.internal.TypedIntrinsic import kotlin.native.internal.PointsTo @@ -44,7 +45,7 @@ public final class ByteArray { * * If the [index] is out of bounds of this array, throws an [IndexOutOfBoundsException]. */ - @SymbolName("Kotlin_ByteArray_get") + @GCUnsafeCall("Kotlin_ByteArray_get") external public operator fun get(index: Int): Byte /** @@ -52,10 +53,10 @@ public final class ByteArray { * * If the [index] is out of bounds of this array, throws an [IndexOutOfBoundsException]. */ - @SymbolName("Kotlin_ByteArray_set") + @GCUnsafeCall("Kotlin_ByteArray_set") external public operator fun set(index: Int, value: Byte): Unit - @SymbolName("Kotlin_ByteArray_getArrayLength") + @GCUnsafeCall("Kotlin_ByteArray_getArrayLength") external private fun getArrayLength(): Int /** Creates an iterator over the elements of the array. */ @@ -108,7 +109,7 @@ public final class CharArray { * * If the [index] is out of bounds of this array, throws an [IndexOutOfBoundsException]. */ - @SymbolName("Kotlin_CharArray_get") + @GCUnsafeCall("Kotlin_CharArray_get") external public operator fun get(index: Int): Char /** @@ -116,10 +117,10 @@ public final class CharArray { * * If the [index] is out of bounds of this array, throws an [IndexOutOfBoundsException]. */ - @SymbolName("Kotlin_CharArray_set") + @GCUnsafeCall("Kotlin_CharArray_set") external public operator fun set(index: Int, value: Char): Unit - @SymbolName("Kotlin_CharArray_getArrayLength") + @GCUnsafeCall("Kotlin_CharArray_getArrayLength") external private fun getArrayLength(): Int /** Creates an iterator over the elements of the array. */ @@ -172,7 +173,7 @@ public final class ShortArray { * * If the [index] is out of bounds of this array, throws an [IndexOutOfBoundsException]. */ - @SymbolName("Kotlin_ShortArray_get") + @GCUnsafeCall("Kotlin_ShortArray_get") external public operator fun get(index: Int): Short /** @@ -180,10 +181,10 @@ public final class ShortArray { * * If the [index] is out of bounds of this array, throws an [IndexOutOfBoundsException]. */ - @SymbolName("Kotlin_ShortArray_set") + @GCUnsafeCall("Kotlin_ShortArray_set") external public operator fun set(index: Int, value: Short): Unit - @SymbolName("Kotlin_ShortArray_getArrayLength") + @GCUnsafeCall("Kotlin_ShortArray_getArrayLength") external private fun getArrayLength(): Int /** Creates an iterator over the elements of the array. */ @@ -236,7 +237,7 @@ public final class IntArray { * * If the [index] is out of bounds of this array, throws an [IndexOutOfBoundsException]. */ - @SymbolName("Kotlin_IntArray_get") + @GCUnsafeCall("Kotlin_IntArray_get") external public operator fun get(index: Int): Int /** @@ -244,10 +245,10 @@ public final class IntArray { * * If the [index] is out of bounds of this array, throws an [IndexOutOfBoundsException]. */ - @SymbolName("Kotlin_IntArray_set") + @GCUnsafeCall("Kotlin_IntArray_set") external public operator fun set(index: Int, value: Int): Unit - @SymbolName("Kotlin_IntArray_getArrayLength") + @GCUnsafeCall("Kotlin_IntArray_getArrayLength") external private fun getArrayLength(): Int /** Creates an iterator over the elements of the array. */ @@ -300,7 +301,7 @@ public final class LongArray { * * If the [index] is out of bounds of this array, throws an [IndexOutOfBoundsException]. */ - @SymbolName("Kotlin_LongArray_get") + @GCUnsafeCall("Kotlin_LongArray_get") external public operator fun get(index: Int): Long /** @@ -308,10 +309,10 @@ public final class LongArray { * * If the [index] is out of bounds of this array, throws an [IndexOutOfBoundsException]. */ - @SymbolName("Kotlin_LongArray_set") + @GCUnsafeCall("Kotlin_LongArray_set") external public operator fun set(index: Int, value: Long): Unit - @SymbolName("Kotlin_LongArray_getArrayLength") + @GCUnsafeCall("Kotlin_LongArray_getArrayLength") external private fun getArrayLength(): Int /** Creates an iterator over the elements of the array. */ @@ -364,7 +365,7 @@ public final class FloatArray { * * If the [index] is out of bounds of this array, throws an [IndexOutOfBoundsException]. */ - @SymbolName("Kotlin_FloatArray_get") + @GCUnsafeCall("Kotlin_FloatArray_get") external public operator fun get(index: Int): Float /** @@ -372,10 +373,10 @@ public final class FloatArray { * * If the [index] is out of bounds of this array, throws an [IndexOutOfBoundsException]. */ - @SymbolName("Kotlin_FloatArray_set") + @GCUnsafeCall("Kotlin_FloatArray_set") external public operator fun set(index: Int, value: Float): Unit - @SymbolName("Kotlin_FloatArray_getArrayLength") + @GCUnsafeCall("Kotlin_FloatArray_getArrayLength") external private fun getArrayLength(): Int /** Creates an iterator over the elements of the array. */ @@ -424,7 +425,7 @@ public final class DoubleArray { * * If the [index] is out of bounds of this array, throws an [IndexOutOfBoundsException]. */ - @SymbolName("Kotlin_DoubleArray_get") + @GCUnsafeCall("Kotlin_DoubleArray_get") external public operator fun get(index: Int): Double /** @@ -432,10 +433,10 @@ public final class DoubleArray { * * If the [index] is out of bounds of this array, throws an [IndexOutOfBoundsException]. */ - @SymbolName("Kotlin_DoubleArray_set") + @GCUnsafeCall("Kotlin_DoubleArray_set") external public operator fun set(index: Int, value: Double): Unit - @SymbolName("Kotlin_DoubleArray_getArrayLength") + @GCUnsafeCall("Kotlin_DoubleArray_getArrayLength") external private fun getArrayLength(): Int /** Creates an iterator over the elements of the array. */ @@ -484,7 +485,7 @@ public final class BooleanArray { * * If the [index] is out of bounds of this array, throws an [IndexOutOfBoundsException]. */ - @SymbolName("Kotlin_BooleanArray_get") + @GCUnsafeCall("Kotlin_BooleanArray_get") external public operator fun get(index: Int): Boolean /** @@ -492,10 +493,10 @@ public final class BooleanArray { * * If the [index] is out of bounds of this array, throws an [IndexOutOfBoundsException]. */ - @SymbolName("Kotlin_BooleanArray_set") + @GCUnsafeCall("Kotlin_BooleanArray_set") external public operator fun set(index: Int, value: Boolean): Unit - @SymbolName("Kotlin_BooleanArray_getArrayLength") + @GCUnsafeCall("Kotlin_BooleanArray_getArrayLength") external private fun getArrayLength(): Int /** Creates an iterator over the elements of the array. */ @@ -531,7 +532,7 @@ public inline fun arrayOfNulls(size: Int): Array @PointsTo(0x00, 0x01) // ret -> elements public external inline fun arrayOf(vararg elements: T): Array -@SymbolName("Kotlin_emptyArray") +@GCUnsafeCall("Kotlin_emptyArray") external public fun emptyArray(): Array /** diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/Char.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/Char.kt index b92438c69b1..fda553f8f31 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/Char.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/Char.kt @@ -6,6 +6,7 @@ package kotlin +import kotlin.native.internal.GCUnsafeCall import kotlin.native.internal.TypedIntrinsic import kotlin.native.internal.IntrinsicType @@ -171,7 +172,7 @@ public class Char private constructor() : Comparable { public override fun equals(other: Any?): Boolean = other is Char && this.equals(other) - @SymbolName("Kotlin_Char_toString") + @GCUnsafeCall("Kotlin_Char_toString") external public override fun toString(): String public override fun hashCode(): Int { diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/Numbers.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/Numbers.kt index 9071f4b0d41..1d9ac26c0e9 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/Numbers.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/Numbers.kt @@ -5,6 +5,7 @@ package kotlin +import kotlin.native.internal.GCUnsafeCall import kotlin.native.internal.TypedIntrinsic import kotlin.native.internal.IntrinsicType @@ -12,38 +13,38 @@ import kotlin.native.internal.IntrinsicType * Returns `true` if the specified number is a * Not-a-Number (NaN) value, `false` otherwise. */ -@SymbolName("Kotlin_Double_isNaN") +@GCUnsafeCall("Kotlin_Double_isNaN") public actual external fun Double.isNaN(): Boolean /** * Returns `true` if the specified number is a * Not-a-Number (NaN) value, `false` otherwise. */ -@SymbolName("Kotlin_Float_isNaN") +@GCUnsafeCall("Kotlin_Float_isNaN") public actual external fun Float.isNaN(): Boolean /** * Returns `true` if this value is infinitely large in magnitude. */ -@SymbolName("Kotlin_Double_isInfinite") +@GCUnsafeCall("Kotlin_Double_isInfinite") public actual external fun Double.isInfinite(): Boolean /** * Returns `true` if this value is infinitely large in magnitude. */ -@SymbolName("Kotlin_Float_isInfinite") +@GCUnsafeCall("Kotlin_Float_isInfinite") public actual external fun Float.isInfinite(): Boolean /** * Returns `true` if the argument is a finite floating-point value; returns `false` otherwise (for `NaN` and infinity arguments). */ -@SymbolName("Kotlin_Double_isFinite") +@GCUnsafeCall("Kotlin_Double_isFinite") public actual external fun Double.isFinite(): Boolean /** * Returns `true` if the argument is a finite floating-point value; returns `false` otherwise (for `NaN` and infinity arguments). */ -@SymbolName("Kotlin_Float_isFinite") +@GCUnsafeCall("Kotlin_Float_isFinite") public actual external fun Float.isFinite(): Boolean /** @@ -109,14 +110,14 @@ internal external fun fromBits(bits: Int): Float */ @SinceKotlin("1.4") @WasExperimental(ExperimentalStdlibApi::class) -@SymbolName("Kotlin_Int_countOneBits") +@GCUnsafeCall("Kotlin_Int_countOneBits") public actual external fun Int.countOneBits(): Int /** * Counts the number of consecutive most significant bits that are zero in the binary representation of [Int] [value]. * Returns undefined result for zero [value]. */ -@SymbolName("Kotlin_Int_countLeadingZeroBits") +@GCUnsafeCall("Kotlin_Int_countLeadingZeroBits") private external fun countLeadingZeroBits(value: Int): Int /** @@ -131,7 +132,7 @@ public actual fun Int.countLeadingZeroBits(): Int = * Counts the number of consecutive least significant bits that are zero in the binary representation of [Int] [value]. * Returns undefined result for zero [value]. */ -@SymbolName("Kotlin_Int_countTrailingZeroBits") +@GCUnsafeCall("Kotlin_Int_countTrailingZeroBits") private external fun countTrailingZeroBits(value: Int): Int /** @@ -197,14 +198,14 @@ public actual fun Int.rotateRight(bitCount: Int): Int = */ @SinceKotlin("1.4") @WasExperimental(ExperimentalStdlibApi::class) -@SymbolName("Kotlin_Long_countOneBits") +@GCUnsafeCall("Kotlin_Long_countOneBits") public actual external fun Long.countOneBits(): Int /** * Counts the number of consecutive most significant bits that are zero in the binary representation of [Long] [value]. * Returns undefined result for zero [value]. */ -@SymbolName("Kotlin_Long_countLeadingZeroBits") +@GCUnsafeCall("Kotlin_Long_countLeadingZeroBits") private external fun countLeadingZeroBits(value: Long): Int /** @@ -219,7 +220,7 @@ public actual fun Long.countLeadingZeroBits(): Int = * Counts the number of consecutive least significant bits that are zero in the binary representation of [Long] [value]. * Returns undefined result for zero [value]. */ -@SymbolName("Kotlin_Long_countTrailingZeroBits") +@GCUnsafeCall("Kotlin_Long_countTrailingZeroBits") private external fun countTrailingZeroBits(value: Long): Int /** diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/Primitives.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/Primitives.kt index 17268a00ba3..f22954e7d5f 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/Primitives.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/Primitives.kt @@ -7,10 +7,7 @@ package kotlin -import kotlin.native.internal.CanBePrecreated -import kotlin.native.internal.IntrinsicType -import kotlin.native.internal.NumberConverter -import kotlin.native.internal.TypedIntrinsic +import kotlin.native.internal.* /** * Represents a 8-bit signed integer. @@ -307,7 +304,7 @@ public final class Byte private constructor() : Number(), Comparable { public override fun equals(other: Any?): Boolean = other is Byte && kotlin.native.internal.areEqualByValue(this, other) - @SymbolName("Kotlin_Byte_toString") + @GCUnsafeCall("Kotlin_Byte_toString") external public override fun toString(): String public override fun hashCode(): Int { @@ -609,7 +606,7 @@ public final class Short private constructor() : Number(), Comparable { public override fun equals(other: Any?): Boolean = other is Short && kotlin.native.internal.areEqualByValue(this, other) - @SymbolName("Kotlin_Short_toString") + @GCUnsafeCall("Kotlin_Short_toString") external public override fun toString(): String public override fun hashCode(): Int { @@ -953,7 +950,7 @@ public final class Int private constructor() : Number(), Comparable { public override fun equals(other: Any?): Boolean = other is Int && kotlin.native.internal.areEqualByValue(this, other) - @SymbolName("Kotlin_Int_toString") + @GCUnsafeCall("Kotlin_Int_toString") external public override fun toString(): String public override fun hashCode(): Int { @@ -1301,7 +1298,7 @@ public final class Long private constructor() : Number(), Comparable { public override fun equals(other: Any?): Boolean = other is Long && kotlin.native.internal.areEqualByValue(this, other) - @SymbolName("Kotlin_Long_toString") + @GCUnsafeCall("Kotlin_Long_toString") external public override fun toString(): String public override fun hashCode(): Int { @@ -1577,7 +1574,7 @@ public final class Float private constructor() : Number(), Comparable { * Returns zero if this `Float` value is `NaN`, [Int.MIN_VALUE] if it's less than `Int.MIN_VALUE`, * [Int.MAX_VALUE] if it's bigger than `Int.MAX_VALUE`. */ - @SymbolName("Kotlin_Float_toInt") + @GCUnsafeCall("Kotlin_Float_toInt") external public override fun toInt(): Int /** * Converts this [Float] value to [Long]. @@ -1586,7 +1583,7 @@ public final class Float private constructor() : Number(), Comparable { * Returns zero if this `Float` value is `NaN`, [Long.MIN_VALUE] if it's less than `Long.MIN_VALUE`, * [Long.MAX_VALUE] if it's bigger than `Long.MAX_VALUE`. */ - @SymbolName("Kotlin_Float_toLong") + @GCUnsafeCall("Kotlin_Float_toLong") external public override fun toLong(): Long /** Returns this value. */ @@ -1887,7 +1884,7 @@ public final class Double private constructor() : Number(), Comparable { * Returns zero if this `Double` value is `NaN`, [Int.MIN_VALUE] if it's less than `Int.MIN_VALUE`, * [Int.MAX_VALUE] if it's bigger than `Int.MAX_VALUE`. */ - @SymbolName("Kotlin_Double_toInt") + @GCUnsafeCall("Kotlin_Double_toInt") external public override fun toInt(): Int /** * Converts this [Double] value to [Long]. @@ -1896,7 +1893,7 @@ public final class Double private constructor() : Number(), Comparable { * Returns zero if this `Double` value is `NaN`, [Long.MIN_VALUE] if it's less than `Long.MIN_VALUE`, * [Long.MAX_VALUE] if it's bigger than `Long.MAX_VALUE`. */ - @SymbolName("Kotlin_Double_toLong") + @GCUnsafeCall("Kotlin_Double_toLong") external public override fun toLong(): Long /** * Converts this [Double] value to [Float]. diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/String.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/String.kt index 5e31807da15..07dc633a313 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/String.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/String.kt @@ -7,6 +7,7 @@ package kotlin import kotlin.native.internal.ExportTypeInfo import kotlin.native.internal.Frozen +import kotlin.native.internal.GCUnsafeCall @ExportTypeInfo("theStringTypeInfo") @Frozen @@ -14,7 +15,7 @@ public final class String : Comparable, CharSequence { public companion object { } - @SymbolName("Kotlin_String_hashCode") + @GCUnsafeCall("Kotlin_String_hashCode") external public override fun hashCode(): Int public operator fun plus(other: Any?): String { @@ -33,23 +34,23 @@ public final class String : Comparable, CharSequence { * * If the [index] is out of bounds of this string, throws an [IndexOutOfBoundsException]. */ - @SymbolName("Kotlin_String_get") - external override public fun get(index: Int): Char + @GCUnsafeCall("Kotlin_String_get") + public external override fun get(index: Int): Char - @SymbolName("Kotlin_String_subSequence") - external override public fun subSequence(startIndex: Int, endIndex: Int): CharSequence + @GCUnsafeCall("Kotlin_String_subSequence") + public external override fun subSequence(startIndex: Int, endIndex: Int): CharSequence - @SymbolName("Kotlin_String_compareTo") - override external public fun compareTo(other: String): Int + @GCUnsafeCall("Kotlin_String_compareTo") + public external override fun compareTo(other: String): Int - @SymbolName("Kotlin_String_getStringLength") - external private fun getStringLength(): Int + @GCUnsafeCall("Kotlin_String_getStringLength") + private external fun getStringLength(): Int - @SymbolName("Kotlin_String_plusImpl") - external private fun plusImpl(other: String): String + @GCUnsafeCall("Kotlin_String_plusImpl") + private external fun plusImpl(other: String): String - @SymbolName("Kotlin_String_equals") - external public override fun equals(other: Any?): Boolean + @GCUnsafeCall("Kotlin_String_equals") + external override fun equals(other: Any?): Boolean } public inline operator fun kotlin.String?.plus(other: kotlin.Any?): kotlin.String = diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/Throwable.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/Throwable.kt index 391a1ba273a..815536707c8 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/Throwable.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/Throwable.kt @@ -9,6 +9,7 @@ import kotlin.native.concurrent.freeze import kotlin.native.concurrent.isFrozen import kotlin.native.internal.ExportForCppRuntime import kotlin.native.internal.ExportTypeInfo +import kotlin.native.internal.GCUnsafeCall import kotlin.native.internal.NativePtrArray /** @@ -139,10 +140,10 @@ public open class Throwable(open val message: String?, open val cause: Throwable internal var suppressedExceptionsList: MutableList? = null } -@SymbolName("Kotlin_getCurrentStackTrace") +@GCUnsafeCall("Kotlin_getCurrentStackTrace") private external fun getCurrentStackTrace(): NativePtrArray -@SymbolName("Kotlin_getStackTraceStrings") +@GCUnsafeCall("Kotlin_getStackTraceStrings") private external fun getStackTraceStrings(stackTrace: NativePtrArray): Array /** diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/collections/ArrayUtil.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/collections/ArrayUtil.kt index ad19f96b15b..92b819ed187 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/collections/ArrayUtil.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/collections/ArrayUtil.kt @@ -7,6 +7,7 @@ package kotlin.collections import kotlin.native.internal.PointsTo import kotlin.native.internal.ExportForCppRuntime +import kotlin.native.internal.GCUnsafeCall /** * Returns an array of objects of the given type with the given [size], initialized with _uninitialized_ values. @@ -75,32 +76,32 @@ internal fun Array.resetAt(index: Int) { (@Suppress("UNCHECKED_CAST")(this as Array))[index] = null } -@SymbolName("Kotlin_Array_fillImpl") +@GCUnsafeCall("Kotlin_Array_fillImpl") @PointsTo(0x3000, 0x0000, 0x0000, 0x0000) // array.intestines -> value internal external fun arrayFill(array: Array, fromIndex: Int, toIndex: Int, value: T) -@SymbolName("Kotlin_ByteArray_fillImpl") +@GCUnsafeCall("Kotlin_ByteArray_fillImpl") internal external fun arrayFill(array: ByteArray, fromIndex: Int, toIndex: Int, value: Byte) -@SymbolName("Kotlin_ShortArray_fillImpl") +@GCUnsafeCall("Kotlin_ShortArray_fillImpl") internal external fun arrayFill(array: ShortArray, fromIndex: Int, toIndex: Int, value: Short) -@SymbolName("Kotlin_CharArray_fillImpl") +@GCUnsafeCall("Kotlin_CharArray_fillImpl") internal external fun arrayFill(array: CharArray, fromIndex: Int, toIndex: Int, value: Char) -@SymbolName("Kotlin_IntArray_fillImpl") +@GCUnsafeCall("Kotlin_IntArray_fillImpl") internal external fun arrayFill(array: IntArray, fromIndex: Int, toIndex: Int, value: Int) -@SymbolName("Kotlin_LongArray_fillImpl") +@GCUnsafeCall("Kotlin_LongArray_fillImpl") internal external fun arrayFill(array: LongArray, fromIndex: Int, toIndex: Int, value: Long) -@SymbolName("Kotlin_DoubleArray_fillImpl") +@GCUnsafeCall("Kotlin_DoubleArray_fillImpl") internal external fun arrayFill(array: DoubleArray, fromIndex: Int, toIndex: Int, value: Double) -@SymbolName("Kotlin_FloatArray_fillImpl") +@GCUnsafeCall("Kotlin_FloatArray_fillImpl") internal external fun arrayFill(array: FloatArray, fromIndex: Int, toIndex: Int, value: Float) -@SymbolName("Kotlin_BooleanArray_fillImpl") +@GCUnsafeCall("Kotlin_BooleanArray_fillImpl") internal external fun arrayFill(array: BooleanArray, fromIndex: Int, toIndex: Int, value: Boolean) @ExportForCppRuntime @@ -124,32 +125,32 @@ internal fun Array.resetRange(fromIndex: Int, toIndex: Int) { arrayFill(@Suppress("UNCHECKED_CAST") (this as Array), fromIndex, toIndex, null) } -@SymbolName("Kotlin_Array_copyImpl") +@GCUnsafeCall("Kotlin_Array_copyImpl") @PointsTo(0x00000, 0x00000, 0x00004, 0x00000, 0x00000) // destination.intestines -> array.intestines internal external fun arrayCopy(array: Array, fromIndex: Int, destination: Array, toIndex: Int, count: Int) -@SymbolName("Kotlin_ByteArray_copyImpl") +@GCUnsafeCall("Kotlin_ByteArray_copyImpl") internal external fun arrayCopy(array: ByteArray, fromIndex: Int, destination: ByteArray, toIndex: Int, count: Int) -@SymbolName("Kotlin_ShortArray_copyImpl") +@GCUnsafeCall("Kotlin_ShortArray_copyImpl") internal external fun arrayCopy(array: ShortArray, fromIndex: Int, destination: ShortArray, toIndex: Int, count: Int) -@SymbolName("Kotlin_CharArray_copyImpl") +@GCUnsafeCall("Kotlin_CharArray_copyImpl") internal external fun arrayCopy(array: CharArray, fromIndex: Int, destination: CharArray, toIndex: Int, count: Int) -@SymbolName("Kotlin_IntArray_copyImpl") +@GCUnsafeCall("Kotlin_IntArray_copyImpl") internal external fun arrayCopy(array: IntArray, fromIndex: Int, destination: IntArray, toIndex: Int, count: Int) -@SymbolName("Kotlin_LongArray_copyImpl") +@GCUnsafeCall("Kotlin_LongArray_copyImpl") internal external fun arrayCopy(array: LongArray, fromIndex: Int, destination: LongArray, toIndex: Int, count: Int) -@SymbolName("Kotlin_FloatArray_copyImpl") +@GCUnsafeCall("Kotlin_FloatArray_copyImpl") internal external fun arrayCopy(array: FloatArray, fromIndex: Int, destination: FloatArray, toIndex: Int, count: Int) -@SymbolName("Kotlin_DoubleArray_copyImpl") +@GCUnsafeCall("Kotlin_DoubleArray_copyImpl") internal external fun arrayCopy(array: DoubleArray, fromIndex: Int, destination: DoubleArray, toIndex: Int, count: Int) -@SymbolName("Kotlin_BooleanArray_copyImpl") +@GCUnsafeCall("Kotlin_BooleanArray_copyImpl") internal external fun arrayCopy(array: BooleanArray, fromIndex: Int, destination: BooleanArray, toIndex: Int, count: Int) diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/io/Console.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/io/Console.kt index a0b3bc11e1f..c2726a29278 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/io/Console.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/io/Console.kt @@ -5,9 +5,11 @@ package kotlin.io +import kotlin.native.internal.GCUnsafeCall + /** Prints the given [message] to the standard output stream. */ -@SymbolName("Kotlin_io_Console_print") -external public fun print(message: String) +@GCUnsafeCall("Kotlin_io_Console_print") +public external fun print(message: String) /** Prints the given [message] to the standard output stream. */ public actual fun print(message: Any?) { @@ -15,8 +17,8 @@ public actual fun print(message: Any?) { } /** Prints the given [message] and the line separator to the standard output stream. */ -@SymbolName("Kotlin_io_Console_println") -external public fun println(message: String) +@GCUnsafeCall("Kotlin_io_Console_println") +public external fun println(message: String) /** Prints the given [message] and the line separator to the standard output stream. */ public actual fun println(message: Any?) { @@ -24,13 +26,13 @@ public actual fun println(message: Any?) { } /** Prints the line separator to the standard output stream. */ -@SymbolName("Kotlin_io_Console_println0") -external public actual fun println() +@GCUnsafeCall("Kotlin_io_Console_println0") +public actual external fun println() /** * Reads a line of input from the standard input stream. * * @return the line read or `null` if the input stream is redirected to a file and the end of file has been reached. */ -@SymbolName("Kotlin_io_Console_readLine") -external public fun readLine(): String? +@GCUnsafeCall("Kotlin_io_Console_readLine") +public external fun readLine(): String? diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/math/math.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/math/math.kt index b288cc64725..66ab5dd5f78 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/math/math.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/math/math.kt @@ -5,6 +5,8 @@ package kotlin.math +import kotlin.native.internal.GCUnsafeCall + // region ================ Double Math ======================================== /** Computes the sine of the angle [x] given in radians. @@ -13,7 +15,7 @@ package kotlin.math * - `sin(NaN|+Inf|-Inf)` is `NaN` */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_sin") +@GCUnsafeCall("Kotlin_math_sin") external public actual fun sin(x: Double): Double /** Computes the cosine of the angle [x] given in radians. @@ -22,7 +24,7 @@ external public actual fun sin(x: Double): Double * - `cos(NaN|+Inf|-Inf)` is `NaN` */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_cos") +@GCUnsafeCall("Kotlin_math_cos") external public actual fun cos(x: Double): Double /** Computes the tangent of the angle [x] given in radians. @@ -31,7 +33,7 @@ external public actual fun cos(x: Double): Double * - `tan(NaN|+Inf|-Inf)` is `NaN` */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_tan") +@GCUnsafeCall("Kotlin_math_tan") external public actual fun tan(x: Double): Double /** @@ -42,7 +44,7 @@ external public actual fun tan(x: Double): Double * - `asin(x)` is `NaN`, when `abs(x) > 1` or x is `NaN` */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_asin") +@GCUnsafeCall("Kotlin_math_asin") external public actual fun asin(x: Double): Double /** @@ -53,7 +55,7 @@ external public actual fun asin(x: Double): Double * - `acos(x)` is `NaN`, when `abs(x) > 1` or x is `NaN` */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_acos") +@GCUnsafeCall("Kotlin_math_acos") external public actual fun acos(x: Double): Double /** @@ -64,7 +66,7 @@ external public actual fun acos(x: Double): Double * - `atan(NaN)` is `NaN` */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_atan") +@GCUnsafeCall("Kotlin_math_atan") external public actual fun atan(x: Double): Double /** @@ -84,7 +86,7 @@ external public actual fun atan(x: Double): Double * - `atan2(NaN, x)` and `atan2(y, NaN)` is `NaN` */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_atan2") +@GCUnsafeCall("Kotlin_math_atan2") external public actual fun atan2(y: Double, x: Double): Double /** @@ -96,7 +98,7 @@ external public actual fun atan2(y: Double, x: Double): Double * - `sinh(-Inf)` is `-Inf` */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_sinh") +@GCUnsafeCall("Kotlin_math_sinh") external public actual fun sinh(x: Double): Double /** @@ -107,7 +109,7 @@ external public actual fun sinh(x: Double): Double * - `cosh(+Inf|-Inf)` is `+Inf` */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_cosh") +@GCUnsafeCall("Kotlin_math_cosh") external public actual fun cosh(x: Double): Double /** @@ -119,7 +121,7 @@ external public actual fun cosh(x: Double): Double * - `tanh(-Inf)` is `-1.0` */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_tanh") +@GCUnsafeCall("Kotlin_math_tanh") external public actual fun tanh(x: Double): Double /** @@ -133,7 +135,7 @@ external public actual fun tanh(x: Double): Double * - `asinh(-Inf)` is `-Inf` */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_asinh") +@GCUnsafeCall("Kotlin_math_asinh") external public actual fun asinh(x: Double): Double /** @@ -147,7 +149,7 @@ external public actual fun asinh(x: Double): Double * - `acosh(+Inf)` is `+Inf` */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_acosh") +@GCUnsafeCall("Kotlin_math_acosh") external public actual fun acosh(x: Double): Double /** @@ -162,7 +164,7 @@ external public actual fun acosh(x: Double): Double * - `tanh(-1.0)` is `-Inf` */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_atanh") +@GCUnsafeCall("Kotlin_math_atanh") external public actual fun atanh(x: Double): Double /** @@ -173,7 +175,7 @@ external public actual fun atanh(x: Double): Double * - returns `NaN` if any of arguments is `NaN` and the other is not infinite */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_hypot") +@GCUnsafeCall("Kotlin_math_hypot") external public actual fun hypot(x: Double, y: Double): Double /** @@ -183,7 +185,7 @@ external public actual fun hypot(x: Double, y: Double): Double * - `sqrt(x)` is `NaN` when `x < 0` or `x` is `NaN` */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_sqrt") +@GCUnsafeCall("Kotlin_math_sqrt") external public actual fun sqrt(x: Double): Double /** @@ -195,7 +197,7 @@ external public actual fun sqrt(x: Double): Double * - `exp(-Inf)` is `0.0` */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_exp") +@GCUnsafeCall("Kotlin_math_exp") external public actual fun exp(x: Double): Double /** @@ -211,7 +213,7 @@ external public actual fun exp(x: Double): Double * @see [exp] function. */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_expm1") +@GCUnsafeCall("Kotlin_math_expm1") external public actual fun expm1(x: Double): Double /** @@ -242,7 +244,7 @@ public actual fun log(x: Double, base: Double): Double { * - `ln(0.0)` is `-Inf` */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_ln") +@GCUnsafeCall("Kotlin_math_ln") external public actual fun ln(x: Double): Double /** @@ -251,7 +253,7 @@ external public actual fun ln(x: Double): Double * @see [ln] actual function for special cases. */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_log10") +@GCUnsafeCall("Kotlin_math_log10") external public actual fun log10(x: Double): Double /** @@ -260,7 +262,7 @@ external public actual fun log10(x: Double): Double * @see [ln] actual function for special cases. */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_log2") +@GCUnsafeCall("Kotlin_math_log2") external public actual fun log2(x: Double): Double /** @@ -278,7 +280,7 @@ external public actual fun log2(x: Double): Double * @see [expm1] function */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_ln1p") +@GCUnsafeCall("Kotlin_math_ln1p") external public actual fun ln1p(x: Double): Double /** @@ -290,7 +292,7 @@ external public actual fun ln1p(x: Double): Double * - `ceil(x)` is `x` where `x` is `NaN` or `+Inf` or `-Inf` or already a mathematical integer. */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_ceil") +@GCUnsafeCall("Kotlin_math_ceil") external public actual fun ceil(x: Double): Double /** @@ -302,7 +304,7 @@ external public actual fun ceil(x: Double): Double * - `floor(x)` is `x` where `x` is `NaN` or `+Inf` or `-Inf` or already a mathematical integer. */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_floor") +@GCUnsafeCall("Kotlin_math_floor") external public actual fun floor(x: Double): Double /** @@ -327,7 +329,7 @@ public actual fun truncate(x: Double): Double = when { * - `round(x)` is `x` where `x` is `NaN` or `+Inf` or `-Inf` or already a mathematical integer. */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_round") +@GCUnsafeCall("Kotlin_math_round") external public actual fun round(x: Double): Double /** @@ -339,7 +341,7 @@ external public actual fun round(x: Double): Double * @see absoluteValue extension property for [Double] */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_abs") +@GCUnsafeCall("Kotlin_math_abs") external public actual fun abs(x: Double): Double /** @@ -396,7 +398,7 @@ public actual fun max(a: Double, b: Double): Double = when { * - `b.pow(x)` is `NaN` for `b < 0` and `x` is finite and not an integer */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_Double_pow") +@GCUnsafeCall("Kotlin_math_Double_pow") external public actual fun Double.pow(x: Double): Double /** @@ -420,7 +422,7 @@ public actual fun Double.pow(n: Int): Double = pow(n.toDouble()) * @see round */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_Double_IEEErem") +@GCUnsafeCall("Kotlin_math_Double_IEEErem") external public fun Double.IEEErem(divisor: Double): Double /** @@ -454,7 +456,7 @@ public actual val Double.sign: Double * If [sign] is `NaN` the sign of the result is undefined. */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_Double_withSign") +@GCUnsafeCall("Kotlin_math_Double_withSign") external public actual fun Double.withSign(sign: Double): Double /** @@ -489,13 +491,13 @@ public actual val Double.ulp: Double * Returns the [Double] value nearest to this value in direction of positive infinity. */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_Double_nextUp") +@GCUnsafeCall("Kotlin_math_Double_nextUp") external public actual fun Double.nextUp(): Double /** * Returns the [Double] value nearest to this value in direction of negative infinity. */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_Double_nextDown") +@GCUnsafeCall("Kotlin_math_Double_nextDown") external public actual fun Double.nextDown(): Double /** @@ -507,13 +509,13 @@ external public actual fun Double.nextDown(): Double * */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_Double_nextTowards") +@GCUnsafeCall("Kotlin_math_Double_nextTowards") external public actual fun Double.nextTowards(to: Double): Double /** * Returns true if the sign of [this] value is negative and false otherwise */ -@SymbolName("Kotlin_math_Double_signBit") +@GCUnsafeCall("Kotlin_math_Double_signBit") external private fun Double.signBit(): Boolean /** @@ -562,7 +564,7 @@ public actual fun Double.roundToLong(): Long = when { * - `sin(NaN|+Inf|-Inf)` is `NaN` */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_sinf") +@GCUnsafeCall("Kotlin_math_sinf") external public actual fun sin(x: Float): Float /** Computes the cosine of the angle [x] given in radians. @@ -571,7 +573,7 @@ external public actual fun sin(x: Float): Float * - `cos(NaN|+Inf|-Inf)` is `NaN` */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_cosf") +@GCUnsafeCall("Kotlin_math_cosf") external public actual fun cos(x: Float): Float /** Computes the tangent of the angle [x] given in radians. @@ -580,7 +582,7 @@ external public actual fun cos(x: Float): Float * - `tan(NaN|+Inf|-Inf)` is `NaN` */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_tanf") +@GCUnsafeCall("Kotlin_math_tanf") external public actual fun tan(x: Float): Float /** @@ -591,7 +593,7 @@ external public actual fun tan(x: Float): Float * - `asin(x)` is `NaN`, when `abs(x) > 1` or x is `NaN` */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_asinf") +@GCUnsafeCall("Kotlin_math_asinf") external public actual fun asin(x: Float): Float /** @@ -602,7 +604,7 @@ external public actual fun asin(x: Float): Float * - `acos(x)` is `NaN`, when `abs(x) > 1` or x is `NaN` */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_acosf") +@GCUnsafeCall("Kotlin_math_acosf") external public actual fun acos(x: Float): Float /** @@ -613,7 +615,7 @@ external public actual fun acos(x: Float): Float * - `atan(NaN)` is `NaN` */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_atanf") +@GCUnsafeCall("Kotlin_math_atanf") external public actual fun atan(x: Float): Float /** @@ -633,7 +635,7 @@ external public actual fun atan(x: Float): Float * - `atan2(NaN, x)` and `atan2(y, NaN)` is `NaN` */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_atan2f") +@GCUnsafeCall("Kotlin_math_atan2f") external public actual fun atan2(y: Float, x: Float): Float /** @@ -645,7 +647,7 @@ external public actual fun atan2(y: Float, x: Float): Float * - `sinh(-Inf)` is `-Inf` */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_sinhf") +@GCUnsafeCall("Kotlin_math_sinhf") external public actual fun sinh(x: Float): Float /** @@ -656,7 +658,7 @@ external public actual fun sinh(x: Float): Float * - `cosh(+Inf|-Inf)` is `+Inf` */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_coshf") +@GCUnsafeCall("Kotlin_math_coshf") external public actual fun cosh(x: Float): Float /** @@ -668,7 +670,7 @@ external public actual fun cosh(x: Float): Float * - `tanh(-Inf)` is `-1.0` */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_tanhf") +@GCUnsafeCall("Kotlin_math_tanhf") external public actual fun tanh(x: Float): Float /** @@ -682,7 +684,7 @@ external public actual fun tanh(x: Float): Float * - `asinh(-Inf)` is `-Inf` */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_asinhf") +@GCUnsafeCall("Kotlin_math_asinhf") external public actual fun asinh(x: Float): Float /** @@ -696,7 +698,7 @@ external public actual fun asinh(x: Float): Float * - `acosh(+Inf)` is `+Inf` */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_acoshf") +@GCUnsafeCall("Kotlin_math_acoshf") external public actual fun acosh(x: Float): Float /** @@ -711,7 +713,7 @@ external public actual fun acosh(x: Float): Float * - `tanh(-1.0)` is `-Inf` */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_atanhf") +@GCUnsafeCall("Kotlin_math_atanhf") external public actual fun atanh(x: Float): Float /** @@ -722,7 +724,7 @@ external public actual fun atanh(x: Float): Float * - returns `NaN` if any of arguments is `NaN` and the other is not infinite */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_hypotf") +@GCUnsafeCall("Kotlin_math_hypotf") external public actual fun hypot(x: Float, y: Float): Float /** @@ -732,7 +734,7 @@ external public actual fun hypot(x: Float, y: Float): Float * - `sqrt(x)` is `NaN` when `x < 0` or `x` is `NaN` */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_sqrtf") +@GCUnsafeCall("Kotlin_math_sqrtf") external public actual fun sqrt(x: Float): Float /** @@ -744,7 +746,7 @@ external public actual fun sqrt(x: Float): Float * - `exp(-Inf)` is `0.0` */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_expf") +@GCUnsafeCall("Kotlin_math_expf") external public actual fun exp(x: Float): Float /** @@ -760,7 +762,7 @@ external public actual fun exp(x: Float): Float * @see [exp] function. */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_expm1f") +@GCUnsafeCall("Kotlin_math_expm1f") external public actual fun expm1(x: Float): Float /** @@ -791,7 +793,7 @@ public actual fun log(x: Float, base: Float): Float { * - `ln(0.0)` is `-Inf` */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_lnf") +@GCUnsafeCall("Kotlin_math_lnf") external public actual fun ln(x: Float): Float /** @@ -800,7 +802,7 @@ external public actual fun ln(x: Float): Float * @see [ln] actual function for special cases. */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_log10f") +@GCUnsafeCall("Kotlin_math_log10f") external public actual fun log10(x: Float): Float /** @@ -809,7 +811,7 @@ external public actual fun log10(x: Float): Float * @see [ln] actual function for special cases. */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_log2f") +@GCUnsafeCall("Kotlin_math_log2f") external public actual fun log2(x: Float): Float /** @@ -827,7 +829,7 @@ external public actual fun log2(x: Float): Float * @see [expm1] function */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_ln1pf") +@GCUnsafeCall("Kotlin_math_ln1pf") external public actual fun ln1p(x: Float): Float /** @@ -839,7 +841,7 @@ external public actual fun ln1p(x: Float): Float * - `ceil(x)` is `x` where `x` is `NaN` or `+Inf` or `-Inf` or already a mathematical integer. */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_ceilf") +@GCUnsafeCall("Kotlin_math_ceilf") external public actual fun ceil(x: Float): Float /** @@ -851,7 +853,7 @@ external public actual fun ceil(x: Float): Float * - `floor(x)` is `x` where `x` is `NaN` or `+Inf` or `-Inf` or already a mathematical integer. */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_floorf") +@GCUnsafeCall("Kotlin_math_floorf") external public actual fun floor(x: Float): Float /** @@ -876,7 +878,7 @@ public actual fun truncate(x: Float): Float = when { * - `round(x)` is `x` where `x` is `NaN` or `+Inf` or `-Inf` or already a mathematical integer. */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_roundf") +@GCUnsafeCall("Kotlin_math_roundf") external public actual fun round(x: Float): Float @@ -889,7 +891,7 @@ external public actual fun round(x: Float): Float * @see absoluteValue extension property for [Float] */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_absf") +@GCUnsafeCall("Kotlin_math_absf") external public actual fun abs(x: Float): Float /** @@ -946,7 +948,7 @@ public actual fun max(a: Float, b: Float): Float = when { * - `b.pow(x)` is `NaN` for `b < 0` and `x` is finite and not an integer */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_Float_pow") +@GCUnsafeCall("Kotlin_math_Float_pow") external public actual fun Float.pow(x: Float): Float /** @@ -970,7 +972,7 @@ public actual fun Float.pow(n: Int): Float = pow(n.toFloat()) * @see round */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_Float_IEEErem") +@GCUnsafeCall("Kotlin_math_Float_IEEErem") external public fun Float.IEEErem(divisor: Float): Float /** @@ -1004,7 +1006,7 @@ public actual val Float.sign: Float * If [sign] is `NaN` the sign of the result is undefined. */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_Float_withSign") +@GCUnsafeCall("Kotlin_math_Float_withSign") external public actual fun Float.withSign(sign: Float): Float /** * Returns this value with the sign bit same as of the [sign] value. @@ -1028,13 +1030,13 @@ public val Float.ulp: Float * Returns the [Float] value nearest to this value in direction of positive infinity. */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_Float_nextUp") +@GCUnsafeCall("Kotlin_math_Float_nextUp") external public fun Float.nextUp(): Float /** * Returns the [Float] value nearest to this value in direction of negative infinity. */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_Float_nextDown") +@GCUnsafeCall("Kotlin_math_Float_nextDown") external public fun Float.nextDown(): Float /** @@ -1046,13 +1048,13 @@ external public fun Float.nextDown(): Float * */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_Float_nextTowards") +@GCUnsafeCall("Kotlin_math_Float_nextTowards") external public fun Float.nextTowards(to: Float): Float /** * Returns true if the sign of [this] value is negative and false otherwise */ -@SymbolName("Kotlin_math_Float_signBit") +@GCUnsafeCall("Kotlin_math_Float_signBit") external private fun Float.signBit(): Boolean /** @@ -1104,7 +1106,7 @@ public actual fun Float.roundToLong(): Long = when { * @see absoluteValue extension property for [Int] */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_absi") +@GCUnsafeCall("Kotlin_math_absi") external public actual fun abs(n: Int): Int /** @@ -1155,7 +1157,7 @@ public actual val Int.sign: Int * @see absoluteValue extension property for [Long] */ @SinceKotlin("1.2") -@SymbolName("Kotlin_math_absl") +@GCUnsafeCall("Kotlin_math_absl") external public actual fun abs(n: Long): Long /** diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/native/Blob.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/native/Blob.kt index d62ff3a9efe..8adc4c4211a 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/native/Blob.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/native/Blob.kt @@ -16,10 +16,10 @@ public final class ImmutableBlob private constructor() { get() = getArrayLength() // Data layout is the same as for ByteArray, so we can share native functions. - @SymbolName("Kotlin_ByteArray_get") + @GCUnsafeCall("Kotlin_ByteArray_get") public external operator fun get(index: Int): Byte - @SymbolName("Kotlin_ByteArray_getArrayLength") + @GCUnsafeCall("Kotlin_ByteArray_getArrayLength") private external fun getArrayLength(): Int /** Creates an iterator over the elements of the array. */ @@ -47,7 +47,7 @@ private class ImmutableBlobIteratorImpl(val blob: ImmutableBlob) : ByteIterator( * @param startIndex the beginning (inclusive) of the subrange to copy, 0 by default. * @param endIndex the end (exclusive) of the subrange to copy, size of this blob by default. */ -@SymbolName("Kotlin_ImmutableBlob_toByteArray") +@GCUnsafeCall("Kotlin_ImmutableBlob_toByteArray") public external fun ImmutableBlob.toByteArray(startIndex: Int = 0, endIndex: Int = size): ByteArray /** @@ -57,7 +57,7 @@ public external fun ImmutableBlob.toByteArray(startIndex: Int = 0, endIndex: Int * @param endIndex the end (exclusive) of the subrange to copy, size of this blob by default. */ @ExperimentalUnsignedTypes -@SymbolName("Kotlin_ImmutableBlob_toByteArray") +@GCUnsafeCall("Kotlin_ImmutableBlob_toByteArray") public external fun ImmutableBlob.toUByteArray(startIndex: Int = 0, endIndex: Int = size): UByteArray /** @@ -71,7 +71,7 @@ public fun ImmutableBlob.asCPointer(offset: Int = 0): CPointer = public fun ImmutableBlob.asUCPointer(offset: Int = 0): CPointer = interpretCPointer(asCPointerImpl(offset))!! -@SymbolName("Kotlin_ImmutableBlob_asCPointerImpl") +@GCUnsafeCall("Kotlin_ImmutableBlob_asCPointerImpl") private external fun ImmutableBlob.asCPointerImpl(offset: Int): kotlin.native.internal.NativePtr /** diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/native/Platform.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/native/Platform.kt index 39da7b97961..5cf45f52e53 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/native/Platform.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/native/Platform.kt @@ -4,6 +4,8 @@ */ package kotlin.native +import kotlin.native.internal.GCUnsafeCall + /** * Operating system family. */ @@ -100,32 +102,32 @@ public object Platform { set(value) = Platform_setCleanersLeakChecker(value) } -@SymbolName("Konan_Platform_canAccessUnaligned") +@GCUnsafeCall("Konan_Platform_canAccessUnaligned") private external fun Platform_canAccessUnaligned(): Int -@SymbolName("Konan_Platform_isLittleEndian") +@GCUnsafeCall("Konan_Platform_isLittleEndian") private external fun Platform_isLittleEndian(): Int -@SymbolName("Konan_Platform_getOsFamily") +@GCUnsafeCall("Konan_Platform_getOsFamily") private external fun Platform_getOsFamily(): Int -@SymbolName("Konan_Platform_getCpuArchitecture") +@GCUnsafeCall("Konan_Platform_getCpuArchitecture") private external fun Platform_getCpuArchitecture(): Int -@SymbolName("Konan_Platform_getMemoryModel") +@GCUnsafeCall("Konan_Platform_getMemoryModel") private external fun Platform_getMemoryModel(): Int -@SymbolName("Konan_Platform_isDebugBinary") +@GCUnsafeCall("Konan_Platform_isDebugBinary") private external fun Platform_isDebugBinary(): Boolean -@SymbolName("Konan_Platform_getMemoryLeakChecker") +@GCUnsafeCall("Konan_Platform_getMemoryLeakChecker") private external fun Platform_getMemoryLeakChecker(): Boolean -@SymbolName("Konan_Platform_setMemoryLeakChecker") +@GCUnsafeCall("Konan_Platform_setMemoryLeakChecker") private external fun Platform_setMemoryLeakChecker(value: Boolean): Unit -@SymbolName("Konan_Platform_getCleanersLeakChecker") +@GCUnsafeCall("Konan_Platform_getCleanersLeakChecker") private external fun Platform_getCleanersLeakChecker(): Boolean -@SymbolName("Konan_Platform_setCleanersLeakChecker") +@GCUnsafeCall("Konan_Platform_setCleanersLeakChecker") private external fun Platform_setCleanersLeakChecker(value: Boolean): Unit diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/native/Runtime.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/native/Runtime.kt index 331a7ce90bd..7d0857e294a 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/native/Runtime.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/native/Runtime.kt @@ -5,19 +5,20 @@ package kotlin.native import kotlin.native.concurrent.InvalidMutabilityException +import kotlin.native.internal.GCUnsafeCall import kotlin.native.internal.UnhandledExceptionHookHolder /** * Initializes Kotlin runtime for the current thread, if not inited already. */ -@SymbolName("Kotlin_initRuntimeIfNeeded") +@GCUnsafeCall("Kotlin_initRuntimeIfNeeded") external public fun initRuntimeIfNeeded(): Unit /** * Deinitializes Kotlin runtime for the current thread, if was inited. * Cannot be called from Kotlin frames holding references, thus deprecated. */ -@SymbolName("Kotlin_deinitRuntimeIfNeeded") +@GCUnsafeCall("Kotlin_deinitRuntimeIfNeeded") @Deprecated("Deinit runtime can not be called from Kotlin", level = DeprecationLevel.ERROR) external public fun deinitRuntimeIfNeeded(): Unit @@ -55,5 +56,5 @@ public fun setUnhandledExceptionHook(hook: ReportUnhandledExceptionHook): Report * Compute stable wrt potential object relocations by the memory manager identity hash code. * @return 0 for `null` object, identity hash code otherwise. */ -@SymbolName("Kotlin_Any_hashCode") +@GCUnsafeCall("Kotlin_Any_hashCode") public external fun Any?.identityHashCode(): Int diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/native/TypedArrays.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/native/TypedArrays.kt index e8f51a49631..1404ec3ec22 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/native/TypedArrays.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/native/TypedArrays.kt @@ -4,7 +4,7 @@ */ package kotlin.native -import kotlin.native.SymbolName +import kotlin.native.internal.GCUnsafeCall /** * Those operations allows to extract primitive values out of the [ByteArray] byte buffers. @@ -23,21 +23,21 @@ public fun ByteArray.getUByteAt(index: Int): UByte = UByte(get(index)) * Gets [Char] out of the [ByteArray] byte buffer at specified index [index] * @throws ArrayIndexOutOfBoundsException if [index] is outside of array boundaries. */ -@SymbolName("Kotlin_ByteArray_getCharAt") +@GCUnsafeCall("Kotlin_ByteArray_getCharAt") public external fun ByteArray.getCharAt(index: Int): Char /** * Gets [Short] out of the [ByteArray] byte buffer at specified index [index] * @throws ArrayIndexOutOfBoundsException if [index] is outside of array boundaries. */ -@SymbolName("Kotlin_ByteArray_getShortAt") +@GCUnsafeCall("Kotlin_ByteArray_getShortAt") public external fun ByteArray.getShortAt(index: Int): Short /** * Gets [UShort] out of the [ByteArray] byte buffer at specified index [index] * @throws ArrayIndexOutOfBoundsException if [index] is outside of array boundaries. */ -@SymbolName("Kotlin_ByteArray_getShortAt") +@GCUnsafeCall("Kotlin_ByteArray_getShortAt") @ExperimentalUnsignedTypes public external fun ByteArray.getUShortAt(index: Int): UShort @@ -45,14 +45,14 @@ public external fun ByteArray.getUShortAt(index: Int): UShort * Gets [Int] out of the [ByteArray] byte buffer at specified index [index] * @throws ArrayIndexOutOfBoundsException if [index] is outside of array boundaries. */ -@SymbolName("Kotlin_ByteArray_getIntAt") +@GCUnsafeCall("Kotlin_ByteArray_getIntAt") public external fun ByteArray.getIntAt(index: Int): Int /** * Gets [UInt] out of the [ByteArray] byte buffer at specified index [index] * @throws ArrayIndexOutOfBoundsException if [index] is outside of array boundaries. */ -@SymbolName("Kotlin_ByteArray_getIntAt") +@GCUnsafeCall("Kotlin_ByteArray_getIntAt") @ExperimentalUnsignedTypes public external fun ByteArray.getUIntAt(index: Int): UInt @@ -60,14 +60,14 @@ public external fun ByteArray.getUIntAt(index: Int): UInt * Gets [Long] out of the [ByteArray] byte buffer at specified index [index] * @throws ArrayIndexOutOfBoundsException if [index] is outside of array boundaries. */ -@SymbolName("Kotlin_ByteArray_getLongAt") +@GCUnsafeCall("Kotlin_ByteArray_getLongAt") public external fun ByteArray.getLongAt(index: Int): Long /** * Gets [ULong] out of the [ByteArray] byte buffer at specified index [index] * @throws ArrayIndexOutOfBoundsException if [index] is outside of array boundaries. */ -@SymbolName("Kotlin_ByteArray_getLongAt") +@GCUnsafeCall("Kotlin_ByteArray_getLongAt") @ExperimentalUnsignedTypes public external fun ByteArray.getULongAt(index: Int): ULong @@ -75,42 +75,42 @@ public external fun ByteArray.getULongAt(index: Int): ULong * Gets [Float] out of the [ByteArray] byte buffer at specified index [index] * @throws ArrayIndexOutOfBoundsException if [index] is outside of array boundaries. */ -@SymbolName("Kotlin_ByteArray_getFloatAt") +@GCUnsafeCall("Kotlin_ByteArray_getFloatAt") public external fun ByteArray.getFloatAt(index: Int): Float /** * Gets [Double] out of the [ByteArray] byte buffer at specified index [index] * @throws ArrayIndexOutOfBoundsException if [index] is outside of array boundaries. */ -@SymbolName("Kotlin_ByteArray_getDoubleAt") +@GCUnsafeCall("Kotlin_ByteArray_getDoubleAt") public external fun ByteArray.getDoubleAt(index: Int): Double /** * Sets [UByte] out of the [ByteArray] byte buffer at specified index [index] * @throws ArrayIndexOutOfBoundsException if [index] is outside of array boundaries. */ -@SymbolName("Kotlin_ByteArray_set") +@GCUnsafeCall("Kotlin_ByteArray_set") public external fun ByteArray.setUByteAt(index: Int, value: UByte) /** * Sets [Char] out of the [ByteArray] byte buffer at specified index [index] * @throws ArrayIndexOutOfBoundsException if [index] is outside of array boundaries. */ -@SymbolName("Kotlin_ByteArray_setCharAt") +@GCUnsafeCall("Kotlin_ByteArray_setCharAt") public external fun ByteArray.setCharAt(index: Int, value: Char) /** * Sets [Short] out of the [ByteArray] byte buffer at specified index [index] * @throws ArrayIndexOutOfBoundsException if [index] is outside of array boundaries. */ -@SymbolName("Kotlin_ByteArray_setShortAt") +@GCUnsafeCall("Kotlin_ByteArray_setShortAt") public external fun ByteArray.setShortAt(index: Int, value: Short) /** * Sets [UShort] out of the [ByteArray] byte buffer at specified index [index] * @throws ArrayIndexOutOfBoundsException if [index] is outside of array boundaries. */ -@SymbolName("Kotlin_ByteArray_setShortAt") +@GCUnsafeCall("Kotlin_ByteArray_setShortAt") @ExperimentalUnsignedTypes public external fun ByteArray.setUShortAt(index: Int, value: UShort) @@ -118,28 +118,28 @@ public external fun ByteArray.setUShortAt(index: Int, value: UShort) * Sets [Int] out of the [ByteArray] byte buffer at specified index [index] * @throws ArrayIndexOutOfBoundsException if [index] is outside of array boundaries. */ -@SymbolName("Kotlin_ByteArray_setIntAt") +@GCUnsafeCall("Kotlin_ByteArray_setIntAt") public external fun ByteArray.setIntAt(index: Int, value: Int) /** * Sets [UInt] out of the [ByteArray] byte buffer at specified index [index] * @throws ArrayIndexOutOfBoundsException if [index] is outside of array boundaries. */ -@SymbolName("Kotlin_ByteArray_setIntAt") +@GCUnsafeCall("Kotlin_ByteArray_setIntAt") public external fun ByteArray.setUIntAt(index: Int, value: UInt) /** * Sets [Long] out of the [ByteArray] byte buffer at specified index [index] * @throws ArrayIndexOutOfBoundsException if [index] is outside of array boundaries. */ -@SymbolName("Kotlin_ByteArray_setLongAt") +@GCUnsafeCall("Kotlin_ByteArray_setLongAt") public external fun ByteArray.setLongAt(index: Int, value: Long) /** * Sets [ULong] out of the [ByteArray] byte buffer at specified index [index] * @throws ArrayIndexOutOfBoundsException if [index] is outside of array boundaries. */ -@SymbolName("Kotlin_ByteArray_setLongAt") +@GCUnsafeCall("Kotlin_ByteArray_setLongAt") @ExperimentalUnsignedTypes public external fun ByteArray.setULongAt(index: Int, value: ULong) @@ -147,12 +147,12 @@ public external fun ByteArray.setULongAt(index: Int, value: ULong) * Sets [Float] out of the [ByteArray] byte buffer at specified index [index] * @throws ArrayIndexOutOfBoundsException if [index] is outside of array boundaries. */ -@SymbolName("Kotlin_ByteArray_setFloatAt") +@GCUnsafeCall("Kotlin_ByteArray_setFloatAt") public external fun ByteArray.setFloatAt(index: Int, value: Float) /** * Sets [Double] out of the [ByteArray] byte buffer at specified index [index] * @throws ArrayIndexOutOfBoundsException if [index] is outside of array boundaries. */ -@SymbolName("Kotlin_ByteArray_setDoubleAt") +@GCUnsafeCall("Kotlin_ByteArray_setDoubleAt") public external fun ByteArray.setDoubleAt(index: Int, value: Double) diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/native/concurrent/Atomics.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/native/concurrent/Atomics.kt index 1b28d5cfc49..62605702847 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/native/concurrent/Atomics.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/native/concurrent/Atomics.kt @@ -5,12 +5,8 @@ package kotlin.native.concurrent -import kotlin.native.internal.ExportTypeInfo -import kotlin.native.internal.Frozen -import kotlin.native.internal.LeakDetectorCandidate -import kotlin.native.internal.NoReorderFields -import kotlin.native.SymbolName import kotlinx.cinterop.NativePtr +import kotlin.native.internal.* /** * Atomic values and freezing: atomics [AtomicInt], [AtomicLong], [AtomicNativePtr] and [AtomicReference] @@ -32,7 +28,7 @@ public class AtomicInt(private var value_: Int) { * @param delta the value to add * @return the new value */ - @SymbolName("Kotlin_AtomicInt_addAndGet") + @GCUnsafeCall("Kotlin_AtomicInt_addAndGet") external public fun addAndGet(delta: Int): Int /** @@ -42,7 +38,7 @@ public class AtomicInt(private var value_: Int) { * @param new the new value * @return the old value */ - @SymbolName("Kotlin_AtomicInt_compareAndSwap") + @GCUnsafeCall("Kotlin_AtomicInt_compareAndSwap") external public fun compareAndSwap(expected: Int, new: Int): Int /** @@ -52,7 +48,7 @@ public class AtomicInt(private var value_: Int) { * @param new the new value * @return true if successful */ - @SymbolName("Kotlin_AtomicInt_compareAndSet") + @GCUnsafeCall("Kotlin_AtomicInt_compareAndSet") external public fun compareAndSet(expected: Int, new: Int): Boolean /** @@ -77,10 +73,10 @@ public class AtomicInt(private var value_: Int) { public override fun toString(): String = value.toString() // Implementation details. - @SymbolName("Kotlin_AtomicInt_set") + @GCUnsafeCall("Kotlin_AtomicInt_set") private external fun setImpl(new: Int): Unit - @SymbolName("Kotlin_AtomicInt_get") + @GCUnsafeCall("Kotlin_AtomicInt_get") private external fun getImpl(): Int } @@ -99,7 +95,7 @@ public class AtomicLong(private var value_: Long = 0) { * @param delta the value to add * @return the new value */ - @SymbolName("Kotlin_AtomicLong_addAndGet") + @GCUnsafeCall("Kotlin_AtomicLong_addAndGet") external public fun addAndGet(delta: Long): Long /** @@ -117,7 +113,7 @@ public class AtomicLong(private var value_: Long = 0) { * @param new the new value * @return the old value */ - @SymbolName("Kotlin_AtomicLong_compareAndSwap") + @GCUnsafeCall("Kotlin_AtomicLong_compareAndSwap") external public fun compareAndSwap(expected: Long, new: Long): Long /** @@ -127,7 +123,7 @@ public class AtomicLong(private var value_: Long = 0) { * @param new the new value * @return true if successful, false if state is unchanged */ - @SymbolName("Kotlin_AtomicLong_compareAndSet") + @GCUnsafeCall("Kotlin_AtomicLong_compareAndSet") external public fun compareAndSet(expected: Long, new: Long): Boolean /** @@ -152,10 +148,10 @@ public class AtomicLong(private var value_: Long = 0) { public override fun toString(): String = value.toString() // Implementation details. - @SymbolName("Kotlin_AtomicLong_set") + @GCUnsafeCall("Kotlin_AtomicLong_set") private external fun setImpl(new: Long): Unit - @SymbolName("Kotlin_AtomicLong_get") + @GCUnsafeCall("Kotlin_AtomicLong_get") private external fun getImpl(): Long } @@ -177,7 +173,7 @@ public class AtomicNativePtr(private var value_: NativePtr) { * @throws InvalidMutabilityException if [new] is not frozen or a permanent object * @return the old value */ - @SymbolName("Kotlin_AtomicNativePtr_compareAndSwap") + @GCUnsafeCall("Kotlin_AtomicNativePtr_compareAndSwap") external public fun compareAndSwap(expected: NativePtr, new: NativePtr): NativePtr /** @@ -187,7 +183,7 @@ public class AtomicNativePtr(private var value_: NativePtr) { * @param new the new value * @return true if successful */ - @SymbolName("Kotlin_AtomicNativePtr_compareAndSet") + @GCUnsafeCall("Kotlin_AtomicNativePtr_compareAndSet") external public fun compareAndSet(expected: NativePtr, new: NativePtr): Boolean /** @@ -198,10 +194,10 @@ public class AtomicNativePtr(private var value_: NativePtr) { public override fun toString(): String = value.toString() // Implementation details. - @SymbolName("Kotlin_AtomicNativePtr_set") + @GCUnsafeCall("Kotlin_AtomicNativePtr_set") private external fun setImpl(new: NativePtr): Unit - @SymbolName("Kotlin_AtomicNativePtr_get") + @GCUnsafeCall("Kotlin_AtomicNativePtr_get") private external fun getImpl(): NativePtr } @@ -261,7 +257,7 @@ public class AtomicReference { * @throws InvalidMutabilityException if the value is not frozen or a permanent object * @return the old value */ - @SymbolName("Kotlin_AtomicReference_compareAndSwap") + @GCUnsafeCall("Kotlin_AtomicReference_compareAndSwap") external public fun compareAndSwap(expected: T, new: T): T /** @@ -272,7 +268,7 @@ public class AtomicReference { * @param new the new value * @return true if successful */ - @SymbolName("Kotlin_AtomicReference_compareAndSet") + @GCUnsafeCall("Kotlin_AtomicReference_compareAndSet") external public fun compareAndSet(expected: T, new: T): Boolean /** @@ -297,10 +293,10 @@ public class AtomicReference { } // Implementation details. - @SymbolName("Kotlin_AtomicReference_set") + @GCUnsafeCall("Kotlin_AtomicReference_set") private external fun setImpl(new: Any?): Unit - @SymbolName("Kotlin_AtomicReference_get") + @GCUnsafeCall("Kotlin_AtomicReference_get") private external fun getImpl(): Any? } @@ -381,15 +377,15 @@ public class FreezableAtomicReference(private var value_: T) { "${debugString(this)} -> ${debugString(value)}" // Implementation details. - @SymbolName("Kotlin_AtomicReference_set") + @GCUnsafeCall("Kotlin_AtomicReference_set") private external fun setImpl(new: Any?): Unit - @SymbolName("Kotlin_AtomicReference_get") + @GCUnsafeCall("Kotlin_AtomicReference_get") private external fun getImpl(): Any? - @SymbolName("Kotlin_AtomicReference_compareAndSwap") + @GCUnsafeCall("Kotlin_AtomicReference_compareAndSwap") private external fun compareAndSwapImpl(expected: Any?, new: Any?): Any? - @SymbolName("Kotlin_AtomicReference_compareAndSet") + @GCUnsafeCall("Kotlin_AtomicReference_compareAndSet") private external fun compareAndSetImpl(expected: Any?, new: Any?): Boolean } diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/native/concurrent/Freezing.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/native/concurrent/Freezing.kt index 4b3f326358f..669fe6a6be0 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/native/concurrent/Freezing.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/native/concurrent/Freezing.kt @@ -5,6 +5,8 @@ package kotlin.native.concurrent +import kotlin.native.internal.GCUnsafeCall + /** * Exception thrown whenever freezing is not possible. * @@ -49,5 +51,5 @@ public val Any?.isFrozen * @throws FreezingException thrown immediately if this object is already frozen * @see freeze */ -@SymbolName("Kotlin_Worker_ensureNeverFrozen") +@GCUnsafeCall("Kotlin_Worker_ensureNeverFrozen") public external fun Any.ensureNeverFrozen() \ No newline at end of file diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/native/concurrent/Internal.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/native/concurrent/Internal.kt index 0d13130f25b..58ee42f71fc 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/native/concurrent/Internal.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/native/concurrent/Internal.kt @@ -7,6 +7,7 @@ package kotlin.native.concurrent import kotlin.native.internal.DescribeObjectForDebugging import kotlin.native.internal.ExportForCppRuntime +import kotlin.native.internal.GCUnsafeCall import kotlin.native.internal.InternalForKotlinNative import kotlin.native.internal.debugDescription import kotlin.native.identityHashCode @@ -15,17 +16,17 @@ import kotlinx.cinterop.* // Implementation details. -@SymbolName("Kotlin_Worker_stateOfFuture") +@GCUnsafeCall("Kotlin_Worker_stateOfFuture") external internal fun stateOfFuture(id: Int): Int -@SymbolName("Kotlin_Worker_consumeFuture") +@GCUnsafeCall("Kotlin_Worker_consumeFuture") @PublishedApi external internal fun consumeFuture(id: Int): Any? -@SymbolName("Kotlin_Worker_waitForAnyFuture") +@GCUnsafeCall("Kotlin_Worker_waitForAnyFuture") external internal fun waitForAnyFuture(versionToken: Int, millis: Int): Boolean -@SymbolName("Kotlin_Worker_versionToken") +@GCUnsafeCall("Kotlin_Worker_versionToken") external internal fun versionToken(): Int @kotlin.native.internal.ExportForCompiler @@ -33,29 +34,29 @@ internal fun executeImpl(worker: Worker, mode: TransferMode, producer: () -> Any job: CPointer>): Future = Future(executeInternal(worker.id, mode.value, producer, job)) -@SymbolName("Kotlin_Worker_startInternal") +@GCUnsafeCall("Kotlin_Worker_startInternal") external internal fun startInternal(errorReporting: Boolean, name: String?): Int -@SymbolName("Kotlin_Worker_currentInternal") +@GCUnsafeCall("Kotlin_Worker_currentInternal") external internal fun currentInternal(): Int -@SymbolName("Kotlin_Worker_requestTerminationWorkerInternal") +@GCUnsafeCall("Kotlin_Worker_requestTerminationWorkerInternal") external internal fun requestTerminationInternal(id: Int, processScheduledJobs: Boolean): Int -@SymbolName("Kotlin_Worker_executeInternal") +@GCUnsafeCall("Kotlin_Worker_executeInternal") external internal fun executeInternal( id: Int, mode: Int, producer: () -> Any?, job: CPointer>): Int -@SymbolName("Kotlin_Worker_executeAfterInternal") +@GCUnsafeCall("Kotlin_Worker_executeAfterInternal") external internal fun executeAfterInternal(id: Int, operation: () -> Unit, afterMicroseconds: Long): Unit -@SymbolName("Kotlin_Worker_processQueueInternal") +@GCUnsafeCall("Kotlin_Worker_processQueueInternal") external internal fun processQueueInternal(id: Int): Boolean -@SymbolName("Kotlin_Worker_parkInternal") +@GCUnsafeCall("Kotlin_Worker_parkInternal") external internal fun parkInternal(id: Int, timeoutMicroseconds: Long, process: Boolean): Boolean -@SymbolName("Kotlin_Worker_getNameInternal") +@GCUnsafeCall("Kotlin_Worker_getNameInternal") external internal fun getWorkerNameInternal(id: Int): String? @ExportForCppRuntime @@ -70,17 +71,17 @@ internal fun ThrowWorkerInvalidState(): Unit = internal fun WorkerLaunchpad(function: () -> Any?) = function() @PublishedApi -@SymbolName("Kotlin_Worker_detachObjectGraphInternal") +@GCUnsafeCall("Kotlin_Worker_detachObjectGraphInternal") external internal fun detachObjectGraphInternal(mode: Int, producer: () -> Any?): NativePtr @PublishedApi -@SymbolName("Kotlin_Worker_attachObjectGraphInternal") +@GCUnsafeCall("Kotlin_Worker_attachObjectGraphInternal") external internal fun attachObjectGraphInternal(stable: NativePtr): Any? -@SymbolName("Kotlin_Worker_freezeInternal") +@GCUnsafeCall("Kotlin_Worker_freezeInternal") internal external fun freezeInternal(it: Any?) -@SymbolName("Kotlin_Worker_isFrozenInternal") +@GCUnsafeCall("Kotlin_Worker_isFrozenInternal") internal external fun isFrozenInternal(it: Any?): Boolean @ExportForCppRuntime @@ -99,9 +100,9 @@ internal fun ThrowIllegalObjectSharingException(typeInfo: NativePtr, address: Na throw IncorrectDereferenceException("illegal attempt to access non-shared $description from other thread") } -@SymbolName("Kotlin_AtomicReference_checkIfFrozen") +@GCUnsafeCall("Kotlin_AtomicReference_checkIfFrozen") external internal fun checkIfFrozen(ref: Any?) @InternalForKotlinNative -@SymbolName("Kotlin_Worker_waitTermination") +@GCUnsafeCall("Kotlin_Worker_waitTermination") external public fun waitWorkerTermination(worker: Worker) diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/native/concurrent/MutableData.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/native/concurrent/MutableData.kt index e097f883df3..7ae4b3f3c95 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/native/concurrent/MutableData.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/native/concurrent/MutableData.kt @@ -8,13 +8,13 @@ package kotlin.native.concurrent import kotlin.native.internal.* import kotlinx.cinterop.* -@SymbolName("Kotlin_Any_share") +@GCUnsafeCall("Kotlin_Any_share") external private fun Any.share() -@SymbolName("Kotlin_CPointer_CopyMemory") +@GCUnsafeCall("Kotlin_CPointer_CopyMemory") external private fun CopyMemory(to: COpaquePointer?, from: COpaquePointer?, count: Int) -@SymbolName("ReadHeapRefNoLock") +@GCUnsafeCall("ReadHeapRefNoLock") internal external fun readHeapRefNoLock(where: Any, index: Int): Any? /** diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/native/concurrent/WorkerBoundReference.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/native/concurrent/WorkerBoundReference.kt index c1d5ef010d6..c89b25ec11c 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/native/concurrent/WorkerBoundReference.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/native/concurrent/WorkerBoundReference.kt @@ -7,13 +7,13 @@ package kotlin.native.concurrent import kotlin.native.internal.* -@SymbolName("Kotlin_WorkerBoundReference_create") +@GCUnsafeCall("Kotlin_WorkerBoundReference_create") external private fun createWorkerBoundReference(value: Any): NativePtr -@SymbolName("Kotlin_WorkerBoundReference_deref") +@GCUnsafeCall("Kotlin_WorkerBoundReference_deref") external private fun derefWorkerBoundReference(ref: NativePtr): Any? -@SymbolName("Kotlin_WorkerBoundReference_describe") +@GCUnsafeCall("Kotlin_WorkerBoundReference_describe") external private fun describeWorkerBoundReference(ref: NativePtr): String /** diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/Annotations.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/Annotations.kt index bb18ed0132a..4e236b8993b 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/Annotations.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/Annotations.kt @@ -150,4 +150,17 @@ internal annotation class InternalForKotlinNative * Marks a class that has a freeze hook. */ @Target(AnnotationTarget.CLASS) -internal annotation class HasFreezeHook \ No newline at end of file +internal annotation class HasFreezeHook + +/** + * Indicates that calls of this function will be replaced with calls to the + * [callee] implemented in the C++ part of the runtime. + * + * This annotation is unsafe and should be used with care: [callee] is + * responsible for correct interaction with the garbage collector, like + * placing safe points and switching thread state when using blocking APIs. + */ +@Target(AnnotationTarget.FUNCTION) +@Retention(value = AnnotationRetention.BINARY) +@InternalForKotlinNative +annotation class GCUnsafeCall(val callee: String) diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/Boxing.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/Boxing.kt index b9adfca5c55..3887f35a7e5 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/Boxing.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/Boxing.kt @@ -5,29 +5,40 @@ package kotlin.native.internal -@SymbolName("getCachedBooleanBox") +@GCUnsafeCall("getCachedBooleanBox") external fun getCachedBooleanBox(value: Boolean): Boolean? -@SymbolName("inBooleanBoxCache") + +@GCUnsafeCall("inBooleanBoxCache") external fun inBooleanBoxCache(value: Boolean): Boolean -@SymbolName("getCachedByteBox") + +@GCUnsafeCall("getCachedByteBox") external fun getCachedByteBox(value: Byte): Byte? -@SymbolName("inByteBoxCache") + +@GCUnsafeCall("inByteBoxCache") external fun inByteBoxCache(value: Byte): Boolean -@SymbolName("getCachedCharBox") + +@GCUnsafeCall("getCachedCharBox") external fun getCachedCharBox(value: Char): Char? -@SymbolName("inCharBoxCache") + +@GCUnsafeCall("inCharBoxCache") external fun inCharBoxCache(value: Char): Boolean -@SymbolName("getCachedShortBox") + +@GCUnsafeCall("getCachedShortBox") external fun getCachedShortBox(value: Short): Short? -@SymbolName("inShortBoxCache") + +@GCUnsafeCall("inShortBoxCache") external fun inShortBoxCache(value: Short): Boolean -@SymbolName("getCachedIntBox") + +@GCUnsafeCall("getCachedIntBox") external fun getCachedIntBox(idx: Int): Int? -@SymbolName("inIntBoxCache") + +@GCUnsafeCall("inIntBoxCache") external fun inIntBoxCache(value: Int): Boolean -@SymbolName("getCachedLongBox") + +@GCUnsafeCall("getCachedLongBox") external fun getCachedLongBox(value: Long): Long? -@SymbolName("inLongBoxCache") + +@GCUnsafeCall("inLongBoxCache") external fun inLongBoxCache(value: Long): Boolean // TODO: functions below are used for ObjCExport, move and rename them correspondigly. diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/Cleaner.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/Cleaner.kt index 382dd03d572..fbeed01a169 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/Cleaner.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/Cleaner.kt @@ -105,7 +105,7 @@ fun waitCleanerWorker() = Unit }.result -@SymbolName("Kotlin_CleanerImpl_getCleanerWorker") +@GCUnsafeCall("Kotlin_CleanerImpl_getCleanerWorker") external private fun getCleanerWorker(): Worker @ExportForCppRuntime("Kotlin_CleanerImpl_shutdownCleanerWorker") @@ -125,8 +125,8 @@ private class CleanerImpl( private val cleanPtr: NativePtr, ): Cleaner {} -@SymbolName("Kotlin_Any_isShareable") +@GCUnsafeCall("Kotlin_Any_isShareable") external private fun Any?.isShareable(): Boolean -@SymbolName("CreateStablePointer") +@GCUnsafeCall("CreateStablePointer") external private fun createStablePointer(obj: Any): NativePtr diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/Debugging.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/Debugging.kt index 638e0363df9..32886feb19b 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/Debugging.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/Debugging.kt @@ -11,10 +11,16 @@ public object Debugging { public var forceCheckedShutdown: Boolean get() = Debugging_getForceCheckedShutdown() set(value) = Debugging_setForceCheckedShutdown(value) + + public val isThreadStateRunnable: Boolean + get() = Debugging_isThreadStateRunnable() } -@SymbolName("Kotlin_Debugging_getForceCheckedShutdown") +@GCUnsafeCall("Kotlin_Debugging_getForceCheckedShutdown") private external fun Debugging_getForceCheckedShutdown(): Boolean -@SymbolName("Kotlin_Debugging_setForceCheckedShutdown") +@GCUnsafeCall("Kotlin_Debugging_setForceCheckedShutdown") private external fun Debugging_setForceCheckedShutdown(value: Boolean): Unit + +@GCUnsafeCall("Kotlin_Debugging_isThreadStateRunnable") +private external fun Debugging_isThreadStateRunnable(): Boolean diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/FloatingPointParser.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/FloatingPointParser.kt index cee7cd9e9f6..2ec8cee19b7 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/FloatingPointParser.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/FloatingPointParser.kt @@ -31,7 +31,7 @@ import kotlin.comparisons.* * @return the double closest to the real number * @exception NumberFormatException if the String doesn't represent a positive integer value */ -@SymbolName("Kotlin_native_FloatingPointParser_parseDoubleImpl") +@GCUnsafeCall("Kotlin_native_FloatingPointParser_parseDoubleImpl") private external fun parseDoubleImpl(s: String, e: Int): Double /** @@ -46,7 +46,7 @@ private external fun parseDoubleImpl(s: String, e: Int): Double * @return the float closest to the real number * @exception NumberFormatException if the String doesn't represent a positive integer value */ -@SymbolName("Kotlin_native_FloatingPointParser_parseFloatImpl") +@GCUnsafeCall("Kotlin_native_FloatingPointParser_parseFloatImpl") private external fun parseFloatImpl(s: String, e: Int): Float /** diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/GC.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/GC.kt index fd52d6ceeee..00920609b93 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/GC.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/GC.kt @@ -26,39 +26,39 @@ object GC { * To force garbage collection immediately, unless collector is stopped * with [stop] operation. Even if GC is suspended, [collect] still triggers collection. */ - @SymbolName("Kotlin_native_internal_GC_collect") + @GCUnsafeCall("Kotlin_native_internal_GC_collect") external fun collect() /** * Request global cyclic collector, operation is async and just triggers the collection. */ - @SymbolName("Kotlin_native_internal_GC_collectCyclic") + @GCUnsafeCall("Kotlin_native_internal_GC_collectCyclic") external fun collectCyclic() /** * Suspend garbage collection. Release candidates are still collected, but * GC algorithm is not executed. */ - @SymbolName("Kotlin_native_internal_GC_suspend") + @GCUnsafeCall("Kotlin_native_internal_GC_suspend") external fun suspend() /** * Resume garbage collection. Can potentially lead to GC immediately. */ - @SymbolName("Kotlin_native_internal_GC_resume") + @GCUnsafeCall("Kotlin_native_internal_GC_resume") external fun resume() /** * Stop garbage collection. Cyclical garbage is no longer collected. */ - @SymbolName("Kotlin_native_internal_GC_stop") + @GCUnsafeCall("Kotlin_native_internal_GC_stop") external fun stop() /** * Start garbage collection. Cyclical garbage produced while GC was stopped * cannot be reclaimed, but all new garbage is collected. */ - @SymbolName("Kotlin_native_internal_GC_start") + @GCUnsafeCall("Kotlin_native_internal_GC_start") external fun start() /** @@ -105,42 +105,42 @@ object GC { * or `null` if the leak detector is not available. Use [Platform.isMemoryLeakCheckerActive] to check * leak detector availability. */ - @SymbolName("Kotlin_native_internal_GC_detectCycles") + @GCUnsafeCall("Kotlin_native_internal_GC_detectCycles") external fun detectCycles(): Array? /** * Find a reference cycle including from the given object, `null` if no cycles detected. */ - @SymbolName("Kotlin_native_internal_GC_findCycle") + @GCUnsafeCall("Kotlin_native_internal_GC_findCycle") external fun findCycle(root: Any): Array? - @SymbolName("Kotlin_native_internal_GC_getThreshold") + @GCUnsafeCall("Kotlin_native_internal_GC_getThreshold") private external fun getThreshold(): Int - @SymbolName("Kotlin_native_internal_GC_setThreshold") + @GCUnsafeCall("Kotlin_native_internal_GC_setThreshold") private external fun setThreshold(value: Int) - @SymbolName("Kotlin_native_internal_GC_getCollectCyclesThreshold") + @GCUnsafeCall("Kotlin_native_internal_GC_getCollectCyclesThreshold") private external fun getCollectCyclesThreshold(): Long - @SymbolName("Kotlin_native_internal_GC_setCollectCyclesThreshold") + @GCUnsafeCall("Kotlin_native_internal_GC_setCollectCyclesThreshold") private external fun setCollectCyclesThreshold(value: Long) - @SymbolName("Kotlin_native_internal_GC_getThresholdAllocations") + @GCUnsafeCall("Kotlin_native_internal_GC_getThresholdAllocations") private external fun getThresholdAllocations(): Long - @SymbolName("Kotlin_native_internal_GC_setThresholdAllocations") + @GCUnsafeCall("Kotlin_native_internal_GC_setThresholdAllocations") private external fun setThresholdAllocations(value: Long) - @SymbolName("Kotlin_native_internal_GC_getTuneThreshold") + @GCUnsafeCall("Kotlin_native_internal_GC_getTuneThreshold") private external fun getTuneThreshold(): Boolean - @SymbolName("Kotlin_native_internal_GC_setTuneThreshold") + @GCUnsafeCall("Kotlin_native_internal_GC_setTuneThreshold") private external fun setTuneThreshold(value: Boolean) - @SymbolName("Kotlin_native_internal_GC_getCyclicCollector") + @GCUnsafeCall("Kotlin_native_internal_GC_getCyclicCollector") private external fun getCyclicCollectorEnabled(): Boolean - @SymbolName("Kotlin_native_internal_GC_setCyclicCollector") + @GCUnsafeCall("Kotlin_native_internal_GC_setCyclicCollector") private external fun setCyclicCollectorEnabled(value: Boolean) } diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/HexStringParser.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/HexStringParser.kt index db106c7913d..87e6336b063 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/HexStringParser.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/HexStringParser.kt @@ -19,10 +19,10 @@ package kotlin.native.internal import kotlin.text.regex.* -@SymbolName("Kotlin_native_int_bits_to_float") +@GCUnsafeCall("Kotlin_native_int_bits_to_float") private external fun intBitsToFloat(x: Int): Float -@SymbolName("Kotlin_native_long_bits_to_double") +@GCUnsafeCall("Kotlin_native_long_bits_to_double") private external fun longBitsToDouble(x: Long): Double /* diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/KClassImpl.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/KClassImpl.kt index a145346d09b..06caf7180df 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/KClassImpl.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/KClassImpl.kt @@ -68,22 +68,22 @@ internal class KClassUnsupportedImpl(private val message: String) : KClass override fun toString(): String = "unreflected class ($message)" } -@SymbolName("Kotlin_TypeInfo_findAssociatedObject") +@GCUnsafeCall("Kotlin_TypeInfo_findAssociatedObject") private external fun findAssociatedObjectImpl(typeInfo: NativePtr, key: NativePtr): Any? @ExportForCompiler -@SymbolName("Kotlin_Any_getTypeInfo") +@GCUnsafeCall("Kotlin_Any_getTypeInfo") internal external fun getObjectTypeInfo(obj: Any): NativePtr @ExportForCompiler @TypedIntrinsic(IntrinsicType.GET_CLASS_TYPE_INFO) internal external inline fun getClassTypeInfo(): NativePtr -@SymbolName("Kotlin_TypeInfo_getPackageName") +@GCUnsafeCall("Kotlin_TypeInfo_getPackageName") private external fun getPackageName(typeInfo: NativePtr): String? -@SymbolName("Kotlin_TypeInfo_getRelativeName") +@GCUnsafeCall("Kotlin_TypeInfo_getRelativeName") private external fun getRelativeName(typeInfo: NativePtr): String? -@SymbolName("Kotlin_TypeInfo_isInstance") +@GCUnsafeCall("Kotlin_TypeInfo_isInstance") private external fun isInstance(obj: Any, typeInfo: NativePtr): Boolean diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/NativePtr.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/NativePtr.kt index 64612ed3cd4..3932410d131 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/NativePtr.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/NativePtr.kt @@ -47,15 +47,15 @@ internal class NonNullNativePtr private constructor() { // TODO: refactor to use @ExportTypeInfo("theNativePtrArrayTypeInfo") internal class NativePtrArray { - @SymbolName("Kotlin_NativePtrArray_get") + @GCUnsafeCall("Kotlin_NativePtrArray_get") external public operator fun get(index: Int): NativePtr - @SymbolName("Kotlin_NativePtrArray_set") + @GCUnsafeCall("Kotlin_NativePtrArray_set") external public operator fun set(index: Int, value: NativePtr): Unit val size: Int get() = getArrayLength() - @SymbolName("Kotlin_NativePtrArray_getArrayLength") + @GCUnsafeCall("Kotlin_NativePtrArray_getArrayLength") external private fun getArrayLength(): Int } diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/NumberConverter.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/NumberConverter.kt index cd53a538095..555053d8be2 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/NumberConverter.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/NumberConverter.kt @@ -17,11 +17,11 @@ package kotlin.native.internal -@SymbolName("Kotlin_native_NumberConverter_bigIntDigitGeneratorInstImpl") +@GCUnsafeCall("Kotlin_native_NumberConverter_bigIntDigitGeneratorInstImpl") private external fun bigIntDigitGeneratorInstImpl(results: IntArray, uArray: IntArray, f: Long, e: Int, isDenormalized: Boolean, mantissaIsZero: Boolean, p: Int) -@SymbolName("Kotlin_native_NumberConverter_ceil") +@GCUnsafeCall("Kotlin_native_NumberConverter_ceil") private external fun ceil(x: Double): Double /** diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/RuntimeUtils.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/RuntimeUtils.kt index 3437fda6b8e..9852874cef7 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/RuntimeUtils.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/RuntimeUtils.kt @@ -116,7 +116,7 @@ internal fun ReportUnhandledException(throwable: Throwable) { throwable.printStackTrace() } -@SymbolName("TerminateWithUnhandledException") +@GCUnsafeCall("TerminateWithUnhandledException") internal external fun TerminateWithUnhandledException(throwable: Throwable) // Using object to make sure that `hook` is initialized when it's needed instead of diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/Undefined.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/Undefined.kt index 2a3723cbddf..b379c63cad9 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/Undefined.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/Undefined.kt @@ -9,5 +9,5 @@ package kotlin.native.internal * Returns undefined value of type `T`. * This method is unsafe and should be used with care. */ -@SymbolName("Kotlin_native_internal_undefined") +@GCUnsafeCall("Kotlin_native_internal_undefined") internal external fun undefined(): T \ No newline at end of file diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/native/ref/WeakPrivate.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/native/ref/WeakPrivate.kt index b5c3fb32319..86d1700c86a 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/native/ref/WeakPrivate.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/native/ref/WeakPrivate.kt @@ -8,6 +8,7 @@ package kotlin.native.ref import kotlinx.cinterop.COpaquePointer import kotlin.native.internal.ExportForCppRuntime import kotlin.native.internal.Frozen +import kotlin.native.internal.GCUnsafeCall import kotlin.native.internal.NoReorderFields import kotlin.native.internal.Escapes @@ -43,7 +44,7 @@ internal class WeakReferenceCounter(var referred: COpaquePointer?) : WeakReferen // Optimization for concurrent access. var cookie: Int = 0 - @SymbolName("Konan_WeakReferenceCounter_get") + @GCUnsafeCall("Konan_WeakReferenceCounter_get") external override fun get(): Any? } @@ -53,7 +54,7 @@ internal abstract class WeakReferenceImpl { } // Get a counter from non-null object. -@SymbolName("Konan_getWeakReferenceImpl") +@GCUnsafeCall("Konan_getWeakReferenceImpl") @Escapes(0b01) // referent escapes. external internal fun getWeakReferenceImpl(referent: Any): WeakReferenceImpl diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/native/simd.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/native/simd.kt index 85d4e9de5ec..95dc47202a4 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/native/simd.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/native/simd.kt @@ -4,6 +4,7 @@ */ package kotlin.native +import kotlin.native.internal.GCUnsafeCall import kotlin.native.internal.TypedIntrinsic import kotlin.native.internal.IntrinsicType @@ -50,8 +51,8 @@ public final class Vector128 private constructor() { } } -@SymbolName("Kotlin_Vector4f_of") +@GCUnsafeCall("Kotlin_Vector4f_of") external fun vectorOf(f0: Float, f1: Float, f2: Float, f3: Float): Vector128 -@SymbolName("Kotlin_Vector4i32_of") +@GCUnsafeCall("Kotlin_Vector4i32_of") external fun vectorOf(f0: Int, f1: Int, f2: Int, f3: Int): Vector128 diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/system/Process.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/system/Process.kt index c4b892fc172..8078acc0975 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/system/Process.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/system/Process.kt @@ -5,6 +5,8 @@ package kotlin.system +import kotlin.native.internal.GCUnsafeCall + /** * Terminates the currently running process. * @@ -13,5 +15,5 @@ package kotlin.system * * @return This method never returns normally. */ -@SymbolName("Kotlin_system_exitProcess") +@GCUnsafeCall("Kotlin_system_exitProcess") public external fun exitProcess(status: Int): Nothing \ No newline at end of file diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/system/Timing.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/system/Timing.kt index 9e922c90c02..f25bc20abaa 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/system/Timing.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/system/Timing.kt @@ -5,25 +5,27 @@ package kotlin.system +import kotlin.native.internal.GCUnsafeCall + /** * Gets current system time in milliseconds since certain moment in the past, * only delta between two subsequent calls makes sense. */ -@SymbolName("Kotlin_system_getTimeMillis") +@GCUnsafeCall("Kotlin_system_getTimeMillis") public external fun getTimeMillis() : Long /** * Gets current system time in nanoseconds since certain moment in the past, * only delta between two subsequent calls makes sense. */ -@SymbolName("Kotlin_system_getTimeNanos") +@GCUnsafeCall("Kotlin_system_getTimeNanos") public external fun getTimeNanos() : Long /** * Gets current system time in microseconds since certain moment in the past, * only delta between two subsequent calls makes sense. */ -@SymbolName("Kotlin_system_getTimeMicros") +@GCUnsafeCall("Kotlin_system_getTimeMicros") public external fun getTimeMicros() : Long /** diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/text/Char.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/text/Char.kt index 1431c720294..70f6fc3c4dc 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/text/Char.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/text/Char.kt @@ -6,6 +6,7 @@ package kotlin.text import kotlin.IllegalArgumentException +import kotlin.native.internal.GCUnsafeCall /** * Returns `true` if this character (Unicode code point) is defined in Unicode. @@ -77,7 +78,7 @@ public actual fun Char.isDigit(): Boolean { * Returns `true` if this character (Unicode code point) should be regarded as an ignorable * character in a Java identifier or a Unicode identifier. */ -@SymbolName("Kotlin_Char_isIdentifierIgnorable") +@GCUnsafeCall("Kotlin_Char_isIdentifierIgnorable") external public fun Char.isIdentifierIgnorable(): Boolean /** @@ -88,7 +89,7 @@ external public fun Char.isIdentifierIgnorable(): Boolean * * @sample samples.text.Chars.isISOControl */ -@SymbolName("Kotlin_Char_isISOControl") +@GCUnsafeCall("Kotlin_Char_isISOControl") external public actual fun Char.isISOControl(): Boolean /** @@ -231,19 +232,19 @@ public actual fun Char.titlecaseChar(): Char = titlecaseCharImpl() /** * Returns `true` if this character is a Unicode high-surrogate code unit (also known as leading-surrogate code unit). */ -@SymbolName("Kotlin_Char_isHighSurrogate") +@GCUnsafeCall("Kotlin_Char_isHighSurrogate") external public actual fun Char.isHighSurrogate(): Boolean /** * Returns `true` if this character is a Unicode low-surrogate code unit (also known as trailing-surrogate code unit). */ -@SymbolName("Kotlin_Char_isLowSurrogate") +@GCUnsafeCall("Kotlin_Char_isLowSurrogate") external public actual fun Char.isLowSurrogate(): Boolean internal actual fun digitOf(char: Char, radix: Int): Int = digitOfChecked(char, checkRadix(radix)) -@SymbolName("Kotlin_Char_digitOfChecked") +@GCUnsafeCall("Kotlin_Char_digitOfChecked") external internal fun digitOfChecked(char: Char, radix: Int): Int /** diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/text/StringBuilder.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/text/StringBuilder.kt index 9bf4efbe9af..656e266c150 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/text/StringBuilder.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/text/StringBuilder.kt @@ -5,6 +5,8 @@ package kotlin.text +import kotlin.native.internal.GCUnsafeCall + /** * A mutable sequence of characters. * @@ -803,10 +805,10 @@ public actual inline fun StringBuilder.insertRange(index: Int, value: CharSequen internal fun insertString(array: CharArray, start: Int, value: String): Int = insertString(array, start, value, 0, value.length) -@SymbolName("Kotlin_StringBuilder_insertString") +@GCUnsafeCall("Kotlin_StringBuilder_insertString") internal external fun insertString(array: CharArray, distIndex: Int, value: String, sourceIndex: Int, count: Int): Int -@SymbolName("Kotlin_StringBuilder_insertInt") +@GCUnsafeCall("Kotlin_StringBuilder_insertInt") internal external fun insertInt(array: CharArray, start: Int, value: Int): Int diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/text/StringNumberConversions.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/text/StringNumberConversions.kt index 67c78822f61..86dcee5d222 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/text/StringNumberConversions.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/text/StringNumberConversions.kt @@ -6,6 +6,7 @@ package kotlin.text import kotlin.native.internal.FloatingPointParser +import kotlin.native.internal.GCUnsafeCall /** * Returns a string representation of this [Byte] value in the specified [radix]. @@ -25,7 +26,7 @@ public actual inline fun Byte.toString(radix: Int): String = this.toInt().toStri @kotlin.internal.InlineOnly public actual inline fun Short.toString(radix: Int): String = this.toInt().toString(checkRadix(radix)) -@SymbolName("Kotlin_Int_toStringRadix") +@GCUnsafeCall("Kotlin_Int_toStringRadix") @PublishedApi external internal fun intToString(value: Int, radix: Int): String @@ -38,7 +39,7 @@ external internal fun intToString(value: Int, radix: Int): String @kotlin.internal.InlineOnly public actual inline fun Int.toString(radix: Int): String = intToString(this, checkRadix(radix)) -@SymbolName("Kotlin_Long_toStringRadix") +@GCUnsafeCall("Kotlin_Long_toStringRadix") @PublishedApi external internal fun longToString(value: Long, radix: Int): String diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/text/Strings.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/text/Strings.kt index 310e062dc73..a32e67cf7d2 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/text/Strings.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/text/Strings.kt @@ -6,29 +6,30 @@ package kotlin.text import kotlin.native.concurrent.SharedImmutable +import kotlin.native.internal.GCUnsafeCall /** * Returns the index within this string of the first occurrence of the specified character, starting from the specified offset. */ -@SymbolName("Kotlin_String_indexOfChar") +@GCUnsafeCall("Kotlin_String_indexOfChar") internal actual external fun String.nativeIndexOf(ch: Char, fromIndex: Int): Int /** * Returns the index within this string of the first occurrence of the specified substring, starting from the specified offset. */ -@SymbolName("Kotlin_String_indexOfString") +@GCUnsafeCall("Kotlin_String_indexOfString") internal actual external fun String.nativeIndexOf(str: String, fromIndex: Int): Int /** * Returns the index within this string of the last occurrence of the specified character. */ -@SymbolName("Kotlin_String_lastIndexOfChar") +@GCUnsafeCall("Kotlin_String_lastIndexOfChar") internal actual external fun String.nativeLastIndexOf(ch: Char, fromIndex: Int): Int /** * Returns the index within this string of the last occurrence of the specified character, starting from the specified offset. */ -@SymbolName("Kotlin_String_lastIndexOfString") +@GCUnsafeCall("Kotlin_String_lastIndexOfString") internal actual external fun String.nativeLastIndexOf(str: String, fromIndex: Int): Int /** @@ -61,7 +62,7 @@ public actual fun String.replace(oldChar: Char, newChar: Char, ignoreCase: Boole replaceIgnoreCase(oldChar, newChar) } -@SymbolName("Kotlin_String_replace") +@GCUnsafeCall("Kotlin_String_replace") private external fun String.replace(oldChar: Char, newChar: Char): String private fun String.replaceIgnoreCase(oldChar: Char, newChar: Char): String { @@ -187,7 +188,7 @@ public fun String.regionMatches( } // Bounds must be checked before calling this method -@SymbolName("Kotlin_String_unsafeRangeEquals") +@GCUnsafeCall("Kotlin_String_unsafeRangeEquals") private external fun String.unsafeRangeEquals(thisOffset: Int, other: String, otherOffset: Int, length: Int): Boolean // Bounds must be checked before calling this method @@ -245,7 +246,7 @@ public actual fun String.lowercase(): String = lowercaseImpl() */ public actual fun String.toCharArray(): CharArray = toCharArray(this, 0, length) -@SymbolName("Kotlin_String_toCharArray") +@GCUnsafeCall("Kotlin_String_toCharArray") private external fun toCharArray(string: String, start: Int, size: Int): CharArray /** @@ -356,7 +357,7 @@ internal fun checkBoundsIndexes(startIndex: Int, endIndex: Int, size: Int) { } } -@SymbolName("Kotlin_String_unsafeStringFromCharArray") +@GCUnsafeCall("Kotlin_String_unsafeStringFromCharArray") internal external fun unsafeStringFromCharArray(array: CharArray, start: Int, size: Int) : String /** @@ -430,16 +431,16 @@ public actual fun String.encodeToByteArray(startIndex: Int, endIndex: Int, throw unsafeStringToUtf8(startIndex, endIndex - startIndex) } -@SymbolName("Kotlin_ByteArray_unsafeStringFromUtf8") +@GCUnsafeCall("Kotlin_ByteArray_unsafeStringFromUtf8") internal external fun ByteArray.unsafeStringFromUtf8(start: Int, size: Int) : String -@SymbolName("Kotlin_ByteArray_unsafeStringFromUtf8OrThrow") +@GCUnsafeCall("Kotlin_ByteArray_unsafeStringFromUtf8OrThrow") internal external fun ByteArray.unsafeStringFromUtf8OrThrow(start: Int, size: Int) : String -@SymbolName("Kotlin_String_unsafeStringToUtf8") +@GCUnsafeCall("Kotlin_String_unsafeStringToUtf8") internal external fun String.unsafeStringToUtf8(start: Int, size: Int) : ByteArray -@SymbolName("Kotlin_String_unsafeStringToUtf8OrThrow") +@GCUnsafeCall("Kotlin_String_unsafeStringToUtf8OrThrow") internal external fun String.unsafeStringToUtf8OrThrow(start: Int, size: Int) : ByteArray internal fun compareToIgnoreCase(thiz: String, other: String): Int { diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/text/regex/Lexer.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/text/regex/Lexer.kt index d28094d5a73..364c21eb3d6 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/text/regex/Lexer.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/text/regex/Lexer.kt @@ -24,24 +24,26 @@ @file:Suppress("DEPRECATION") // Char.toInt() package kotlin.text.regex +import kotlin.native.internal.GCUnsafeCall + // Access to the decomposition tables. ========================================================================= /** Gets canonical class for given codepoint from decomposition mappings table. */ -@SymbolName("Kotlin_text_regex_getCanonicalClassInternal") +@GCUnsafeCall("Kotlin_text_regex_getCanonicalClassInternal") external private fun getCanonicalClassInternal(ch: Int): Int /** Check if the given character is in table of single decompositions. */ -@SymbolName("Kotlin_text_regex_hasSingleCodepointDecompositionInternal") +@GCUnsafeCall("Kotlin_text_regex_hasSingleCodepointDecompositionInternal") external private fun hasSingleCodepointDecompositionInternal(ch: Int): Boolean /** Returns a decomposition for a given codepoint. */ -@SymbolName("Kotlin_text_regex_getDecompositionInternal") +@GCUnsafeCall("Kotlin_text_regex_getDecompositionInternal") external private fun getDecompositionInternal(ch: Int): IntArray? /** * Decomposes the given string represented as an array of codepoints. Saves the decomposition into [outputCodepoints] array. * Returns the length of the decomposition. */ -@SymbolName("Kotlin_text_regex_decomposeString") +@GCUnsafeCall("Kotlin_text_regex_decomposeString") external private fun decomposeString(inputCodePoints: IntArray, inputLength: Int, outputCodePoints: IntArray): Int // ============================================================================================================= diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/text/regex/sets/DecomposedCharSet.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/text/regex/sets/DecomposedCharSet.kt index 825e78a67b1..5b7ff0cff2d 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/text/regex/sets/DecomposedCharSet.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/text/regex/sets/DecomposedCharSet.kt @@ -22,11 +22,13 @@ package kotlin.text.regex +import kotlin.native.internal.GCUnsafeCall + /** * Decomposes the given codepoint. Saves the decomposition into [outputCodepoints] array starting with [fromIndex]. * Returns the length of the decomposition. */ -@SymbolName("Kotlin_text_regex_decomposeCodePoint") +@GCUnsafeCall("Kotlin_text_regex_decomposeCodePoint") external private fun decomposeCodePoint(codePoint: Int, outputCodePoints: IntArray, fromIndex: Int): Int /** Represents canonical decomposition of Unicode character. Is used when CANON_EQ flag of Pattern class is specified. */ diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/time/formatToDecimals.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/time/formatToDecimals.kt index eef7e12b957..23b2e24fe96 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/time/formatToDecimals.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/time/formatToDecimals.kt @@ -5,15 +5,16 @@ package kotlin.time +import kotlin.native.internal.GCUnsafeCall -@SymbolName("Kotlin_DurationValue_formatToExactDecimals") +@GCUnsafeCall("Kotlin_DurationValue_formatToExactDecimals") internal actual external fun formatToExactDecimals(value: Double, decimals: Int): String internal actual fun formatUpToDecimals(value: Double, decimals: Int): String { return formatToExactDecimals(value, decimals).trimEnd('0') } -@SymbolName("Kotlin_DurationValue_formatScientificImpl") +@GCUnsafeCall("Kotlin_DurationValue_formatScientificImpl") internal external fun formatScientificImpl(value: Double): String internal actual fun formatScientific(value: Double): String { diff --git a/kotlin-native/runtime/src/mm/cpp/InitializationScheme.cpp b/kotlin-native/runtime/src/mm/cpp/InitializationScheme.cpp index b1d44c57c20..004bf2d2307 100644 --- a/kotlin-native/runtime/src/mm/cpp/InitializationScheme.cpp +++ b/kotlin-native/runtime/src/mm/cpp/InitializationScheme.cpp @@ -8,11 +8,11 @@ #include "Common.h" #include "ObjectOps.hpp" #include "ThreadData.hpp" +#include "ThreadState.hpp" using namespace kotlin; OBJ_GETTER(mm::InitThreadLocalSingleton, ThreadData* threadData, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) { - // TODO: Is it possible that threadData != CurrentThreadData? AssertThreadState(threadData, ThreadState::kRunnable); if (auto* value = *location) { // Initialized by someone else. @@ -35,7 +35,6 @@ OBJ_GETTER(mm::InitThreadLocalSingleton, ThreadData* threadData, ObjHeader** loc } OBJ_GETTER(mm::InitSingleton, ThreadData* threadData, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) { - // TODO: Is it possible that threadData != CurrentThreadData? AssertThreadState(threadData, ThreadState::kRunnable); auto& initializingSingletons = threadData->initializingSingletons(); @@ -50,7 +49,10 @@ OBJ_GETTER(mm::InitSingleton, ThreadData* threadData, ObjHeader** location, cons // Spin lock. ObjHeader* value = nullptr; - while ((value = __sync_val_compare_and_swap(location, nullptr, initializing)) == initializing) { + { + ThreadStateGuard guard(ThreadState::kNative); + while ((value = __sync_val_compare_and_swap(location, nullptr, initializing)) == initializing) { + } } if (value != nullptr) { // Initialized by someone else. diff --git a/kotlin-native/runtime/src/mm/cpp/InitializationSchemeTest.cpp b/kotlin-native/runtime/src/mm/cpp/InitializationSchemeTest.cpp index 0325791a758..5e0ab7c3d1e 100644 --- a/kotlin-native/runtime/src/mm/cpp/InitializationSchemeTest.cpp +++ b/kotlin-native/runtime/src/mm/cpp/InitializationSchemeTest.cpp @@ -31,36 +31,26 @@ class InitSingletonTest : public testing::Test { public: InitSingletonTest() { globalConstructor_ = &constructor_; - - for (auto& threadData : threadDatas_) { - threadData = make_unique(pthread_t{}); - } } ~InitSingletonTest() { globalConstructor_ = nullptr; - // Make sure to clean everything allocated by the tests. - for (auto& threadData : threadDatas_) { - threadData->ClearForTests(); - } + mm::GlobalData::Instance().objectFactory().ClearForTests(); + mm::GlobalData::Instance().globalsRegistry().ClearForTests(); } - mm::ThreadData& threadData(size_t threadIndex) { return *threadDatas_[threadIndex]; } - testing::MockFunction& constructor() { return constructor_; } - OBJ_GETTER(InitThreadLocalSingleton, ObjHeader** location, size_t threadIndex) { - RETURN_RESULT_OF(mm::InitThreadLocalSingleton, threadDatas_[threadIndex].get(), location, type_.typeInfo(), constructorImpl); + OBJ_GETTER(InitThreadLocalSingleton, ObjHeader** location, mm::ThreadData& threadData) { + RETURN_RESULT_OF(mm::InitThreadLocalSingleton, &threadData, location, type_.typeInfo(), constructorImpl); } - OBJ_GETTER(InitSingleton, ObjHeader** location, size_t threadIndex) { - RETURN_RESULT_OF(mm::InitSingleton, threadDatas_[threadIndex].get(), location, type_.typeInfo(), constructorImpl); + OBJ_GETTER(InitSingleton, ObjHeader** location, mm::ThreadData& threadData) { + RETURN_RESULT_OF(mm::InitSingleton, &threadData, location, type_.typeInfo(), constructorImpl); } private: testing::StrictMock> constructor_; - // TODO: It makes sense to somehow abstract `ThreadData` stuff away. Allocation in this case. - std::array, kDefaultThreadCount> threadDatas_; test_support::TypeInfoHolder type_{test_support::TypeInfoHolder::ObjectBuilder()}; static testing::MockFunction* globalConstructor_; @@ -74,119 +64,136 @@ testing::MockFunction* InitSingletonTest::globalConstructor_ = } // namespace TEST_F(InitSingletonTest, InitThreadLocalSingleton) { - ObjHeader* location = nullptr; - ObjHeader* stackLocation = nullptr; + RunInNewThread([this](mm::ThreadData& threadData) { + ObjHeader* location = nullptr; + ObjHeader* stackLocation = nullptr; - ObjHeader* valueAtConstructor = nullptr; - EXPECT_CALL(constructor(), Call(_)).WillOnce([&location, &stackLocation, &valueAtConstructor](ObjHeader* value) { + ObjHeader* valueAtConstructor = nullptr; + EXPECT_CALL(constructor(), Call(_)).WillOnce( + [&location, &stackLocation, &valueAtConstructor](ObjHeader* value) { + EXPECT_THAT(value, stackLocation); + EXPECT_THAT(value, location); + valueAtConstructor = value; + }); + ObjHeader* value = InitThreadLocalSingleton(&location, threadData, &stackLocation); EXPECT_THAT(value, stackLocation); EXPECT_THAT(value, location); - valueAtConstructor = value; + EXPECT_THAT(valueAtConstructor, location); }); - ObjHeader* value = InitThreadLocalSingleton(&location, 0, &stackLocation); - EXPECT_THAT(value, stackLocation); - EXPECT_THAT(value, location); - EXPECT_THAT(valueAtConstructor, location); } TEST_F(InitSingletonTest, InitThreadLocalSingletonTwice) { - ObjHeader previousValue; - ObjHeader* location = &previousValue; - ObjHeader* stackLocation = nullptr; + RunInNewThread([this](mm::ThreadData& threadData) { + ObjHeader previousValue; + ObjHeader* location = &previousValue; + ObjHeader* stackLocation = nullptr; - EXPECT_CALL(constructor(), Call(_)).Times(0); - ObjHeader* value = InitThreadLocalSingleton(&location, 0, &stackLocation); - EXPECT_THAT(value, stackLocation); - EXPECT_THAT(value, location); - EXPECT_THAT(value, &previousValue); + EXPECT_CALL(constructor(), Call(_)).Times(0); + ObjHeader* value = InitThreadLocalSingleton(&location, threadData, &stackLocation); + EXPECT_THAT(value, stackLocation); + EXPECT_THAT(value, location); + EXPECT_THAT(value, &previousValue); + }); } TEST_F(InitSingletonTest, InitThreadLocalSingletonFail) { - ObjHeader* location = nullptr; - ObjHeader* stackLocation = nullptr; - constexpr int kException = 42; + RunInNewThread([this](mm::ThreadData& threadData) { + ObjHeader* location = nullptr; + ObjHeader* stackLocation = nullptr; + constexpr int kException = 42; - EXPECT_CALL(constructor(), Call(_)).WillOnce([]() { throw kException; }); - try { - InitThreadLocalSingleton(&location, 0, &stackLocation); - ASSERT_TRUE(false); // Cannot be reached. - } catch (int exception) { - EXPECT_THAT(exception, kException); - } - EXPECT_THAT(stackLocation, nullptr); - EXPECT_THAT(location, nullptr); + EXPECT_CALL(constructor(), Call(_)).WillOnce([]() { throw kException; }); + try { + InitThreadLocalSingleton(&location, threadData, &stackLocation); + ASSERT_TRUE(false); // Cannot be reached. + } catch (int exception) { + EXPECT_THAT(exception, kException); + } + EXPECT_THAT(stackLocation, nullptr); + EXPECT_THAT(location, nullptr); + }); } TEST_F(InitSingletonTest, InitSingleton) { - ObjHeader* location = nullptr; - ObjHeader* stackLocation = nullptr; + RunInNewThread([this](mm::ThreadData& threadData) { + ObjHeader* location = nullptr; + ObjHeader* stackLocation = nullptr; - ObjHeader* valueAtConstructor = nullptr; - EXPECT_CALL(constructor(), Call(_)).WillOnce([&location, &stackLocation, &valueAtConstructor](ObjHeader* value) { + ObjHeader* valueAtConstructor = nullptr; + EXPECT_CALL(constructor(), Call(_)).WillOnce( + [&location, &stackLocation, &valueAtConstructor](ObjHeader* value) { + EXPECT_THAT(value, stackLocation); + EXPECT_THAT(location, kInitializingSingleton); + valueAtConstructor = value; + }); + ObjHeader* value = InitSingleton(&location, threadData, &stackLocation); EXPECT_THAT(value, stackLocation); - EXPECT_THAT(location, kInitializingSingleton); - valueAtConstructor = value; + EXPECT_THAT(value, location); + EXPECT_THAT(valueAtConstructor, location); }); - ObjHeader* value = InitSingleton(&location, 0, &stackLocation); - EXPECT_THAT(value, stackLocation); - EXPECT_THAT(value, location); - EXPECT_THAT(valueAtConstructor, location); } -TEST_F(InitSingletonTest, InitSingletonTwice) { - ObjHeader previousValue; - ObjHeader* location = &previousValue; - ObjHeader* stackLocation = nullptr; - EXPECT_CALL(constructor(), Call(_)).Times(0); - ObjHeader* value = InitSingleton(&location, 0, &stackLocation); - EXPECT_THAT(value, stackLocation); - EXPECT_THAT(value, location); - EXPECT_THAT(value, &previousValue); +TEST_F(InitSingletonTest, InitSingletonTwice) { + RunInNewThread([this](mm::ThreadData& threadData) { + ObjHeader previousValue; + ObjHeader* location = &previousValue; + ObjHeader* stackLocation = nullptr; + + EXPECT_CALL(constructor(), Call(_)).Times(0); + ObjHeader* value = InitSingleton(&location, threadData, &stackLocation); + EXPECT_THAT(value, stackLocation); + EXPECT_THAT(value, location); + EXPECT_THAT(value, &previousValue); + }); } TEST_F(InitSingletonTest, InitSingletonFail) { - ObjHeader* location = nullptr; - ObjHeader* stackLocation = nullptr; - constexpr int kException = 42; + RunInNewThread([this](mm::ThreadData& threadData) { + ObjHeader* location = nullptr; + ObjHeader* stackLocation = nullptr; + constexpr int kException = 42; - EXPECT_CALL(constructor(), Call(_)).WillOnce([]() { throw kException; }); - try { - InitSingleton(&location, 0, &stackLocation); - ASSERT_TRUE(false); // Cannot be reached. - } catch (int exception) { - EXPECT_THAT(exception, kException); - } - EXPECT_THAT(stackLocation, nullptr); - EXPECT_THAT(location, nullptr); + EXPECT_CALL(constructor(), Call(_)).WillOnce([]() { throw kException; }); + try { + InitSingleton(&location, threadData, &stackLocation); + ASSERT_TRUE(false); // Cannot be reached. + } catch (int exception) { + EXPECT_THAT(exception, kException); + } + EXPECT_THAT(stackLocation, nullptr); + EXPECT_THAT(location, nullptr); + }); } TEST_F(InitSingletonTest, InitSingletonRecursive) { - // The first singleton. Its constructor depends on the second singleton. - ObjHeader* location1 = nullptr; - ObjHeader* stackLocation1 = nullptr; - // The second singleton. Its constructor depends on the first singleton. - ObjHeader* location2 = nullptr; - ObjHeader* stackLocation2 = nullptr; + RunInNewThread([this](mm::ThreadData& threadData) { + // The first singleton. Its constructor depends on the second singleton. + ObjHeader* location1 = nullptr; + ObjHeader* stackLocation1 = nullptr; + // The second singleton. Its constructor depends on the first singleton. + ObjHeader* location2 = nullptr; + ObjHeader* stackLocation2 = nullptr; - EXPECT_CALL(constructor(), Call(_)) - .Times(2) // called only once for each singleton. - .WillRepeatedly([this, &location1, &stackLocation1, &location2, &stackLocation2](ObjHeader* value) { - if (value == stackLocation1) { - ObjHeader* result = InitSingleton(&location2, 0, &stackLocation2); - EXPECT_THAT(result, stackLocation2); - EXPECT_THAT(result, location2); - EXPECT_THAT(result, testing::Not(testing::Truly(isNullOrMarker))); - } else { - ObjHeader* result = InitSingleton(&location1, 0, &stackLocation1); - EXPECT_THAT(result, stackLocation1); - EXPECT_THAT(result, testing::Ne(location1)); - EXPECT_THAT(location1, kInitializingSingleton); - } - }); - ObjHeader* value = InitSingleton(&location1, 0, &stackLocation1); - EXPECT_THAT(value, stackLocation1); - EXPECT_THAT(value, location1); + EXPECT_CALL(constructor(), Call(_)) + .Times(2) // called only once for each singleton. + .WillRepeatedly([this, &location1, &stackLocation1, &location2, &stackLocation2, &threadData](ObjHeader* value) { + if (value == stackLocation1) { + ObjHeader* result = InitSingleton(&location2, threadData, &stackLocation2); + EXPECT_THAT(result, stackLocation2); + EXPECT_THAT(result, location2); + EXPECT_THAT(result, testing::Not(testing::Truly(isNullOrMarker))); + } else { + ObjHeader* result = InitSingleton(&location1, threadData, &stackLocation1); + EXPECT_THAT(result, stackLocation1); + EXPECT_THAT(result, testing::Ne(location1)); + EXPECT_THAT(location1, kInitializingSingleton); + } + }); + ObjHeader* value = InitSingleton(&location1, threadData, &stackLocation1); + EXPECT_THAT(value, stackLocation1); + EXPECT_THAT(value, location1); + }); } TEST_F(InitSingletonTest, InitSingletonConcurrent) { @@ -200,10 +207,13 @@ TEST_F(InitSingletonTest, InitSingletonConcurrent) { for (size_t i = 0; i < kThreadCount; ++i) { threads.emplace_back([this, i, &location, &stackLocations, &actual, &readyCount, &canStart]() { + ScopedMemoryInit init; + auto* threadData = init.memoryState()->GetThreadData(); ++readyCount; while (!canStart) { } - actual[i] = InitSingleton(&location, i, &stackLocations[i]); + actual[i] = InitSingleton(&location, *threadData, &stackLocations[i]); + threadData->Publish(); }); } @@ -233,15 +243,18 @@ TEST_F(InitSingletonTest, InitSingletonConcurrentFailing) { for (size_t i = 0; i < kThreadCount; ++i) { threads.emplace_back([this, i, &location, &stackLocations, &readyCount, &canStart]() { + ScopedMemoryInit init; + auto* threadData = init.memoryState()->GetThreadData(); ++readyCount; while (!canStart) { } try { - InitSingleton(&location, i, &stackLocations[i]); + InitSingleton(&location, *threadData, &stackLocations[i]); ASSERT_TRUE(false); // Cannot be reached. } catch (int exception) { EXPECT_THAT(exception, kException); } + threadData->Publish(); }); } diff --git a/kotlin-native/runtime/src/mm/cpp/Memory.cpp b/kotlin-native/runtime/src/mm/cpp/Memory.cpp index 991a51ced7b..42cd2469dd3 100644 --- a/kotlin-native/runtime/src/mm/cpp/Memory.cpp +++ b/kotlin-native/runtime/src/mm/cpp/Memory.cpp @@ -18,6 +18,7 @@ #include "StableRefRegistry.hpp" #include "ThreadData.hpp" #include "ThreadRegistry.hpp" +#include "ThreadState.hpp" #include "Utils.hpp" using namespace kotlin; @@ -354,12 +355,15 @@ extern "C" RUNTIME_NOTHROW void* CreateStablePointer(ObjHeader* object) { } extern "C" RUNTIME_NOTHROW void DisposeStablePointer(void* pointer) { + DisposeStablePointerFor(kotlin::mm::GetMemoryState(), pointer); +} + +extern "C" RUNTIME_NOTHROW void DisposeStablePointerFor(MemoryState* memoryState, void* pointer) { if (!pointer) return; - auto* threadData = mm::ThreadRegistry::Instance().CurrentThreadData(); auto* node = static_cast(pointer); - mm::StableRefRegistry::Instance().UnregisterStableRef(threadData, node); + mm::StableRefRegistry::Instance().UnregisterStableRef(memoryState->GetThreadData(), node); } extern "C" RUNTIME_NOTHROW OBJ_GETTER(DerefStablePointer, void* pointer) { @@ -443,16 +447,19 @@ extern "C" void CheckGlobalsAccessible() { extern "C" RUNTIME_NOTHROW void Kotlin_mm_safePointFunctionEpilogue() { auto* threadData = mm::ThreadRegistry::Instance().CurrentThreadData(); + AssertThreadState(threadData, ThreadState::kRunnable); threadData->gc().SafePointFunctionEpilogue(); } extern "C" RUNTIME_NOTHROW void Kotlin_mm_safePointWhileLoopBody() { auto* threadData = mm::ThreadRegistry::Instance().CurrentThreadData(); + AssertThreadState(threadData, ThreadState::kRunnable); threadData->gc().SafePointLoopBody(); } extern "C" RUNTIME_NOTHROW void Kotlin_mm_safePointExceptionUnwind() { auto* threadData = mm::ThreadRegistry::Instance().CurrentThreadData(); + AssertThreadState(threadData, ThreadState::kRunnable); threadData->gc().SafePointExceptionUnwind(); } diff --git a/kotlin-native/runtime/src/mm/cpp/ObjectOps.cpp b/kotlin-native/runtime/src/mm/cpp/ObjectOps.cpp index b05707b919a..0ac28b86767 100644 --- a/kotlin-native/runtime/src/mm/cpp/ObjectOps.cpp +++ b/kotlin-native/runtime/src/mm/cpp/ObjectOps.cpp @@ -7,6 +7,7 @@ #include "Common.h" #include "ThreadData.hpp" +#include "ThreadState.hpp" using namespace kotlin; @@ -55,14 +56,14 @@ ALWAYS_INLINE OBJ_GETTER(mm::CompareAndSwapHeapRef, ObjHeader** location, ObjHea #pragma clang diagnostic pop OBJ_GETTER(mm::AllocateObject, ThreadData* threadData, const TypeInfo* typeInfo) noexcept { - AssertThreadState(ThreadState::kRunnable); + AssertThreadState(threadData, ThreadState::kRunnable); // TODO: Make this work with GCs that can stop thread at any point. auto* object = threadData->objectFactoryThreadQueue().CreateObject(typeInfo); RETURN_OBJ(object); } OBJ_GETTER(mm::AllocateArray, ThreadData* threadData, const TypeInfo* typeInfo, uint32_t elements) noexcept { - AssertThreadState(ThreadState::kRunnable); + AssertThreadState(threadData, ThreadState::kRunnable); // TODO: Make this work with GCs that can stop thread at any point. auto* array = threadData->objectFactoryThreadQueue().CreateArray(typeInfo, static_cast(elements)); // `ArrayHeader` and `ObjHeader` are expected to be compatible. diff --git a/kotlin-native/runtime/src/mm/cpp/TestSupport.cpp b/kotlin-native/runtime/src/mm/cpp/TestSupport.cpp new file mode 100644 index 00000000000..0c542b101f1 --- /dev/null +++ b/kotlin-native/runtime/src/mm/cpp/TestSupport.cpp @@ -0,0 +1,60 @@ +/* + * 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 "gtest/gtest.h" +#include "gmock/gmock.h" + +#include "GC.hpp" +#include "GlobalData.hpp" +#include "GlobalsRegistry.hpp" +#include "TestSupport.hpp" +#include "ThreadData.hpp" + +using namespace kotlin; + +namespace { + +template +bool isEmpty(T& iterable) { + return iterable.begin() == iterable.end(); +} + +template +std::vector collect(T& iterable) { + std::vector result; + for (E element : iterable) { + result.push_back(element); + } + return std::move(result); +} + +std::vector collect(mm::ThreadRegistry::Iterable& iterable) { + std::vector result; + for (mm::ThreadData& element : iterable) { + result.push_back(&element); + } + // Do not use std::move because clang complains that it prevents copy elision. + return result; +} + +} // namespace + +extern "C" void Kotlin_TestSupport_AssertClearGlobalState() { + // Validate that global registries are empty. + auto globals = mm::GlobalsRegistry::Instance().Iter(); + auto objects = mm::GlobalData::Instance().objectFactory().Iter(); + auto stableRefs = mm::StableRefRegistry::Instance().Iter(); + auto threads = mm::ThreadRegistry::Instance().Iter(); + + EXPECT_THAT(collect(globals), testing::UnorderedElementsAre()); + EXPECT_THAT(collect::NodeRef>(objects), testing::UnorderedElementsAre()); + EXPECT_THAT(collect(stableRefs), testing::UnorderedElementsAre()); + EXPECT_THAT(collect(threads), testing::UnorderedElementsAre()); +} + +void kotlin::DeinitMemoryForTests(MemoryState* memoryState) { + DeinitMemory(memoryState, false); + mm::ThreadRegistry::TestSupport::ClearCurrentThreadData(); +} \ No newline at end of file diff --git a/kotlin-native/runtime/src/mm/cpp/ThreadData.hpp b/kotlin-native/runtime/src/mm/cpp/ThreadData.hpp index 12e6d7e4a68..c994561b879 100644 --- a/kotlin-native/runtime/src/mm/cpp/ThreadData.hpp +++ b/kotlin-native/runtime/src/mm/cpp/ThreadData.hpp @@ -16,7 +16,6 @@ #include "ShadowStack.hpp" #include "StableRefRegistry.hpp" #include "ThreadLocalStorage.hpp" -#include "ThreadState.hpp" #include "Types.h" #include "Utils.hpp" diff --git a/kotlin-native/runtime/src/mm/cpp/ThreadRegistry.hpp b/kotlin-native/runtime/src/mm/cpp/ThreadRegistry.hpp index 9a902879fc3..92462033101 100644 --- a/kotlin-native/runtime/src/mm/cpp/ThreadRegistry.hpp +++ b/kotlin-native/runtime/src/mm/cpp/ThreadRegistry.hpp @@ -37,6 +37,11 @@ public: ALWAYS_INLINE ThreadData* CurrentThreadData() const noexcept; Node* CurrentThreadDataNode() const noexcept { return currentThreadDataNode_; } + class TestSupport { + public: + static void ClearCurrentThreadData() { currentThreadDataNode_ = nullptr; } + }; + private: friend class GlobalData; diff --git a/kotlin-native/runtime/src/mm/cpp/ThreadRegistryTest.cpp b/kotlin-native/runtime/src/mm/cpp/ThreadRegistryTest.cpp index 11fd9b033eb..711267e5e87 100644 --- a/kotlin-native/runtime/src/mm/cpp/ThreadRegistryTest.cpp +++ b/kotlin-native/runtime/src/mm/cpp/ThreadRegistryTest.cpp @@ -16,8 +16,14 @@ using namespace kotlin; TEST(ThreadRegistryTest, RegisterCurrentThread) { std::thread t([]() { - auto* node = mm::ThreadRegistry::Instance().RegisterCurrentThread(); - auto* threadData = node->Get(); + class ScopedRegistration { + public: + ScopedRegistration() : node(mm::ThreadRegistry::Instance().RegisterCurrentThread()) {} + ~ScopedRegistration() { mm::ThreadRegistry::Instance().Unregister(node); } + mm::ThreadRegistry::Node* node; + } registration; + + auto* threadData = registration.node->Get(); EXPECT_EQ(pthread_self(), threadData->threadId()); EXPECT_EQ(threadData, mm::ThreadRegistry::Instance().CurrentThreadData()); }); diff --git a/kotlin-native/runtime/src/mm/cpp/ThreadState.cpp b/kotlin-native/runtime/src/mm/cpp/ThreadState.cpp index ece1b42ce56..8486ca4719e 100644 --- a/kotlin-native/runtime/src/mm/cpp/ThreadState.cpp +++ b/kotlin-native/runtime/src/mm/cpp/ThreadState.cpp @@ -7,13 +7,7 @@ #include "ThreadData.hpp" #include "ThreadState.hpp" -namespace { - -ALWAYS_INLINE bool isStateSwitchAllowed(ThreadState oldState, ThreadState newState) noexcept { - return oldState != newState; -} - -const char* stateToString(ThreadState state) noexcept { +const char* kotlin::internal::stateToString(ThreadState state) noexcept { switch (state) { case ThreadState::kRunnable: return "RUNNABLE"; @@ -22,29 +16,14 @@ const char* stateToString(ThreadState state) noexcept { } } -} // namespace - -// Switches the state of the given thread to `newState` and returns the previous state. -ALWAYS_INLINE ThreadState kotlin::SwitchThreadState(mm::ThreadData* threadData, ThreadState newState) noexcept { - auto oldState = threadData->setState(newState); - // TODO(perf): Mesaure the impact of this assert in debug and opt modes. - RuntimeAssert(isStateSwitchAllowed(oldState, newState), - "Illegal thread state switch. Old state: %s. New state: %s.", - stateToString(oldState), stateToString(newState)); - return oldState; -} - -ALWAYS_INLINE ThreadState kotlin::SwitchThreadState(MemoryState* thread, ThreadState newState) noexcept { - return SwitchThreadState(thread->GetThreadData(), newState); -} - -ALWAYS_INLINE void kotlin::AssertThreadState(mm::ThreadData* threadData, ThreadState expected) noexcept { - auto actual = threadData->state(); - RuntimeAssert(actual == expected, - "Unexpected thread state. Expected: %s. Actual: %s.", - stateToString(expected), stateToString(actual)); +ALWAYS_INLINE ThreadState kotlin::SwitchThreadState(MemoryState* thread, ThreadState newState, bool reentrant) noexcept { + return SwitchThreadState(thread->GetThreadData(), newState, reentrant); } ALWAYS_INLINE void kotlin::AssertThreadState(MemoryState* thread, ThreadState expected) noexcept { AssertThreadState(thread->GetThreadData(), expected); } + +ThreadState kotlin::GetThreadState(MemoryState* thread) noexcept { + return thread->GetThreadData()->state(); +} \ No newline at end of file diff --git a/kotlin-native/runtime/src/mm/cpp/ThreadState.hpp b/kotlin-native/runtime/src/mm/cpp/ThreadState.hpp index 4e3be55678c..6b3c8ee49d9 100644 --- a/kotlin-native/runtime/src/mm/cpp/ThreadState.hpp +++ b/kotlin-native/runtime/src/mm/cpp/ThreadState.hpp @@ -9,13 +9,37 @@ #include #include +#include "ThreadData.hpp" + namespace kotlin { +namespace internal { + +ALWAYS_INLINE inline bool isStateSwitchAllowed(ThreadState oldState, ThreadState newState, bool reentrant) noexcept { + return oldState != newState || reentrant; +} + +const char* stateToString(ThreadState state) noexcept; + +} // namespace internal + // Switches the state of the given thread to `newState` and returns the previous thread state. -ALWAYS_INLINE ThreadState SwitchThreadState(mm::ThreadData* threadData, ThreadState newState) noexcept; +ALWAYS_INLINE inline ThreadState SwitchThreadState(mm::ThreadData* threadData, ThreadState newState, bool reentrant = false) noexcept { + auto oldState = threadData->setState(newState); + // TODO(perf): Mesaure the impact of this assert in debug and opt modes. + RuntimeAssert(internal::isStateSwitchAllowed(oldState, newState, reentrant), + "Illegal thread state switch. Old state: %s. New state: %s.", + internal::stateToString(oldState), internal::stateToString(newState)); + return oldState; +} // Asserts that the given thread is in the given state. -ALWAYS_INLINE void AssertThreadState(mm::ThreadData* threadData, ThreadState expected) noexcept; +ALWAYS_INLINE inline void AssertThreadState(mm::ThreadData* threadData, ThreadState expected) noexcept { + auto actual = threadData->state(); + RuntimeAssert(actual == expected, + "Unexpected thread state. Expected: %s. Actual: %s.", + internal::stateToString(expected), internal::stateToString(actual)); +} } // namespace kotlin diff --git a/kotlin-native/runtime/src/mm/cpp/ThreadStateTest.cpp b/kotlin-native/runtime/src/mm/cpp/ThreadStateTest.cpp index c61920383e9..c556820a123 100644 --- a/kotlin-native/runtime/src/mm/cpp/ThreadStateTest.cpp +++ b/kotlin-native/runtime/src/mm/cpp/ThreadStateTest.cpp @@ -20,30 +20,26 @@ namespace { class ThreadStateTest : public testing::Test { public: ThreadStateTest() { - globalKotlinFunctionMock = &kotlinFunctionMock_; + globalSomeFunctionMock = &someFunctionMock(); } ~ThreadStateTest() { - globalKotlinFunctionMock = nullptr; + globalSomeFunctionMock = nullptr; } - testing::MockFunction& kotlinFunctionMock() { return kotlinFunctionMock_; } + testing::MockFunction& someFunctionMock() { return someFunctionMock_; } - static int32_t kotlinFunction(int32_t arg) { - return globalKotlinFunctionMock->Call(arg); + static int32_t someFunction(int32_t arg) { + return globalSomeFunctionMock->Call(arg); } - static RUNTIME_NORETURN void noReturnKotlinFunciton(int32_t arg) { - globalKotlinFunctionMock->Call(arg); - throw std::exception(); - } private: - testing::MockFunction kotlinFunctionMock_; - static testing::MockFunction* globalKotlinFunctionMock; + testing::MockFunction someFunctionMock_; + static testing::MockFunction* globalSomeFunctionMock; }; //static -testing::MockFunction* ThreadStateTest::globalKotlinFunctionMock = nullptr; +testing::MockFunction* ThreadStateTest::globalSomeFunctionMock = nullptr; } // namespace @@ -109,35 +105,34 @@ TEST_F(ThreadStateTest, StateGuardForCurrentThread) { }); } -TEST_F(ThreadStateTest, CallKotlin) { +TEST_F(ThreadStateTest, CallWithNativeState) { RunInNewThread([this](mm::ThreadData& threadData) { - SwitchThreadState(&threadData, ThreadState::kNative); - ASSERT_THAT(threadData.state(), ThreadState::kNative); + ASSERT_THAT(threadData.state(), ThreadState::kRunnable); - EXPECT_CALL(kotlinFunctionMock(), Call(42)) + EXPECT_CALL(someFunctionMock(), Call(42)) .WillOnce([&threadData](int32_t arg) { - EXPECT_THAT(threadData.state(), ThreadState::kRunnable); + EXPECT_THAT(threadData.state(), ThreadState::kNative); return 24; }); - int32_t result = CallKotlin(kotlinFunction, 42); - EXPECT_THAT(threadData.state(), ThreadState::kNative); + int32_t result = CallWithThreadState(someFunction, 42); + EXPECT_THAT(threadData.state(), ThreadState::kRunnable); EXPECT_THAT(result, 24); }); } -TEST_F(ThreadStateTest, CallKotlinNoReturn) { +TEST_F(ThreadStateTest, CallWithRunnableState) { RunInNewThread([this](mm::ThreadData& threadData) { - SwitchThreadState(&threadData, ThreadState::kNative); - ASSERT_THAT(threadData.state(), ThreadState::kNative); + SwitchThreadState(&threadData, ThreadState::kNative); + ASSERT_THAT(threadData.state(), ThreadState::kNative); - EXPECT_CALL(kotlinFunctionMock(), Call(42)) - .WillOnce([&threadData](int32_t arg){ + EXPECT_CALL(someFunctionMock(), Call(42)) + .WillOnce([&threadData](int32_t arg) { EXPECT_THAT(threadData.state(), ThreadState::kRunnable); return 24; }); - - EXPECT_THROW(CallKotlinNoReturn(noReturnKotlinFunciton, 42), std::exception); - EXPECT_THAT(threadData.state(), ThreadState::kNative); + int32_t result = CallWithThreadState(someFunction, 42); + EXPECT_THAT(threadData.state(), ThreadState::kNative); + EXPECT_THAT(result, 24); }); } @@ -169,3 +164,13 @@ TEST(ThreadStateDeathTest, IncorrectStateSwitch) { "runtime assert: Illegal thread state switch. Old state: NATIVE. New state: NATIVE"); }); } + +TEST(ThreadStateDeathTest, ReentrantStateSwitch) { + RunInNewThread([](MemoryState* memoryState) { + auto* threadData = memoryState->GetThreadData(); + ASSERT_EQ(threadData->state(), ThreadState::kRunnable); + EXPECT_EXIT({ SwitchThreadState(memoryState, ThreadState::kRunnable, true); exit(0); }, + testing::ExitedWithCode(0), + testing::Not(testing::ContainsRegex("runtime assert: Illegal thread state switch."))); + }); +} \ No newline at end of file diff --git a/kotlin-native/runtime/src/test_support/cpp/TestLauncher.cpp b/kotlin-native/runtime/src/test_support/cpp/TestLauncher.cpp index 0981e3e6e71..8c107c2353a 100644 --- a/kotlin-native/runtime/src/test_support/cpp/TestLauncher.cpp +++ b/kotlin-native/runtime/src/test_support/cpp/TestLauncher.cpp @@ -6,10 +6,26 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" +extern "C" void Kotlin_TestSupport_AssertClearGlobalState(); + +namespace { + +class GlobalStateChecker : public testing::Environment { +public: + void TearDown() override { Kotlin_TestSupport_AssertClearGlobalState(); } +}; + +} // namespace + int main(int argc, char** argv) { testing::InitGoogleMock(&argc, argv); + // Use the `threadsafe` style to mitigate possible issues with multithreaded death tests. // See more about death test styles: https://github.com/google/googletest/blob/master/googletest/docs/advanced.md#how-it-works testing::FLAGS_gtest_death_test_style="threadsafe"; + + // Googletest takes ownership of the registered environment object. + testing::AddGlobalTestEnvironment(new GlobalStateChecker()); + return RUN_ALL_TESTS(); }