Roman Elizarov | bf9509d | 2020-02-14 15:52:10 +0300 | [diff] [blame] | 1 | /* |
Aurimas Liutikas | cd3cb31 | 2021-05-12 21:56:16 +0000 | [diff] [blame^] | 2 | * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. |
Roman Elizarov | bf9509d | 2020-02-14 15:52:10 +0300 | [diff] [blame] | 3 | */ |
| 4 | |
Vsevolod Tolstopyatov | 4327b21 | 2018-12-17 19:49:12 +0300 | [diff] [blame] | 5 | package kotlinx.coroutines.internal |
| 6 | |
| 7 | import kotlinx.coroutines.* |
| 8 | import java.util.* |
| 9 | import kotlin.coroutines.* |
| 10 | |
Wojtek Kaliciński | b9b7d82 | 2019-05-30 14:17:51 +0200 | [diff] [blame] | 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 | |
Vsevolod Tolstopyatov | 4327b21 | 2018-12-17 19:49:12 +0300 | [diff] [blame] | 16 | // Lazy loader for the main dispatcher |
| 17 | internal object MainDispatcherLoader { |
Wojtek Kaliciński | b9b7d82 | 2019-05-30 14:17:51 +0200 | [diff] [blame] | 18 | |
| 19 | private val FAST_SERVICE_LOADER_ENABLED = systemProp(FAST_SERVICE_LOADER_PROPERTY_NAME, true) |
| 20 | |
Vsevolod Tolstopyatov | 4327b21 | 2018-12-17 19:49:12 +0300 | [diff] [blame] | 21 | @JvmField |
| 22 | val dispatcher: MainCoroutineDispatcher = loadMainDispatcher() |
| 23 | |
| 24 | private fun loadMainDispatcher(): MainCoroutineDispatcher { |
| 25 | return try { |
Wojtek Kaliciński | b9b7d82 | 2019-05-30 14:17:51 +0200 | [diff] [blame] | 26 | val factories = if (FAST_SERVICE_LOADER_ENABLED) { |
Vsevolod Tolstopyatov | e60ec8e | 2019-12-12 12:10:21 +0300 | [diff] [blame] | 27 | FastServiceLoader.loadMainDispatcherFactory() |
Wojtek Kaliciński | b9b7d82 | 2019-05-30 14:17:51 +0200 | [diff] [blame] | 28 | } else { |
Vsevolod Tolstopyatov | e60ec8e | 2019-12-12 12:10:21 +0300 | [diff] [blame] | 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. |
Wojtek Kaliciński | b9b7d82 | 2019-05-30 14:17:51 +0200 | [diff] [blame] | 32 | ServiceLoader.load( |
| 33 | MainDispatcherFactory::class.java, |
| 34 | MainDispatcherFactory::class.java.classLoader |
| 35 | ).iterator().asSequence().toList() |
Vsevolod Tolstopyatov | 4327b21 | 2018-12-17 19:49:12 +0300 | [diff] [blame] | 36 | } |
Roman Elizarov | bf9509d | 2020-02-14 15:52:10 +0300 | [diff] [blame] | 37 | @Suppress("ConstantConditionIf") |
Aurimas Liutikas | cd3cb31 | 2021-05-12 21:56:16 +0000 | [diff] [blame^] | 38 | factories.maxBy { it.loadPriority }?.tryCreateDispatcher(factories) |
Roman Elizarov | bf9509d | 2020-02-14 15:52:10 +0300 | [diff] [blame] | 39 | ?: createMissingDispatcher() |
Vsevolod Tolstopyatov | 4327b21 | 2018-12-17 19:49:12 +0300 | [diff] [blame] | 40 | } catch (e: Throwable) { |
| 41 | // Service loader can throw an exception as well |
Roman Elizarov | bf9509d | 2020-02-14 15:52:10 +0300 | [diff] [blame] | 42 | createMissingDispatcher(e) |
Vsevolod Tolstopyatov | 4327b21 | 2018-12-17 19:49:12 +0300 | [diff] [blame] | 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. |
Vsevolod Tolstopyatov | 1616676 | 2018-12-18 19:13:35 +0300 | [diff] [blame] | 51 | * |
| 52 | * @suppress internal API |
Vsevolod Tolstopyatov | 4327b21 | 2018-12-17 19:49:12 +0300 | [diff] [blame] | 53 | */ |
| 54 | @InternalCoroutinesApi |
| 55 | public fun MainDispatcherFactory.tryCreateDispatcher(factories: List<MainDispatcherFactory>): MainCoroutineDispatcher = |
| 56 | try { |
| 57 | createDispatcher(factories) |
| 58 | } catch (cause: Throwable) { |
Roman Elizarov | bf9509d | 2020-02-14 15:52:10 +0300 | [diff] [blame] | 59 | createMissingDispatcher(cause, hintOnError()) |
Vsevolod Tolstopyatov | 4327b21 | 2018-12-17 19:49:12 +0300 | [diff] [blame] | 60 | } |
| 61 | |
| 62 | /** @suppress */ |
| 63 | @InternalCoroutinesApi |
| 64 | public fun MainCoroutineDispatcher.isMissing(): Boolean = this is MissingMainCoroutineDispatcher |
| 65 | |
Roman Elizarov | bf9509d | 2020-02-14 15:52:10 +0300 | [diff] [blame] | 66 | // R8 optimization hook, not const on purpose to enable R8 optimizations via "assumenosideeffects" |
| 67 | @Suppress("MayBeConstant") |
| 68 | private val SUPPORT_MISSING = true |
| 69 | |
| 70 | @Suppress("ConstantConditionIf") |
| 71 | private fun createMissingDispatcher(cause: Throwable? = null, errorHint: String? = null) = |
| 72 | if (SUPPORT_MISSING) MissingMainCoroutineDispatcher(cause, errorHint) else |
| 73 | cause?.let { throw it } ?: throwMissingMainDispatcherException() |
| 74 | |
| 75 | internal fun throwMissingMainDispatcherException(): Nothing { |
| 76 | throw IllegalStateException( |
| 77 | "Module with the Main dispatcher is missing. " + |
| 78 | "Add dependency providing the Main dispatcher, e.g. 'kotlinx-coroutines-android' " + |
| 79 | "and ensure it has the same version as 'kotlinx-coroutines-core'" |
| 80 | ) |
| 81 | } |
| 82 | |
Vsevolod Tolstopyatov | 4327b21 | 2018-12-17 19:49:12 +0300 | [diff] [blame] | 83 | private class MissingMainCoroutineDispatcher( |
| 84 | private val cause: Throwable?, |
| 85 | private val errorHint: String? = null |
| 86 | ) : MainCoroutineDispatcher(), Delay { |
| 87 | |
| 88 | override val immediate: MainCoroutineDispatcher get() = this |
| 89 | |
Roman Elizarov | 20341f2 | 2020-10-09 16:40:15 +0300 | [diff] [blame] | 90 | override fun isDispatchNeeded(context: CoroutineContext): Boolean = |
Vsevolod Tolstopyatov | 4327b21 | 2018-12-17 19:49:12 +0300 | [diff] [blame] | 91 | missing() |
Vsevolod Tolstopyatov | 4327b21 | 2018-12-17 19:49:12 +0300 | [diff] [blame] | 92 | |
Roman Elizarov | 20341f2 | 2020-10-09 16:40:15 +0300 | [diff] [blame] | 93 | override suspend fun delay(time: Long) = |
Vsevolod Tolstopyatov | 4327b21 | 2018-12-17 19:49:12 +0300 | [diff] [blame] | 94 | missing() |
Vsevolod Tolstopyatov | 4327b21 | 2018-12-17 19:49:12 +0300 | [diff] [blame] | 95 | |
Roman Elizarov | 20341f2 | 2020-10-09 16:40:15 +0300 | [diff] [blame] | 96 | override fun invokeOnTimeout(timeMillis: Long, block: Runnable, context: CoroutineContext): DisposableHandle = |
Vsevolod Tolstopyatov | 4327b21 | 2018-12-17 19:49:12 +0300 | [diff] [blame] | 97 | missing() |
Vsevolod Tolstopyatov | 4327b21 | 2018-12-17 19:49:12 +0300 | [diff] [blame] | 98 | |
| 99 | override fun dispatch(context: CoroutineContext, block: Runnable) = |
| 100 | missing() |
| 101 | |
| 102 | override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation<Unit>) = |
| 103 | missing() |
| 104 | |
| 105 | private fun missing(): Nothing { |
| 106 | if (cause == null) { |
Roman Elizarov | bf9509d | 2020-02-14 15:52:10 +0300 | [diff] [blame] | 107 | throwMissingMainDispatcherException() |
Vsevolod Tolstopyatov | 4327b21 | 2018-12-17 19:49:12 +0300 | [diff] [blame] | 108 | } else { |
| 109 | val message = "Module with the Main dispatcher had failed to initialize" + (errorHint?.let { ". $it" } ?: "") |
| 110 | throw IllegalStateException(message, cause) |
| 111 | } |
| 112 | } |
| 113 | |
Roman Elizarov | b7d6518 | 2020-07-16 19:31:13 +0300 | [diff] [blame] | 114 | override fun toString(): String = "Dispatchers.Main[missing${if (cause != null) ", cause=$cause" else ""}]" |
Vsevolod Tolstopyatov | 4327b21 | 2018-12-17 19:49:12 +0300 | [diff] [blame] | 115 | } |
| 116 | |
Vsevolod Tolstopyatov | 89d43af | 2018-12-18 16:26:58 +0300 | [diff] [blame] | 117 | /** |
| 118 | * @suppress |
| 119 | */ |
Vsevolod Tolstopyatov | 4327b21 | 2018-12-17 19:49:12 +0300 | [diff] [blame] | 120 | @InternalCoroutinesApi |
| 121 | public object MissingMainCoroutineDispatcherFactory : MainDispatcherFactory { |
| 122 | override val loadPriority: Int |
| 123 | get() = -1 |
| 124 | |
| 125 | override fun createDispatcher(allFactories: List<MainDispatcherFactory>): MainCoroutineDispatcher { |
sokolova | 15b6345 | 2019-01-28 11:43:44 +0300 | [diff] [blame] | 126 | return MissingMainCoroutineDispatcher(null) |
Vsevolod Tolstopyatov | 4327b21 | 2018-12-17 19:49:12 +0300 | [diff] [blame] | 127 | } |
Vsevolod Tolstopyatov | 6d1a6e3 | 2020-02-18 15:28:00 +0300 | [diff] [blame] | 128 | } |