From b31f2c825d0c39de89b68c60e1d502e0e0c0f083 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Wed, 29 Jun 2022 17:32:23 +0200 Subject: [PATCH] Reduce reachable references of disposed invokeOnTimeout handle Fixes #3351 --- .../native/src/MultithreadedDispatchers.kt | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/kotlinx-coroutines-core/native/src/MultithreadedDispatchers.kt b/kotlinx-coroutines-core/native/src/MultithreadedDispatchers.kt index d991fc169e..36722e3823 100644 --- a/kotlinx-coroutines-core/native/src/MultithreadedDispatchers.kt +++ b/kotlinx-coroutines-core/native/src/MultithreadedDispatchers.kt @@ -35,9 +35,24 @@ 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 + 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() {