@@ -19,15 +19,7 @@ public const val IO_PARALLELISM_PROPERTY_NAME = "kotlinx.coroutines.io.paralleli
1919/* *
2020 * Groups various implementations of [CoroutineDispatcher].
2121 */
22- actual object Dispatchers {
23-
24- private val mainDispatcher = loadMainDispatcher()
25-
26- private fun loadMainDispatcher (): MainCoroutineDispatcher ? {
27- return MainDispatcherFactory ::class .java.let { clz ->
28- ServiceLoader .load(clz, clz.classLoader).toList()
29- }.maxBy { it.loadPriority }?.createDispatcher()
30- }
22+ public actual object Dispatchers {
3123
3224 /* *
3325 * The default [CoroutineDispatcher] that is used by all standard builders like
@@ -59,8 +51,7 @@ actual object Dispatchers {
5951 * Implementation note: [MainCoroutineDispatcher.immediate] is not supported on Native and JS platforms.
6052 */
6153 @JvmStatic
62- public actual val Main : MainCoroutineDispatcher get() = mainDispatcher ? : error(" Module with Main dispatcher is missing. " +
63- " Add dependency with required Main dispatcher, e.g. 'kotlinx-coroutines-android'" )
54+ public actual val Main : MainCoroutineDispatcher get() = MainDispatcherLoader .dispatcher
6455
6556 /* *
6657 * A coroutine dispatcher that is not confined to any specific thread.
@@ -97,3 +88,47 @@ actual object Dispatchers {
9788 @JvmStatic
9889 public val IO : CoroutineDispatcher = DefaultScheduler .IO
9990}
91+
92+ // Lazy loader for the main dispatcher
93+ private object MainDispatcherLoader {
94+ @JvmField
95+ val dispatcher: MainCoroutineDispatcher =
96+ MainDispatcherFactory ::class .java.let { clz ->
97+ ServiceLoader .load(clz, clz.classLoader).toList()
98+ }.maxBy { it.loadPriority }?.tryCreateDispatcher() ? : MissingMainCoroutineDispatcher (null )
99+
100+ /* *
101+ * If anything goes wrong while trying to create main dispatcher (class not found,
102+ * initialization failed, etc), then replace the main dispatcher with a special
103+ * stub that throws an error message on any attempt to actually use it.
104+ */
105+ private fun MainDispatcherFactory.tryCreateDispatcher (): MainCoroutineDispatcher =
106+ try {
107+ createDispatcher()
108+ } catch (cause: Throwable ) {
109+ MissingMainCoroutineDispatcher (cause)
110+ }
111+ }
112+
113+ private class MissingMainCoroutineDispatcher (val cause : Throwable ? ) : MainCoroutineDispatcher(), Delay {
114+ override val immediate: MainCoroutineDispatcher get() = this
115+
116+ override fun dispatch (context : CoroutineContext , block : Runnable ) =
117+ missing()
118+
119+ override fun scheduleResumeAfterDelay (timeMillis : Long , continuation : CancellableContinuation <Unit >) =
120+ missing()
121+
122+ private fun missing () {
123+ if (cause == null ) {
124+ throw IllegalStateException (
125+ " Module with the Main dispatcher is missing. " +
126+ " Add dependency providing the Main dispatcher, e.g. 'kotlinx-coroutines-android'"
127+ )
128+ } else {
129+ throw IllegalStateException (" Module with the Main dispatcher had failed to initialize" , cause)
130+ }
131+ }
132+
133+ override fun toString (): String = " Main[missing${if (cause != null ) " , cause=$cause " else " " } ]"
134+ }
0 commit comments