KT-51297: Allow suspend from any thread from ObjC
This patch adds a binary compiler argument to allow not crashing if a suspend function is called from a non-Main dispatcher from ObjC or Swift. This is relevant for people using the new memory model, in which this restriction may be relaxed.
This commit is contained in:
committed by
SvyatoslavScherbina
parent
2dac366dbc
commit
93cb4d71d8
+2
@@ -26,6 +26,8 @@ object BinaryOptions : BinaryOptionRegistry() {
|
|||||||
|
|
||||||
val unitSuspendFunctionObjCExport by option<UnitSuspendFunctionObjCExport>()
|
val unitSuspendFunctionObjCExport by option<UnitSuspendFunctionObjCExport>()
|
||||||
|
|
||||||
|
val objcExportSuspendFunctionLaunchThreadRestriction by option<ObjCExportSuspendFunctionLaunchThreadRestriction>()
|
||||||
|
|
||||||
val gcSchedulerType by option<GCSchedulerType>()
|
val gcSchedulerType by option<GCSchedulerType>()
|
||||||
|
|
||||||
val linkRuntime by option<RuntimeLinkageStrategyBinaryOption>()
|
val linkRuntime by option<RuntimeLinkageStrategyBinaryOption>()
|
||||||
|
|||||||
+1
@@ -112,6 +112,7 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration
|
|||||||
else -> WorkerExceptionHandling.LEGACY
|
else -> WorkerExceptionHandling.LEGACY
|
||||||
}
|
}
|
||||||
val runtimeLogs: String? get() = configuration.get(KonanConfigKeys.RUNTIME_LOGS)
|
val runtimeLogs: String? get() = configuration.get(KonanConfigKeys.RUNTIME_LOGS)
|
||||||
|
val suspendFunctionsFromAnyThreadFromObjC: Boolean by lazy { configuration.get(BinaryOptions.objcExportSuspendFunctionLaunchThreadRestriction) == ObjCExportSuspendFunctionLaunchThreadRestriction.NONE }
|
||||||
val freezing: Freezing by lazy {
|
val freezing: Freezing by lazy {
|
||||||
val freezingMode = configuration.get(BinaryOptions.freezing)
|
val freezingMode = configuration.get(BinaryOptions.freezing)
|
||||||
when {
|
when {
|
||||||
|
|||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2022 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.backend.konan
|
||||||
|
|
||||||
|
enum class ObjCExportSuspendFunctionLaunchThreadRestriction {
|
||||||
|
/**
|
||||||
|
* In this mode, suspend functions called from ObjC/Swift may only be called from the main thread
|
||||||
|
*/
|
||||||
|
MAIN,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In this mode, suspend functions called from ObjC/Swift may be called from any thread
|
||||||
|
*/
|
||||||
|
NONE,
|
||||||
|
;
|
||||||
|
}
|
||||||
+1
@@ -2775,6 +2775,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
|||||||
overrideRuntimeGlobal("Kotlin_workerExceptionHandling", Int32(context.config.workerExceptionHandling.value))
|
overrideRuntimeGlobal("Kotlin_workerExceptionHandling", Int32(context.config.workerExceptionHandling.value))
|
||||||
overrideRuntimeGlobal("Kotlin_freezingEnabled", Int32(if (context.config.freezing.enableFreezeAtRuntime) 1 else 0))
|
overrideRuntimeGlobal("Kotlin_freezingEnabled", Int32(if (context.config.freezing.enableFreezeAtRuntime) 1 else 0))
|
||||||
overrideRuntimeGlobal("Kotlin_freezingChecksEnabled", Int32(if (context.config.freezing.enableFreezeChecks) 1 else 0))
|
overrideRuntimeGlobal("Kotlin_freezingChecksEnabled", Int32(if (context.config.freezing.enableFreezeChecks) 1 else 0))
|
||||||
|
overrideRuntimeGlobal("Kotlin_suspendFunctionsFromAnyThreadFromObjC", Int32(if (context.config.suspendFunctionsFromAnyThreadFromObjC) 1 else 0))
|
||||||
overrideRuntimeGlobal("Kotlin_gcSchedulerType", Int32(context.config.gcSchedulerType.value))
|
overrideRuntimeGlobal("Kotlin_gcSchedulerType", Int32(context.config.gcSchedulerType.value))
|
||||||
val getSourceInfoFunctionName = when (context.config.sourceInfoType) {
|
val getSourceInfoFunctionName = when (context.config.sourceInfoType) {
|
||||||
SourceInfoType.NOOP -> null
|
SourceInfoType.NOOP -> null
|
||||||
|
|||||||
@@ -5564,8 +5564,10 @@ if (isAppleTarget(project)) {
|
|||||||
artifact = frameworkArtifactName
|
artifact = frameworkArtifactName
|
||||||
library = libraryName
|
library = libraryName
|
||||||
isStatic = true
|
isStatic = true
|
||||||
|
opts = ["-Xbinary=objcExportSuspendFunctionLaunchThreadRestriction=none"]
|
||||||
}
|
}
|
||||||
swiftSources = ['objcexport']
|
swiftSources = ['objcexport']
|
||||||
|
swiftExtraOpts = [ '-D', 'ALLOW_SUSPEND_ANY_THREAD' ]
|
||||||
if (isNoopGC) {
|
if (isNoopGC) {
|
||||||
swiftExtraOpts += ["-D", "NOOP_GC"]
|
swiftExtraOpts += ["-D", "NOOP_GC"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,6 +48,41 @@ private func testUnitCallSimple() throws {
|
|||||||
try assertNil(error)
|
try assertNil(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func testUnitCallNonMainDispatcher() throws {
|
||||||
|
#if LEGACY_SUSPEND_UNIT_FUNCTION_EXPORT
|
||||||
|
var result: KotlinUnit? = nil
|
||||||
|
#endif
|
||||||
|
var error: Error? = nil
|
||||||
|
var completionCalled = 0
|
||||||
|
|
||||||
|
let group = DispatchGroup()
|
||||||
|
|
||||||
|
#if ALLOW_SUSPEND_ANY_THREAD
|
||||||
|
#if LEGACY_SUSPEND_UNIT_FUNCTION_EXPORT
|
||||||
|
DispatchQueue.global().async(group: group) {
|
||||||
|
CoroutinesKt.unitSuspendFun { _result, _error in
|
||||||
|
completionCalled += 1
|
||||||
|
result = _result
|
||||||
|
error = _error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
DispatchQueue.global().async(group: group) {
|
||||||
|
CoroutinesKt.unitSuspendFun { _error in
|
||||||
|
completionCalled += 1
|
||||||
|
error = _error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if ALLOW_SUSPEND_ANY_THREAD
|
||||||
|
group.wait()
|
||||||
|
try assertEquals(actual: completionCalled, expected: 1)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private func testCallSuspendFun(doSuspend: Bool, doThrow: Bool) throws {
|
private func testCallSuspendFun(doSuspend: Bool, doThrow: Bool) throws {
|
||||||
class C {}
|
class C {}
|
||||||
let expectedResult = C()
|
let expectedResult = C()
|
||||||
@@ -630,6 +665,7 @@ class CoroutinesTests : SimpleTestProvider {
|
|||||||
|
|
||||||
test("TestCallSimple", testCallSimple)
|
test("TestCallSimple", testCallSimple)
|
||||||
test("TestCallUnitSimple", testUnitCallSimple)
|
test("TestCallUnitSimple", testUnitCallSimple)
|
||||||
|
test("TestCallFromNonMainDispatcher", testUnitCallNonMainDispatcher)
|
||||||
test("TestCall", testCall)
|
test("TestCall", testCall)
|
||||||
test("TestCallChain", testCallChain)
|
test("TestCallChain", testCallChain)
|
||||||
test("TestOverride", testOverride)
|
test("TestOverride", testOverride)
|
||||||
@@ -640,4 +676,4 @@ class CoroutinesTests : SimpleTestProvider {
|
|||||||
test("TestKSuspendFunctionType", testSuspendFunctionType)
|
test("TestKSuspendFunctionType", testSuspendFunctionType)
|
||||||
test("TestSuspendFunctionSwiftImpl", testSuspendFunctionSwiftImpl)
|
test("TestSuspendFunctionSwiftImpl", testSuspendFunctionSwiftImpl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ RUNTIME_WEAK int32_t Kotlin_gcSchedulerType = 2;
|
|||||||
RUNTIME_WEAK int32_t Kotlin_workerExceptionHandling = 0;
|
RUNTIME_WEAK int32_t Kotlin_workerExceptionHandling = 0;
|
||||||
RUNTIME_WEAK int32_t Kotlin_freezingEnabled = 1;
|
RUNTIME_WEAK int32_t Kotlin_freezingEnabled = 1;
|
||||||
RUNTIME_WEAK int32_t Kotlin_freezingChecksEnabled = 1;
|
RUNTIME_WEAK int32_t Kotlin_freezingChecksEnabled = 1;
|
||||||
|
RUNTIME_WEAK int32_t Kotlin_suspendFunctionsFromAnyThreadFromObjC = 0;
|
||||||
RUNTIME_WEAK const Kotlin_getSourceInfo_FunctionType Kotlin_getSourceInfo_Function = nullptr;
|
RUNTIME_WEAK const Kotlin_getSourceInfo_FunctionType Kotlin_getSourceInfo_Function = nullptr;
|
||||||
#ifdef KONAN_ANDROID
|
#ifdef KONAN_ANDROID
|
||||||
RUNTIME_WEAK int32_t Kotlin_printToAndroidLogcat = 1;
|
RUNTIME_WEAK int32_t Kotlin_printToAndroidLogcat = 1;
|
||||||
@@ -37,6 +38,10 @@ ALWAYS_INLINE bool compiler::freezingChecksEnabled() noexcept {
|
|||||||
return Kotlin_freezingChecksEnabled != 0;
|
return Kotlin_freezingChecksEnabled != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE bool compiler::suspendFunctionsFromAnyThreadFromObjCEnabled() noexcept {
|
||||||
|
return Kotlin_suspendFunctionsFromAnyThreadFromObjC != 0;
|
||||||
|
}
|
||||||
|
|
||||||
ALWAYS_INLINE compiler::GCSchedulerType compiler::getGCSchedulerType() noexcept {
|
ALWAYS_INLINE compiler::GCSchedulerType compiler::getGCSchedulerType() noexcept {
|
||||||
return static_cast<compiler::GCSchedulerType>(Kotlin_gcSchedulerType);
|
return static_cast<compiler::GCSchedulerType>(Kotlin_gcSchedulerType);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ ALWAYS_INLINE inline std::string_view runtimeLogs() noexcept {
|
|||||||
|
|
||||||
bool freezingEnabled() noexcept;
|
bool freezingEnabled() noexcept;
|
||||||
bool freezingChecksEnabled() noexcept;
|
bool freezingChecksEnabled() noexcept;
|
||||||
|
bool suspendFunctionsFromAnyThreadFromObjCEnabled() noexcept;
|
||||||
|
|
||||||
|
|
||||||
ALWAYS_INLINE inline int getSourceInfo(void* addr, SourceInfo *result, int result_size) {
|
ALWAYS_INLINE inline int getSourceInfo(void* addr, SourceInfo *result, int result_size) {
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ extern "C" OBJ_GETTER(Kotlin_ObjCExport_createContinuationArgumentImpl,
|
|||||||
KRef completionHolder, const TypeInfo** exceptionTypes);
|
KRef completionHolder, const TypeInfo** exceptionTypes);
|
||||||
|
|
||||||
extern "C" OBJ_GETTER(Kotlin_ObjCExport_createContinuationArgument, id completion, const TypeInfo** exceptionTypes) {
|
extern "C" OBJ_GETTER(Kotlin_ObjCExport_createContinuationArgument, id completion, const TypeInfo** exceptionTypes) {
|
||||||
if (pthread_main_np() != 1) {
|
if (!kotlin::compiler::suspendFunctionsFromAnyThreadFromObjCEnabled() && pthread_main_np() != 1) {
|
||||||
[NSException raise:NSGenericException
|
[NSException raise:NSGenericException
|
||||||
format:@"Calling Kotlin suspend functions from Swift/Objective-C is currently supported only on main thread"];
|
format:@"Calling Kotlin suspend functions from Swift/Objective-C is currently supported only on main thread"];
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user