• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3  */
4 
5 package kotlinx.coroutines.internal
6 
7 import kotlinx.coroutines.*
8 import java.util.*
9 import kotlin.coroutines.*
10 
11 /**
12  * Name of the boolean property that enables using of [FastServiceLoader].
13  */
14 private const val FAST_SERVICE_LOADER_PROPERTY_NAME = "kotlinx.coroutines.fast.service.loader"
15 
16 // Lazy loader for the main dispatcher
17 internal object MainDispatcherLoader {
18 
19     private val FAST_SERVICE_LOADER_ENABLED = systemProp(FAST_SERVICE_LOADER_PROPERTY_NAME, true)
20 
21     @JvmField
22     val dispatcher: MainCoroutineDispatcher = loadMainDispatcher()
23 
loadMainDispatchernull24     private fun loadMainDispatcher(): MainCoroutineDispatcher {
25         return try {
26             val factories = if (FAST_SERVICE_LOADER_ENABLED) {
27                 FastServiceLoader.loadMainDispatcherFactory()
28             } else {
29                 // We are explicitly using the
30                 // `ServiceLoader.load(MyClass::class.java, MyClass::class.java.classLoader).iterator()`
31                 // form of the ServiceLoader call to enable R8 optimization when compiled on Android.
32                 ServiceLoader.load(
33                         MainDispatcherFactory::class.java,
34                         MainDispatcherFactory::class.java.classLoader
35                 ).iterator().asSequence().toList()
36             }
37             @Suppress("ConstantConditionIf")
38             factories.maxByOrNull { it.loadPriority }?.tryCreateDispatcher(factories)
39                 ?: createMissingDispatcher()
40         } catch (e: Throwable) {
41             // Service loader can throw an exception as well
42             createMissingDispatcher(e)
43         }
44     }
45 }
46 
47 /**
48  * If anything goes wrong while trying to create main dispatcher (class not found,
49  * initialization failed, etc), then replace the main dispatcher with a special
50  * stub that throws an error message on any attempt to actually use it.
51  *
52  * @suppress internal API
53  */
54 @InternalCoroutinesApi
tryCreateDispatchernull55 public fun MainDispatcherFactory.tryCreateDispatcher(factories: List<MainDispatcherFactory>): MainCoroutineDispatcher =
56     try {
57         createDispatcher(factories)
58     } catch (cause: Throwable) {
59         createMissingDispatcher(cause, hintOnError())
60     }
61 
62 /** @suppress */
63 @InternalCoroutinesApi
isMissingnull64 public fun MainCoroutineDispatcher.isMissing(): Boolean =
65     // not checking `this`, as it may be wrapped in a `TestMainDispatcher`, whereas `immediate` never is.
66     this.immediate is MissingMainCoroutineDispatcher
67 
68 // R8 optimization hook, not const on purpose to enable R8 optimizations via "assumenosideeffects"
69 @Suppress("MayBeConstant")
70 private val SUPPORT_MISSING = true
71 
72 @Suppress(
73     "ConstantConditionIf",
74     "IMPLICIT_NOTHING_TYPE_ARGUMENT_AGAINST_NOT_NOTHING_EXPECTED_TYPE" // KT-47626
75 )
76 private fun createMissingDispatcher(cause: Throwable? = null, errorHint: String? = null) =
77     if (SUPPORT_MISSING) MissingMainCoroutineDispatcher(cause, errorHint) else
78         cause?.let { throw it } ?: throwMissingMainDispatcherException()
79 
throwMissingMainDispatcherExceptionnull80 internal fun throwMissingMainDispatcherException(): Nothing {
81     throw IllegalStateException(
82         "Module with the Main dispatcher is missing. " +
83             "Add dependency providing the Main dispatcher, e.g. 'kotlinx-coroutines-android' " +
84             "and ensure it has the same version as 'kotlinx-coroutines-core'"
85     )
86 }
87 
88 private class MissingMainCoroutineDispatcher(
89     private val cause: Throwable?,
90     private val errorHint: String? = null
91 ) : MainCoroutineDispatcher(), Delay {
92 
93     override val immediate: MainCoroutineDispatcher get() = this
94 
isDispatchNeedednull95     override fun isDispatchNeeded(context: CoroutineContext): Boolean =
96         missing()
97 
98     override fun limitedParallelism(parallelism: Int): CoroutineDispatcher =
99         missing()
100 
101     override suspend fun delay(time: Long) =
102         missing()
103 
104     override fun invokeOnTimeout(timeMillis: Long, block: Runnable, context: CoroutineContext): DisposableHandle =
105         missing()
106 
107     override fun dispatch(context: CoroutineContext, block: Runnable) =
108         missing()
109 
110     override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation<Unit>) =
111         missing()
112 
113     private fun missing(): Nothing {
114         if  (cause == null) {
115             throwMissingMainDispatcherException()
116         } else {
117             val message = "Module with the Main dispatcher had failed to initialize" + (errorHint?.let { ". $it" } ?: "")
118             throw IllegalStateException(message, cause)
119         }
120     }
121 
toStringnull122     override fun toString(): String = "Dispatchers.Main[missing${if (cause != null) ", cause=$cause" else ""}]"
123 }
124 
125 /**
126  * @suppress
127  */
128 @InternalCoroutinesApi
129 public object MissingMainCoroutineDispatcherFactory : MainDispatcherFactory {
130     override val loadPriority: Int
131         get() = -1
132 
133     override fun createDispatcher(allFactories: List<MainDispatcherFactory>): MainCoroutineDispatcher {
134         return MissingMainCoroutineDispatcher(null)
135     }
136 }
137