diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BinaryOptions.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BinaryOptions.kt index d851b12e930..11c30e60912 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BinaryOptions.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BinaryOptions.kt @@ -65,6 +65,8 @@ object BinaryOptions : BinaryOptionRegistry() { val objcDisposeOnMain by booleanOption() + val objcDisposeWithRunLoop by booleanOption() + val disableMmap by booleanOption() val disableAllocatorOverheadEstimate by booleanOption() diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt index 85797aefec9..89cc40086a1 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt @@ -245,6 +245,10 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration configuration.get(BinaryOptions.objcDisposeOnMain) ?: true } + val objcDisposeWithRunLoop: Boolean by lazy { + configuration.get(BinaryOptions.objcDisposeWithRunLoop) ?: true + } + val enableSafepointSignposts: Boolean = configuration.get(BinaryOptions.enableSafepointSignposts)?.also { if (it && !target.supportsSignposts) { configuration.report(CompilerMessageSeverity.STRONG_WARNING, "Signposts are not available on $target. The setting will have no effect.") diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt index 1e8f5764a5a..29df9b96a29 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt @@ -2817,6 +2817,7 @@ internal class CodeGeneratorVisitor( overrideRuntimeGlobal("Kotlin_mimallocUseDefaultOptions", llvm.constInt32(if (context.config.mimallocUseDefaultOptions) 1 else 0)) overrideRuntimeGlobal("Kotlin_mimallocUseCompaction", llvm.constInt32(if (context.config.mimallocUseCompaction) 1 else 0)) overrideRuntimeGlobal("Kotlin_objcDisposeOnMain", llvm.constInt32(if (context.config.objcDisposeOnMain) 1 else 0)) + overrideRuntimeGlobal("Kotlin_objcDisposeWithRunLoop", llvm.constInt32(if (context.config.objcDisposeWithRunLoop) 1 else 0)) overrideRuntimeGlobal("Kotlin_enableSafepointSignposts", llvm.constInt32(if (context.config.enableSafepointSignposts) 1 else 0)) } diff --git a/kotlin-native/runtime/src/gc/common/cpp/FinalizerProcessor.hpp b/kotlin-native/runtime/src/gc/common/cpp/FinalizerProcessor.hpp index 363ba249163..252d8ddc474 100644 --- a/kotlin-native/runtime/src/gc/common/cpp/FinalizerProcessor.hpp +++ b/kotlin-native/runtime/src/gc/common/cpp/FinalizerProcessor.hpp @@ -32,7 +32,7 @@ public: // epochDoneCallback could be called on any subset of them. // If no new tasks are set, epochDoneCallback will be eventually called on last epoch explicit FinalizerProcessor(std::function epochDoneCallback) noexcept : - epochDoneCallback_(std::move(epochDoneCallback)), processingLoop_(*this) {} + epochDoneCallback_(std::move(epochDoneCallback)), processingLoop_(makeProcessingLoop(*this)) {} ~FinalizerProcessor() { StopFinalizerThread(); } @@ -46,7 +46,7 @@ public: StartFinalizerThreadIfNone(); FinalizerQueueTraits::add(finalizerQueue_, std::move(tasks)); finalizerQueueEpoch_ = epoch; - processingLoop_.notify(); + processingLoop_->notify(); } void StopFinalizerThread() noexcept { @@ -54,7 +54,7 @@ public: std::unique_lock guard(finalizerQueueMutex_); if (!finalizerThread_.joinable()) return; shutdownFlag_ = true; - processingLoop_.notify(); + processingLoop_->notify(); } finalizerThread_.join(); shutdownFlag_ = false; @@ -71,14 +71,14 @@ public: if (finalizerThread_.joinable()) return; finalizerThread_ = ScopedThread(ScopedThread::attributes().name("GC finalizer processor"), [this] { - processingLoop_.initThreadData(); + processingLoop_->initThreadData(); Kotlin_initRuntimeIfNeeded(); { std::unique_lock guard(initializedMutex_); initialized_ = true; } initializedCondVar_.notify_all(); - processingLoop_.body(); + processingLoop_->body(); { std::unique_lock guard(initializedMutex_); initialized_ = false; @@ -109,24 +109,33 @@ private: epochDoneCallback_(currentEpoch); } -#if KONAN_OBJC_INTEROP - class ProcessingLoop { + class ProcessingLoop : private Pinned { public: - explicit ProcessingLoop(FinalizerProcessor& owner) : + virtual ~ProcessingLoop() = default; + + virtual void notify() = 0; + virtual void initThreadData() = 0; + virtual void body() = 0; + }; + +#if KONAN_OBJC_INTEROP + class ProcessingLoopWithCFImpl final : public ProcessingLoop { + public: + explicit ProcessingLoopWithCFImpl(FinalizerProcessor& owner) : owner_(owner), sourceContext_{ 0, this, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, [](void* info) { - auto& self = *reinterpret_cast(info); + auto& self = *static_cast(info); self.handleNewFinalizers(); }}, runLoopSource_(CFRunLoopSourceCreate(nullptr, 0, &sourceContext_)) {} - ~ProcessingLoop() { + ~ProcessingLoopWithCFImpl() override { CFRelease(runLoopSource_); } - void notify() { + void notify() override { // wait until runLoop_ ptr is published while (runLoop_.load(std::memory_order_acquire) == nullptr) { std::this_thread::yield(); @@ -136,11 +145,11 @@ private: CFRunLoopWakeUp(runLoop_); } - void initThreadData() { + void initThreadData() override { runLoop_.store(CFRunLoopGetCurrent(), std::memory_order_release); } - void body() { + void body() override { konan::AutoreleasePool autoreleasePool; auto mode = kCFRunLoopDefaultMode; CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource_, mode); @@ -150,6 +159,7 @@ private: CFRunLoopRemoveSource(CFRunLoopGetCurrent(), runLoopSource_, mode); runLoop_.store(nullptr, std::memory_order_release); } + private: void handleNewFinalizers() { std::unique_lock lock(owner_.finalizerQueueMutex_); @@ -172,18 +182,19 @@ private: std::atomic runLoop_ = nullptr; CFRunLoopSourceRef runLoopSource_; }; -#else - class ProcessingLoop { - public: - explicit ProcessingLoop(FinalizerProcessor& owner) : owner_(owner) {} +#endif - void notify() { + class ProcessingLoopImpl final : public ProcessingLoop { + public: + explicit ProcessingLoopImpl(FinalizerProcessor& owner) : owner_(owner) {} + + void notify() override { owner_.finalizerQueueCondVar_.notify_all(); } - void initThreadData() { /* noop */ } + void initThreadData() override { /* noop */ } - void body() { + void body() override { int64_t finishedEpoch = 0; while (true) { std::unique_lock lock(owner_.finalizerQueueMutex_); @@ -202,10 +213,19 @@ private: finishedEpoch = currentEpoch; } } + private: FinalizerProcessor& owner_; }; + + static std::unique_ptr makeProcessingLoop(FinalizerProcessor& owner) noexcept { +#if KONAN_OBJC_INTEROP + if (compiler::objcDisposeWithRunLoop()) { + return std::make_unique(owner); + } #endif + return std::make_unique(owner); + } ScopedThread finalizerThread_; FinalizerQueue finalizerQueue_; @@ -216,7 +236,7 @@ private: bool shutdownFlag_ = false; bool newTasksAllowed_ = true; - ProcessingLoop processingLoop_; + std::unique_ptr processingLoop_; std::mutex initializedMutex_; std::condition_variable initializedCondVar_; diff --git a/kotlin-native/runtime/src/main/cpp/CompilerConstants.cpp b/kotlin-native/runtime/src/main/cpp/CompilerConstants.cpp index 83575e9ec8e..f51f8a912bb 100644 --- a/kotlin-native/runtime/src/main/cpp/CompilerConstants.cpp +++ b/kotlin-native/runtime/src/main/cpp/CompilerConstants.cpp @@ -32,6 +32,7 @@ RUNTIME_WEAK int32_t Kotlin_appStateTracking = 0; RUNTIME_WEAK int32_t Kotlin_mimallocUseDefaultOptions = 1; RUNTIME_WEAK int32_t Kotlin_mimallocUseCompaction = 0; RUNTIME_WEAK int32_t Kotlin_objcDisposeOnMain = 0; +RUNTIME_WEAK int32_t Kotlin_objcDisposeWithRunLoop = 1; RUNTIME_WEAK int32_t Kotlin_enableSafepointSignposts = 0; ALWAYS_INLINE compiler::DestroyRuntimeMode compiler::destroyRuntimeMode() noexcept { @@ -85,6 +86,10 @@ ALWAYS_INLINE bool compiler::objcDisposeOnMain() noexcept { return Kotlin_objcDisposeOnMain != 0; } +ALWAYS_INLINE bool compiler::objcDisposeWithRunLoop() noexcept { + return Kotlin_objcDisposeWithRunLoop != 0; +} + ALWAYS_INLINE bool compiler::enableSafepointSignposts() noexcept { return Kotlin_enableSafepointSignposts != 0; } diff --git a/kotlin-native/runtime/src/main/cpp/CompilerConstants.hpp b/kotlin-native/runtime/src/main/cpp/CompilerConstants.hpp index ae0611f2852..83b76c55c88 100644 --- a/kotlin-native/runtime/src/main/cpp/CompilerConstants.hpp +++ b/kotlin-native/runtime/src/main/cpp/CompilerConstants.hpp @@ -113,6 +113,7 @@ int getSourceInfo(void* addr, SourceInfo *result, int result_size) noexcept; bool mimallocUseDefaultOptions() noexcept; bool mimallocUseCompaction() noexcept; bool objcDisposeOnMain() noexcept; +bool objcDisposeWithRunLoop() noexcept; bool enableSafepointSignposts() noexcept; #ifdef KONAN_ANDROID