[K/N][New MM] Support thread state switching

Including

* Support thread state switching in codegen
* Introduce and use GCUnsafeCall annotation
* Switch thread state in C++ runtime code

Also

* Register current thread in Mark&Sweep tests
* Store MemoryState in Worker instance
* Set worker tid in WorkerInit
This commit is contained in:
Ilya Matveev
2021-01-25 19:06:27 +07:00
committed by Space
parent 0b46ed3cde
commit 4d346d3735
86 changed files with 1340 additions and 721 deletions
@@ -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<ShortVar>.toKString(): String = this.toKStringFromUtf16()
public fun CPointer<UShortVar>.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)
@@ -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
@@ -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<ByteVar>.toKStringFromUtf8Impl(): String
@TypedIntrinsic(IntrinsicType.INTEROP_BITS_TO_FLOAT)
@@ -16,6 +16,7 @@
package kotlinx.cinterop
import kotlin.native.*
import kotlin.native.internal.GCUnsafeCall
data class Pinned<out T : Any> internal constructor(private val stablePtr: COpaquePointer) {
@@ -92,38 +93,38 @@ private inline fun <T : Any, P : CPointed> T.usingPinned(
}
}
@SymbolName("Kotlin_Arrays_getByteArrayAddressOfElement")
@GCUnsafeCall("Kotlin_Arrays_getByteArrayAddressOfElement")
private external fun ByteArray.addressOfElement(index: Int): CPointer<ByteVar>
@SymbolName("Kotlin_Arrays_getStringAddressOfElement")
@GCUnsafeCall("Kotlin_Arrays_getStringAddressOfElement")
private external fun String.addressOfElement(index: Int): CPointer<COpaque>
@SymbolName("Kotlin_Arrays_getCharArrayAddressOfElement")
@GCUnsafeCall("Kotlin_Arrays_getCharArrayAddressOfElement")
private external fun CharArray.addressOfElement(index: Int): CPointer<COpaque>
@SymbolName("Kotlin_Arrays_getShortArrayAddressOfElement")
@GCUnsafeCall("Kotlin_Arrays_getShortArrayAddressOfElement")
private external fun ShortArray.addressOfElement(index: Int): CPointer<ShortVar>
@SymbolName("Kotlin_Arrays_getIntArrayAddressOfElement")
@GCUnsafeCall("Kotlin_Arrays_getIntArrayAddressOfElement")
private external fun IntArray.addressOfElement(index: Int): CPointer<IntVar>
@SymbolName("Kotlin_Arrays_getLongArrayAddressOfElement")
@GCUnsafeCall("Kotlin_Arrays_getLongArrayAddressOfElement")
private external fun LongArray.addressOfElement(index: Int): CPointer<LongVar>
@SymbolName("Kotlin_Arrays_getByteArrayAddressOfElement")
@GCUnsafeCall("Kotlin_Arrays_getByteArrayAddressOfElement")
private external fun UByteArray.addressOfElement(index: Int): CPointer<UByteVar>
@SymbolName("Kotlin_Arrays_getShortArrayAddressOfElement")
@GCUnsafeCall("Kotlin_Arrays_getShortArrayAddressOfElement")
private external fun UShortArray.addressOfElement(index: Int): CPointer<UShortVar>
@SymbolName("Kotlin_Arrays_getIntArrayAddressOfElement")
@GCUnsafeCall("Kotlin_Arrays_getIntArrayAddressOfElement")
private external fun UIntArray.addressOfElement(index: Int): CPointer<UIntVar>
@SymbolName("Kotlin_Arrays_getLongArrayAddressOfElement")
@GCUnsafeCall("Kotlin_Arrays_getLongArrayAddressOfElement")
private external fun ULongArray.addressOfElement(index: Int): CPointer<ULongVar>
@SymbolName("Kotlin_Arrays_getFloatArrayAddressOfElement")
@GCUnsafeCall("Kotlin_Arrays_getFloatArrayAddressOfElement")
private external fun FloatArray.addressOfElement(index: Int): CPointer<FloatVar>
@SymbolName("Kotlin_Arrays_getDoubleArrayAddressOfElement")
@GCUnsafeCall("Kotlin_Arrays_getDoubleArrayAddressOfElement")
private external fun DoubleArray.addressOfElement(index: Int): CPointer<DoubleVar>
@@ -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(),
@@ -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")
}
@@ -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")
}
@@ -291,6 +291,8 @@ fun <T> 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
@@ -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<LLVMValueRef>,
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
@@ -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
@@ -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<IrE
}
}
override val exceptionHandler get() = ExceptionHandler.Caller
override val exceptionHandler: ExceptionHandler
get() = ExceptionHandler.Caller
override fun genThrow(exception: LLVMValueRef) {
val objHeaderPtr = functionGenerationContext.bitcast(codegen.kObjHeaderPtr, exception)
@@ -1030,7 +1032,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
private inner abstract class CatchingScope : InnerScopeImpl() {
/**
* The LLVM `landingpad` such that if invoked function throws an exception,
* The LLVM `landingpad` such that if an invoked function throws an exception,
* then this exception is passed to [handler].
*/
private val landingpad: LLVMBasicBlockRef by lazy {
@@ -1087,9 +1089,10 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
}
}
override val exceptionHandler: ExceptionHandler get() = object : ExceptionHandler.Local() {
override val unwind get() = landingpad
}
override val exceptionHandler: ExceptionHandler
get() = object : ExceptionHandler.Local() {
override val unwind get() = landingpad
}
override fun genThrow(exception: LLVMValueRef) {
jumpToHandler(exception)
@@ -2332,17 +2335,39 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
//-------------------------------------------------------------------------//
private val IrFunction.needsNativeThreadState: Boolean
get() {
// We assume that call site thread state switching is required for interop calls only.
val result = context.memoryModel == MemoryModel.EXPERIMENTAL && origin == CBridgeOrigin.KOTLIN_TO_C_BRIDGE
if (result) {
check(isExternal)
check(!annotations.hasAnnotation(KonanFqNames.gcUnsafeCall))
check(annotations.hasAnnotation(RuntimeNames.filterExceptions))
}
return result
}
private fun call(function: IrFunction, llvmFunction: LLVMValueRef, args: List<LLVMValueRef>,
resultLifetime: Lifetime): LLVMValueRef {
check(!function.isTypedIntrinsic)
val needsNativeThreadState = function.needsNativeThreadState
val exceptionHandler = function.annotations.findAnnotation(RuntimeNames.filterExceptions)?.let {
val foreignExceptionMode = ForeignExceptionMode.byValue(it.getAnnotationValueOrNull<String>("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) {
@@ -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
@@ -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'
@@ -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")
}
@@ -0,0 +1,20 @@
#include <thread>
#include <stdint.h>
#include <stdlib.h>
// 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();
}
@@ -0,0 +1,20 @@
language = C
---
#include <stdint.h>
void assertNativeThreadState();
void runCallback(void(*callback)(void)) {
assertNativeThreadState();
callback();
assertNativeThreadState();
}
int32_t answer() {
assertNativeThreadState();
return 42;
}
void runInNewThread(void(*callback)(void));
@@ -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()
})
}
@@ -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<uint32_t>(bufferSize - 1) ? bufferSize - 1 : data->count_;
@@ -6,6 +6,7 @@
#include "MarkAndSweepUtils.hpp"
#include <functional>
#include <TestSupport.hpp>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
@@ -142,6 +143,7 @@ public:
}
private:
kotlin::ScopedMemoryInit memoryInit;
ScopedMarkTraits markTraits_;
};
@@ -3,6 +3,7 @@
* that can be found in the LICENSE file.
*/
#include <TestSupport.hpp>
#include "MarkAndSweepUtils.hpp"
#include "gmock/gmock.h"
@@ -232,6 +233,8 @@ public:
testing::MockFunction<void(ObjHeader*)>& 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_;
@@ -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;
}
@@ -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);
}
+22 -16
View File
@@ -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::ThreadState::kRunnable>(Kotlin_CleanerImpl_createCleanerWorker);
if (!compareAndSet(&globalCleanerWorker, kCleanerWorkerInitializing, worker)) {
RuntimeCheck(false, "Someone interrupted worker initializing");
}
@@ -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<std::future<KInt>> 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);
});
}
+27 -19
View File
@@ -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"
@@ -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<KNativePtr>(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<ThreadState::kNative>(_Unwind_Backtrace, depthCountCallback, static_cast<void*>(&depth));
Backtrace result(depth, kSkipFrames);
if (result.obj()->array()->count_ > 0) {
_Unwind_Backtrace(unwindCallback, &result);
CallWithThreadState<ThreadState::kNative>(_Unwind_Backtrace, unwindCallback, static_cast<void*>(&result));
}
RETURN_OBJ(result.obj());
#else
const int maxSize = 32;
void* buffer[maxSize];
int size = backtrace(buffer, maxSize);
int size = kotlin::CallWithThreadState<kotlin::ThreadState::kNative>(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<int32_t>(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<ThreadState::kNative>(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<KNativePtr>(stackTrace->array(), 0), size);
char **symbols = CallWithThreadState<ThreadState::kNative>(
backtrace_symbols, PrimitiveArrayAddressOfElementAt<KNativePtr>(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<ThreadState::kNative>(getSourceInfo, stackTrace, index);
const char* symbol = symbols[index];
const char* result;
char line[1024];
+22 -21
View File
@@ -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 <typename R, typename... Args>
ALWAYS_INLINE inline R CallKotlin(R(*kotlinFunction)(Args...), Args... args) {
ThreadStateGuard guard(ThreadState::kRunnable);
return kotlinFunction(std::forward<Args>(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 <typename... Args>
ALWAYS_INLINE RUNTIME_NORETURN inline void CallKotlinNoReturn(void(*noreturnKotlinFunction)(Args...), Args... args) {
CallKotlin(noreturnKotlinFunction, std::forward<Args>(args)...);
RuntimeFail("The function must not return");
template <ThreadState state, typename R, typename... Args>
ALWAYS_INLINE inline R CallWithThreadState(R(*function)(Args...), Args... args) {
ThreadStateGuard guard(state);
return function(std::forward<Args>(args)...);
}
} // namespace kotlin
+10 -2
View File
@@ -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"
@@ -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<void(MemoryState*)> f) {
std::thread([&f]() {
ScopedRuntimeInit init;
ScopedMemoryInit init;
f(init.memoryState());
}).join();
}
+139 -68
View File
@@ -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<Job> 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<ThreadState::kNative>(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<ThreadState::kNative>(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 ? &microsecondsPassed : nullptr);
waitInNativeState(&cond_, &lock_, nsDelta, remaining ? &microsecondsPassed : 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");
+1 -1
View File
@@ -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);
@@ -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
/**
@@ -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<T> {
*
* 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<T> {
*
* 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<T> {
return IteratorImpl(this)
}
@SymbolName("Kotlin_Array_getArrayLength")
@GCUnsafeCall("Kotlin_Array_getArrayLength")
external private fun getArrayLength(): Int
}
@@ -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 <reified @PureReifiable T> arrayOfNulls(size: Int): Array<T?>
@PointsTo(0x00, 0x01) // ret -> elements
public external inline fun <reified @PureReifiable T> arrayOf(vararg elements: T): Array<T>
@SymbolName("Kotlin_emptyArray")
@GCUnsafeCall("Kotlin_emptyArray")
external public fun <T> emptyArray(): Array<T>
/**
@@ -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<Char> {
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 {
@@ -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
/**
@@ -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<Byte> {
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<Short> {
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<Int> {
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<Long> {
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<Float> {
* 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<Float> {
* 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<Double> {
* 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<Double> {
* 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].
@@ -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<String>, 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<String>, 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 =
@@ -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<Throwable>? = 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<String>
/**
@@ -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 <E> Array<E>.resetAt(index: Int) {
(@Suppress("UNCHECKED_CAST")(this as Array<Any?>))[index] = null
}
@SymbolName("Kotlin_Array_fillImpl")
@GCUnsafeCall("Kotlin_Array_fillImpl")
@PointsTo(0x3000, 0x0000, 0x0000, 0x0000) // array.intestines -> value
internal external fun <T> arrayFill(array: Array<T>, 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 <E> Array<E>.resetRange(fromIndex: Int, toIndex: Int) {
arrayFill(@Suppress("UNCHECKED_CAST") (this as Array<Any?>), 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<Any?>, fromIndex: Int, destination: Array<Any?>, 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)
@@ -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?
@@ -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
/**
@@ -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<ByteVar> =
public fun ImmutableBlob.asUCPointer(offset: Int = 0): CPointer<UByteVar> =
interpretCPointer<UByteVar>(asCPointerImpl(offset))!!
@SymbolName("Kotlin_ImmutableBlob_asCPointerImpl")
@GCUnsafeCall("Kotlin_ImmutableBlob_asCPointerImpl")
private external fun ImmutableBlob.asCPointerImpl(offset: Int): kotlin.native.internal.NativePtr
/**
@@ -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
@@ -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
@@ -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)
@@ -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<T> {
* @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<T> {
* @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<T> {
}
// 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<T>(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
}
@@ -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()
@@ -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<CFunction<*>>): Future<Any?> =
Future<Any?>(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<CFunction<*>>): 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)
@@ -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?
/**
@@ -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
/**
@@ -150,4 +150,17 @@ internal annotation class InternalForKotlinNative
* Marks a class that has a freeze hook.
*/
@Target(AnnotationTarget.CLASS)
internal annotation class HasFreezeHook
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)
@@ -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.
@@ -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
@@ -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
@@ -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
/**
@@ -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<Any>?
/**
* 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<Any>?
@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)
}
@@ -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
/*
@@ -68,22 +68,22 @@ internal class KClassUnsupportedImpl(private val message: String) : KClass<Any>
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 <reified T : Any> 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
@@ -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
}
@@ -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
/**
@@ -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
@@ -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 <T> undefined(): T
@@ -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
@@ -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
@@ -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
@@ -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
/**
@@ -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
/**
@@ -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
@@ -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
@@ -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 {
@@ -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
// =============================================================================================================
@@ -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. */
@@ -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 {
@@ -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.
@@ -31,36 +31,26 @@ class InitSingletonTest : public testing::Test {
public:
InitSingletonTest() {
globalConstructor_ = &constructor_;
for (auto& threadData : threadDatas_) {
threadData = make_unique<mm::ThreadData>(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<void(ObjHeader*)>& 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<testing::MockFunction<void(ObjHeader*)>> constructor_;
// TODO: It makes sense to somehow abstract `ThreadData` stuff away. Allocation in this case.
std::array<KStdUniquePtr<mm::ThreadData>, kDefaultThreadCount> threadDatas_;
test_support::TypeInfoHolder type_{test_support::TypeInfoHolder::ObjectBuilder<EmptyPayload>()};
static testing::MockFunction<void(ObjHeader*)>* globalConstructor_;
@@ -74,119 +64,136 @@ testing::MockFunction<void(ObjHeader*)>* 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();
});
}
+9 -2
View File
@@ -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<mm::StableRefRegistry::Node*>(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();
}
@@ -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<uint32_t>(elements));
// `ArrayHeader` and `ObjHeader` are expected to be compatible.
@@ -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 <typename T>
bool isEmpty(T& iterable) {
return iterable.begin() == iterable.end();
}
template <typename E, typename T>
std::vector<E> collect(T& iterable) {
std::vector<E> result;
for (E element : iterable) {
result.push_back(element);
}
return std::move(result);
}
std::vector<mm::ThreadData*> collect(mm::ThreadRegistry::Iterable& iterable) {
std::vector<mm::ThreadData*> 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<ObjHeader**>(globals), testing::UnorderedElementsAre());
EXPECT_THAT(collect<mm::ObjectFactory<gc::GC>::NodeRef>(objects), testing::UnorderedElementsAre());
EXPECT_THAT(collect<ObjHeader*>(stableRefs), testing::UnorderedElementsAre());
EXPECT_THAT(collect(threads), testing::UnorderedElementsAre());
}
void kotlin::DeinitMemoryForTests(MemoryState* memoryState) {
DeinitMemory(memoryState, false);
mm::ThreadRegistry::TestSupport::ClearCurrentThreadData();
}
@@ -16,7 +16,6 @@
#include "ShadowStack.hpp"
#include "StableRefRegistry.hpp"
#include "ThreadLocalStorage.hpp"
#include "ThreadState.hpp"
#include "Types.h"
#include "Utils.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;
@@ -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());
});
@@ -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();
}
@@ -9,13 +9,37 @@
#include <Common.h>
#include <Utils.hpp>
#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
@@ -20,30 +20,26 @@ namespace {
class ThreadStateTest : public testing::Test {
public:
ThreadStateTest() {
globalKotlinFunctionMock = &kotlinFunctionMock_;
globalSomeFunctionMock = &someFunctionMock();
}
~ThreadStateTest() {
globalKotlinFunctionMock = nullptr;
globalSomeFunctionMock = nullptr;
}
testing::MockFunction<int32_t(int32_t)>& kotlinFunctionMock() { return kotlinFunctionMock_; }
testing::MockFunction<int32_t(int32_t)>& 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<int32_t(int32_t)> kotlinFunctionMock_;
static testing::MockFunction<int32_t(int32_t)>* globalKotlinFunctionMock;
testing::MockFunction<int32_t(int32_t)> someFunctionMock_;
static testing::MockFunction<int32_t(int32_t)>* globalSomeFunctionMock;
};
//static
testing::MockFunction<int32_t(int32_t)>* ThreadStateTest::globalKotlinFunctionMock = nullptr;
testing::MockFunction<int32_t(int32_t)>* 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<ThreadState::kNative>(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<ThreadState::kRunnable>(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.")));
});
}
@@ -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();
}