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:
Ahmed El-Helw
2022-05-19 22:32:27 +04:00
committed by SvyatoslavScherbina
parent 2dac366dbc
commit 93cb4d71d8
9 changed files with 69 additions and 2 deletions
@@ -26,6 +26,8 @@ object BinaryOptions : BinaryOptionRegistry() {
val unitSuspendFunctionObjCExport by option<UnitSuspendFunctionObjCExport>()
val objcExportSuspendFunctionLaunchThreadRestriction by option<ObjCExportSuspendFunctionLaunchThreadRestriction>()
val gcSchedulerType by option<GCSchedulerType>()
val linkRuntime by option<RuntimeLinkageStrategyBinaryOption>()
@@ -112,6 +112,7 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration
else -> WorkerExceptionHandling.LEGACY
}
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 freezingMode = configuration.get(BinaryOptions.freezing)
when {
@@ -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,
;
}
@@ -2775,6 +2775,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
overrideRuntimeGlobal("Kotlin_workerExceptionHandling", Int32(context.config.workerExceptionHandling.value))
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_suspendFunctionsFromAnyThreadFromObjC", Int32(if (context.config.suspendFunctionsFromAnyThreadFromObjC) 1 else 0))
overrideRuntimeGlobal("Kotlin_gcSchedulerType", Int32(context.config.gcSchedulerType.value))
val getSourceInfoFunctionName = when (context.config.sourceInfoType) {
SourceInfoType.NOOP -> null
@@ -5564,8 +5564,10 @@ if (isAppleTarget(project)) {
artifact = frameworkArtifactName
library = libraryName
isStatic = true
opts = ["-Xbinary=objcExportSuspendFunctionLaunchThreadRestriction=none"]
}
swiftSources = ['objcexport']
swiftExtraOpts = [ '-D', 'ALLOW_SUSPEND_ANY_THREAD' ]
if (isNoopGC) {
swiftExtraOpts += ["-D", "NOOP_GC"]
}
@@ -48,6 +48,41 @@ private func testUnitCallSimple() throws {
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 {
class C {}
let expectedResult = C()
@@ -630,6 +665,7 @@ class CoroutinesTests : SimpleTestProvider {
test("TestCallSimple", testCallSimple)
test("TestCallUnitSimple", testUnitCallSimple)
test("TestCallFromNonMainDispatcher", testUnitCallNonMainDispatcher)
test("TestCall", testCall)
test("TestCallChain", testCallChain)
test("TestOverride", testOverride)
@@ -640,4 +676,4 @@ class CoroutinesTests : SimpleTestProvider {
test("TestKSuspendFunctionType", testSuspendFunctionType)
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_freezingEnabled = 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;
#ifdef KONAN_ANDROID
RUNTIME_WEAK int32_t Kotlin_printToAndroidLogcat = 1;
@@ -37,6 +38,10 @@ ALWAYS_INLINE bool compiler::freezingChecksEnabled() noexcept {
return Kotlin_freezingChecksEnabled != 0;
}
ALWAYS_INLINE bool compiler::suspendFunctionsFromAnyThreadFromObjCEnabled() noexcept {
return Kotlin_suspendFunctionsFromAnyThreadFromObjC != 0;
}
ALWAYS_INLINE compiler::GCSchedulerType compiler::getGCSchedulerType() noexcept {
return static_cast<compiler::GCSchedulerType>(Kotlin_gcSchedulerType);
}
@@ -80,6 +80,7 @@ ALWAYS_INLINE inline std::string_view runtimeLogs() noexcept {
bool freezingEnabled() noexcept;
bool freezingChecksEnabled() noexcept;
bool suspendFunctionsFromAnyThreadFromObjCEnabled() noexcept;
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);
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
format:@"Calling Kotlin suspend functions from Swift/Objective-C is currently supported only on main thread"];
}