From edc2b5e285017380be7428117f863635b6db7641 Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Wed, 17 Nov 2021 15:48:27 +0300 Subject: [PATCH] Use Dispatchers.Main as default delay source where applicable (#2974) * Use Dispatchers.Main as default delay source where applicable It reduces the number of redundant threads in the system and makes time source predictable across Android/JavaFx applications Fixes #2972 --- kotlinx-coroutines-core/common/src/Delay.kt | 2 -- .../jvm/src/DefaultExecutor.kt | 17 ++++++++++++++++- .../src/JavaFxDispatcher.kt | 12 ++++-------- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/kotlinx-coroutines-core/common/src/Delay.kt b/kotlinx-coroutines-core/common/src/Delay.kt index 35e83f3aba..d95dc52f9f 100644 --- a/kotlinx-coroutines-core/common/src/Delay.kt +++ b/kotlinx-coroutines-core/common/src/Delay.kt @@ -51,8 +51,6 @@ public interface Delay { * Schedules invocation of a specified [block] after a specified delay [timeMillis]. * The resulting [DisposableHandle] can be used to [dispose][DisposableHandle.dispose] of this invocation * request if it is not needed anymore. - * - * This implementation uses a built-in single-threaded scheduled executor service. */ public fun invokeOnTimeout(timeMillis: Long, block: Runnable, context: CoroutineContext): DisposableHandle = DefaultDelay.invokeOnTimeout(timeMillis, block, context) diff --git a/kotlinx-coroutines-core/jvm/src/DefaultExecutor.kt b/kotlinx-coroutines-core/jvm/src/DefaultExecutor.kt index 6457d26e0a..7b0810c2a1 100644 --- a/kotlinx-coroutines-core/jvm/src/DefaultExecutor.kt +++ b/kotlinx-coroutines-core/jvm/src/DefaultExecutor.kt @@ -4,10 +4,25 @@ package kotlinx.coroutines +import kotlinx.coroutines.internal.* import java.util.concurrent.* import kotlin.coroutines.* -internal actual val DefaultDelay: Delay = DefaultExecutor +internal actual val DefaultDelay: Delay = initializeDefaultDelay() + +private val defaultMainDelayOptIn = systemProp("kotlinx.coroutines.main.delay", true) + +private fun initializeDefaultDelay(): Delay { + // Opt-out flag + if (!defaultMainDelayOptIn) return DefaultExecutor + val main = Dispatchers.Main + /* + * When we already are working with UI and Main threads, it makes + * no sense to create a separate thread with timer that cannot be controller + * by the UI runtime. + */ + return if (main.isMissing() || main !is Delay) DefaultExecutor else main +} @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") internal actual object DefaultExecutor : EventLoopImplBase(), Runnable { diff --git a/ui/kotlinx-coroutines-javafx/src/JavaFxDispatcher.kt b/ui/kotlinx-coroutines-javafx/src/JavaFxDispatcher.kt index 6915c53972..d158fb745a 100644 --- a/ui/kotlinx-coroutines-javafx/src/JavaFxDispatcher.kt +++ b/ui/kotlinx-coroutines-javafx/src/JavaFxDispatcher.kt @@ -34,22 +34,18 @@ public sealed class JavaFxDispatcher : MainCoroutineDispatcher(), Delay { /** @suppress */ override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation) { - val timeline = schedule(timeMillis, TimeUnit.MILLISECONDS, EventHandler { + val timeline = schedule(timeMillis, TimeUnit.MILLISECONDS) { with(continuation) { resumeUndispatched(Unit) } - }) + } continuation.invokeOnCancellation { timeline.stop() } } /** @suppress */ override fun invokeOnTimeout(timeMillis: Long, block: Runnable, context: CoroutineContext): DisposableHandle { - val timeline = schedule(timeMillis, TimeUnit.MILLISECONDS, EventHandler { + val timeline = schedule(timeMillis, TimeUnit.MILLISECONDS) { block.run() - }) - return object : DisposableHandle { - override fun dispose() { - timeline.stop() - } } + return DisposableHandle { timeline.stop() } } private fun schedule(time: Long, unit: TimeUnit, handler: EventHandler): Timeline =