blob: 9524b05e06f686a5811d6c475fdbd5d4d2f84c38 [file] [log] [blame]
Vsevolod Tolstopyatov4327b212018-12-17 19:49:12 +03001package kotlinx.coroutines.internal
2
3import kotlinx.coroutines.*
4import java.util.*
5import kotlin.coroutines.*
6
7// Lazy loader for the main dispatcher
8internal object MainDispatcherLoader {
9 @JvmField
10 val dispatcher: MainCoroutineDispatcher = loadMainDispatcher()
11
12 private fun loadMainDispatcher(): MainCoroutineDispatcher {
13 return try {
14 val factories = MainDispatcherFactory::class.java.let { clz ->
15 ServiceLoader.load(clz, clz.classLoader).toList()
16 }
17
18 factories.maxBy { it.loadPriority }?.tryCreateDispatcher(factories)
19 ?: MissingMainCoroutineDispatcher(null)
20 } catch (e: Throwable) {
21 // Service loader can throw an exception as well
22 MissingMainCoroutineDispatcher(e)
23 }
24 }
25}
26
27/**
28 * If anything goes wrong while trying to create main dispatcher (class not found,
29 * initialization failed, etc), then replace the main dispatcher with a special
30 * stub that throws an error message on any attempt to actually use it.
31 */
32@InternalCoroutinesApi
33public fun MainDispatcherFactory.tryCreateDispatcher(factories: List<MainDispatcherFactory>): MainCoroutineDispatcher =
34 try {
35 createDispatcher(factories)
36 } catch (cause: Throwable) {
37 MissingMainCoroutineDispatcher(cause, hintOnError())
38 }
39
40/** @suppress */
41@InternalCoroutinesApi
42public fun MainCoroutineDispatcher.isMissing(): Boolean = this is MissingMainCoroutineDispatcher
43
44private class MissingMainCoroutineDispatcher(
45 private val cause: Throwable?,
46 private val errorHint: String? = null
47) : MainCoroutineDispatcher(), Delay {
48
49 override val immediate: MainCoroutineDispatcher get() = this
50
51 override fun isDispatchNeeded(context: CoroutineContext): Boolean {
52 missing()
53 }
54
55 override suspend fun delay(time: Long) {
56 missing()
57 }
58
59 override fun invokeOnTimeout(timeMillis: Long, block: Runnable): DisposableHandle {
60 missing()
61 }
62
63 override fun dispatch(context: CoroutineContext, block: Runnable) =
64 missing()
65
66 override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation<Unit>) =
67 missing()
68
69 private fun missing(): Nothing {
70 if (cause == null) {
71 throw IllegalStateException(
72 "Module with the Main dispatcher is missing. " +
73 "Add dependency providing the Main dispatcher, e.g. 'kotlinx-coroutines-android'"
74 )
75 } else {
76 val message = "Module with the Main dispatcher had failed to initialize" + (errorHint?.let { ". $it" } ?: "")
77 throw IllegalStateException(message, cause)
78 }
79 }
80
81 override fun toString(): String = "Main[missing${if (cause != null) ", cause=$cause" else ""}]"
82}
83
Vsevolod Tolstopyatov89d43af2018-12-18 16:26:58 +030084/**
85 * @suppress
86 */
Vsevolod Tolstopyatov4327b212018-12-17 19:49:12 +030087@InternalCoroutinesApi
88public object MissingMainCoroutineDispatcherFactory : MainDispatcherFactory {
89 override val loadPriority: Int
90 get() = -1
91
92 override fun createDispatcher(allFactories: List<MainDispatcherFactory>): MainCoroutineDispatcher {
93 return MissingMainCoroutineDispatcher(null)
94 }
95}