From 793403272dde4f992d3e513d67e4d6d2c1fc5104 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Thu, 30 Jun 2022 15:29:35 +0200 Subject: [PATCH] Reduce reachable references of disposed invokeOnTimeout handle (#3353) * Reduce reachable references of disposed invokeOnTimeout handle Fixes #3351 --- .../native/src/MultithreadedDispatchers.kt | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/kotlinx-coroutines-core/native/src/MultithreadedDispatchers.kt b/kotlinx-coroutines-core/native/src/MultithreadedDispatchers.kt index d991fc169e..1c1306c291 100644 --- a/kotlinx-coroutines-core/native/src/MultithreadedDispatchers.kt +++ b/kotlinx-coroutines-core/native/src/MultithreadedDispatchers.kt @@ -35,9 +35,25 @@ internal class WorkerDispatcher(name: String) : CloseableCoroutineDispatcher(), } override fun invokeOnTimeout(timeMillis: Long, block: Runnable, context: CoroutineContext): DisposableHandle { - // No API to cancel on timeout - worker.executeAfter(timeMillis.toMicrosSafe()) { block.run() } - return NonDisposableHandle + // Workers don't have an API to cancel sent "executeAfter" block, but we are trying + // to control the damage and reduce reachable objects by nulling out `block` + // that may retain a lot of references, and leaving only an empty shell after a timely disposal + // This is a class and not an object with `block` in a closure because that would defeat the purpose. + class DisposableBlock(block: Runnable) : DisposableHandle, Function0 { + private val disposableHolder = AtomicReference(block) + + override fun invoke() { + disposableHolder.value?.run() + } + + override fun dispose() { + disposableHolder.value = null + } + } + + val disposableBlock = DisposableBlock(block) + worker.executeAfter(timeMillis.toMicrosSafe(), disposableBlock) + return disposableBlock } override fun close() {