diff --git a/kotlinx-coroutines-core/common/src/internal/MainDispatcherFactory.kt b/kotlinx-coroutines-core/common/src/internal/MainDispatcherFactory.kt index 0b8638687b..45872f179d 100644 --- a/kotlinx-coroutines-core/common/src/internal/MainDispatcherFactory.kt +++ b/kotlinx-coroutines-core/common/src/internal/MainDispatcherFactory.kt @@ -14,6 +14,11 @@ public interface MainDispatcherFactory { /** * Creates the main dispatcher. [allFactories] parameter contains all factories found by service loader. * This method is not guaranteed to be idempotent. + * + * It is required that this method fails with an exception instead of returning an instance that doesn't work + * correctly as a [Delay]. + * The reason for this is that, on the JVM, [DefaultDelay] will use [Dispatchers.Main] for most delays by default + * if this method returns an instance without throwing. */ public fun createDispatcher(allFactories: List): MainCoroutineDispatcher diff --git a/ui/kotlinx-coroutines-android/src/HandlerDispatcher.kt b/ui/kotlinx-coroutines-android/src/HandlerDispatcher.kt index ca8dd0d0ca..ffd5df060c 100644 --- a/ui/kotlinx-coroutines-android/src/HandlerDispatcher.kt +++ b/ui/kotlinx-coroutines-android/src/HandlerDispatcher.kt @@ -51,8 +51,10 @@ public sealed class HandlerDispatcher : MainCoroutineDispatcher(), Delay { internal class AndroidDispatcherFactory : MainDispatcherFactory { - override fun createDispatcher(allFactories: List) = - HandlerContext(Looper.getMainLooper().asHandler(async = true)) + override fun createDispatcher(allFactories: List): MainCoroutineDispatcher { + val mainLooper = Looper.getMainLooper() ?: throw IllegalStateException("The main looper is not available") + return HandlerContext(mainLooper.asHandler(async = true)) + } override fun hintOnError(): String = "For tests Dispatchers.setMain from kotlinx-coroutines-test module can be used" diff --git a/ui/kotlinx-coroutines-swing/src/SwingDispatcher.kt b/ui/kotlinx-coroutines-swing/src/SwingDispatcher.kt index d2d9b78658..3b43483dbc 100644 --- a/ui/kotlinx-coroutines-swing/src/SwingDispatcher.kt +++ b/ui/kotlinx-coroutines-swing/src/SwingDispatcher.kt @@ -74,6 +74,16 @@ private object ImmediateSwingDispatcher : SwingDispatcher() { * Dispatches execution onto Swing event dispatching thread and provides native [delay] support. */ internal object Swing : SwingDispatcher() { + + /* A workaround so that the dispatcher's initialization crashes with an exception if running in a headless + environment. This is needed so that this broken dispatcher is not used as the source of delays. */ + init { + Timer(1) { }.apply { + isRepeats = false + start() + } + } + override val immediate: MainCoroutineDispatcher get() = ImmediateSwingDispatcher