• 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
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