• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2016-2020 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      * A dispatcher can override this method to provide a performance optimization and avoid paying a cost of an unnecessary dispatch.
49      * E.g. [MainCoroutineDispatcher.immediate] checks whether we are already in the required UI thread in this method and avoids
50      * an additional dispatch when it is not required.
51      *
52      * While this approach can be more efficient, it is not chosen by default to provide a consistent dispatching behaviour
53      * so that users won't observe unexpected and non-consistent order of events by default.
54      *
55      * Coroutine builders like [launch][CoroutineScope.launch] and [async][CoroutineScope.async] accept an optional [CoroutineStart]
56      * parameter that allows one to optionally choose the [undispatched][CoroutineStart.UNDISPATCHED] behavior to start coroutine immediately,
57      * but to be resumed only in the provided dispatcher.
58      *
59      * This method should generally be exception-safe. An exception thrown from this method
60      * may leave the coroutines that use this dispatcher in the inconsistent and hard to debug state.
61      */
isDispatchNeedednull62     public open fun isDispatchNeeded(context: CoroutineContext): Boolean = true
63 
64     /**
65      * Dispatches execution of a runnable [block] onto another thread in the given [context].
66      * This method should guarantee that the given [block] will be eventually invoked,
67      * otherwise the system may reach a deadlock state and never leave it.
68      * Cancellation mechanism is transparent for [CoroutineDispatcher] and is managed by [block] internals.
69      *
70      * This method should generally be exception-safe. An exception thrown from this method
71      * may leave the coroutines that use this dispatcher in the inconsistent and hard to debug state.
72      *
73      * This method must not immediately call [block]. Doing so would result in [StackOverflowError]
74      * when [yield] is repeatedly called from a loop. However, an implementation that returns `false` from
75      * [isDispatchNeeded] can delegate this function to `dispatch` method of [Dispatchers.Unconfined], which is
76      * integrated with [yield] to avoid this problem.
77      */
78     public abstract fun dispatch(context: CoroutineContext, block: Runnable)
79 
80     /**
81      * Dispatches execution of a runnable `block` onto another thread in the given `context`
82      * with a hint for the dispatcher that the current dispatch is triggered by a [yield] call, so that the execution of this
83      * continuation may be delayed in favor of already dispatched coroutines.
84      *
85      * Though the `yield` marker may be passed as a part of [context], this
86      * is a separate method for performance reasons.
87      *
88      * @suppress **This an internal API and should not be used from general code.**
89      */
90     @InternalCoroutinesApi
91     public open fun dispatchYield(context: CoroutineContext, block: Runnable): Unit = dispatch(context, block)
92 
93     /**
94      * Returns a continuation that wraps the provided [continuation], thus intercepting all resumptions.
95      *
96      * This method should generally be exception-safe. An exception thrown from this method
97      * may leave the coroutines that use this dispatcher in the inconsistent and hard to debug state.
98      */
99     public final override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> =
100         DispatchedContinuation(this, continuation)
101 
102     @InternalCoroutinesApi
103     public override fun releaseInterceptedContinuation(continuation: Continuation<*>) {
104         (continuation as DispatchedContinuation<*>).reusableCancellableContinuation?.detachChild()
105     }
106 
107     /**
108      * @suppress **Error**: Operator '+' on two CoroutineDispatcher objects is meaningless.
109      * CoroutineDispatcher is a coroutine context element and `+` is a set-sum operator for coroutine contexts.
110      * The dispatcher to the right of `+` just replaces the dispatcher to the left.
111      */
112     @Suppress("DeprecatedCallableAddReplaceWith")
113     @Deprecated(
114         message = "Operator '+' on two CoroutineDispatcher objects is meaningless. " +
115             "CoroutineDispatcher is a coroutine context element and `+` is a set-sum operator for coroutine contexts. " +
116             "The dispatcher to the right of `+` just replaces the dispatcher to the left.",
117         level = DeprecationLevel.ERROR
118     )
plusnull119     public operator fun plus(other: CoroutineDispatcher): CoroutineDispatcher = other
120 
121     /** @suppress for nicer debugging */
122     override fun toString(): String = "$classSimpleName@$hexAddress"
123 }
124 
125