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 6 7 import kotlinx.coroutines.internal.* 8 import kotlin.coroutines.* 9 10 /** 11 * Base class to be extended by all coroutine dispatcher implementations. 12 * 13 * The following standard implementations are provided by `kotlinx.coroutines` as properties on 14 * the [Dispatchers] object: 15 * 16 * * [Dispatchers.Default] — is used by all standard builders if no dispatcher or any other [ContinuationInterceptor] 17 * is specified in their context. It uses a common pool of shared background threads. 18 * This is an appropriate choice for compute-intensive coroutines that consume CPU resources. 19 * * [Dispatchers.IO] — uses a shared pool of on-demand created threads and is designed for offloading of IO-intensive _blocking_ 20 * operations (like file I/O and blocking socket I/O). 21 * * [Dispatchers.Unconfined] — starts coroutine execution in the current call-frame until the first suspension, 22 * whereupon the coroutine builder function returns. 23 * The coroutine will later resume in whatever thread used by the 24 * corresponding suspending function, without confining it to any specific thread or pool. 25 * **The `Unconfined` dispatcher should not normally be used in code**. 26 * * Private thread pools can be created with [newSingleThreadContext] and [newFixedThreadPoolContext]. 27 * * An arbitrary [Executor][java.util.concurrent.Executor] can be converted to a dispatcher with the [asCoroutineDispatcher] extension function. 28 * 29 * This class ensures that debugging facilities in [newCoroutineContext] function work properly. 30 */ 31 public abstract class CoroutineDispatcher : 32 AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor { 33 34 /** @suppress */ 35 @ExperimentalStdlibApi 36 public companion object Key : AbstractCoroutineContextKey<ContinuationInterceptor, CoroutineDispatcher>( 37 ContinuationInterceptor, <lambda>null38 { it as? CoroutineDispatcher }) 39 40 /** 41 * Returns `true` if the execution of the coroutine should be performed with [dispatch] method. 42 * The default behavior for most dispatchers is to return `true`. 43 * 44 * If this method returns `false`, the coroutine is resumed immediately in the current thread, 45 * potentially forming an event-loop to prevent stack overflows. 46 * The event loop is an advanced topic and its implications can be found in [Dispatchers.Unconfined] documentation. 47 * 48 * The [context] parameter represents the context of the coroutine that is being dispatched, 49 * or [EmptyCoroutineContext] if a non-coroutine-specific [Runnable] is dispatched instead. 50 * 51 * A dispatcher can override this method to provide a performance optimization and avoid paying a cost of an unnecessary dispatch. 52 * E.g. [MainCoroutineDispatcher.immediate] checks whether we are already in the required UI thread in this method and avoids 53 * an additional dispatch when it is not required. 54 * 55 * While this approach can be more efficient, it is not chosen by default to provide a consistent dispatching behaviour 56 * so that users won't observe unexpected and non-consistent order of events by default. 57 * 58 * Coroutine builders like [launch][CoroutineScope.launch] and [async][CoroutineScope.async] accept an optional [CoroutineStart] 59 * parameter that allows one to optionally choose the [undispatched][CoroutineStart.UNDISPATCHED] behavior to start coroutine immediately, 60 * but to be resumed only in the provided dispatcher. 61 * 62 * This method should generally be exception-safe. An exception thrown from this method 63 * may leave the coroutines that use this dispatcher in the inconsistent and hard to debug state. 64 * 65 * @see dispatch 66 * @see Dispatchers.Unconfined 67 */ isDispatchNeedednull68 public open fun isDispatchNeeded(context: CoroutineContext): Boolean = true 69 70 /** 71 * Creates a view of the current dispatcher that limits the parallelism to the given [value][parallelism]. 72 * The resulting view uses the original dispatcher for execution, but with the guarantee that 73 * no more than [parallelism] coroutines are executed at the same time. 74 * 75 * This method does not impose restrictions on the number of views or the total sum of parallelism values, 76 * each view controls its own parallelism independently with the guarantee that the effective parallelism 77 * of all views cannot exceed the actual parallelism of the original dispatcher. 78 * 79 * ### Limitations 80 * 81 * The default implementation of `limitedParallelism` does not support direct dispatchers, 82 * such as executing the given runnable in place during [dispatch] calls. 83 * Any dispatcher that may return `false` from [isDispatchNeeded] is considered direct. 84 * For direct dispatchers, it is recommended to override this method 85 * and provide a domain-specific implementation or to throw an [UnsupportedOperationException]. 86 * 87 * ### Example of usage 88 * ``` 89 * private val backgroundDispatcher = newFixedThreadPoolContext(4, "App Background") 90 * // At most 2 threads will be processing images as it is really slow and CPU-intensive 91 * private val imageProcessingDispatcher = backgroundDispatcher.limitedParallelism(2) 92 * // At most 3 threads will be processing JSON to avoid image processing starvation 93 * private val jsonProcessingDispatcher = backgroundDispatcher.limitedParallelism(3) 94 * // At most 1 thread will be doing IO 95 * private val fileWriterDispatcher = backgroundDispatcher.limitedParallelism(1) 96 * ``` 97 * Note how in this example the application has an executor with 4 threads, but the total sum of all limits 98 * is 6. Still, at most 4 coroutines can be executed simultaneously as each view limits only its own parallelism. 99 * 100 * Note that this example was structured in such a way that it illustrates the parallelism guarantees. 101 * In practice, it is usually better to use [Dispatchers.IO] or [Dispatchers.Default] instead of creating a 102 * `backgroundDispatcher`. It is both possible and advised to call `limitedParallelism` on them. 103 */ 104 @ExperimentalCoroutinesApi 105 public open fun limitedParallelism(parallelism: Int): CoroutineDispatcher { 106 parallelism.checkParallelism() 107 return LimitedDispatcher(this, parallelism) 108 } 109 110 /** 111 * Requests execution of a runnable [block]. 112 * The dispatcher guarantees that [block] will eventually execute, typically by dispatching it to a thread pool, 113 * using a dedicated thread, or just executing the block in place. 114 * The [context] parameter represents the context of the coroutine that is being dispatched, 115 * or [EmptyCoroutineContext] if a non-coroutine-specific [Runnable] is dispatched instead. 116 * Implementations may use [context] for additional context-specific information, 117 * such as priority, whether the dispatched coroutine can be invoked in place, 118 * coroutine name, and additional diagnostic elements. 119 * 120 * This method should guarantee that the given [block] will be eventually invoked, 121 * otherwise the system may reach a deadlock state and never leave it. 122 * The cancellation mechanism is transparent for [CoroutineDispatcher] and is managed by [block] internals. 123 * 124 * This method should generally be exception-safe. An exception thrown from this method 125 * may leave the coroutines that use this dispatcher in an inconsistent and hard-to-debug state. 126 * 127 * This method must not immediately call [block]. Doing so may result in `StackOverflowError` 128 * when `dispatch` is invoked repeatedly, for example when [yield] is called in a loop. 129 * In order to execute a block in place, it is required to return `false` from [isDispatchNeeded] 130 * and delegate the `dispatch` implementation to `Dispatchers.Unconfined.dispatch` in such cases. 131 * To support this, the coroutines machinery ensures in-place execution and forms an event-loop to 132 * avoid unbound recursion. 133 * 134 * @see isDispatchNeeded 135 * @see Dispatchers.Unconfined 136 */ dispatchnull137 public abstract fun dispatch(context: CoroutineContext, block: Runnable) 138 139 /** 140 * Dispatches execution of a runnable `block` onto another thread in the given `context` 141 * with a hint for the dispatcher that the current dispatch is triggered by a [yield] call, so that the execution of this 142 * continuation may be delayed in favor of already dispatched coroutines. 143 * 144 * Though the `yield` marker may be passed as a part of [context], this 145 * is a separate method for performance reasons. 146 * 147 * @suppress **This an internal API and should not be used from general code.** 148 */ 149 @InternalCoroutinesApi 150 public open fun dispatchYield(context: CoroutineContext, block: Runnable): Unit = dispatch(context, block) 151 152 /** 153 * Returns a continuation that wraps the provided [continuation], thus intercepting all resumptions. 154 * 155 * This method should generally be exception-safe. An exception thrown from this method 156 * may leave the coroutines that use this dispatcher in the inconsistent and hard to debug state. 157 */ 158 public final override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> = 159 DispatchedContinuation(this, continuation) 160 161 public final override fun releaseInterceptedContinuation(continuation: Continuation<*>) { 162 /* 163 * Unconditional cast is safe here: we only return DispatchedContinuation from `interceptContinuation`, 164 * any ClassCastException can only indicate compiler bug 165 */ 166 val dispatched = continuation as DispatchedContinuation<*> 167 dispatched.release() 168 } 169 170 /** 171 * @suppress **Error**: Operator '+' on two CoroutineDispatcher objects is meaningless. 172 * CoroutineDispatcher is a coroutine context element and `+` is a set-sum operator for coroutine contexts. 173 * The dispatcher to the right of `+` just replaces the dispatcher to the left. 174 */ 175 @Suppress("DeprecatedCallableAddReplaceWith") 176 @Deprecated( 177 message = "Operator '+' on two CoroutineDispatcher objects is meaningless. " + 178 "CoroutineDispatcher is a coroutine context element and `+` is a set-sum operator for coroutine contexts. " + 179 "The dispatcher to the right of `+` just replaces the dispatcher to the left.", 180 level = DeprecationLevel.ERROR 181 ) plusnull182 public operator fun plus(other: CoroutineDispatcher): CoroutineDispatcher = other 183 184 /** @suppress for nicer debugging */ 185 override fun toString(): String = "$classSimpleName@$hexAddress" 186 } 187 188