Skip to content

Commit 404e155

Browse files
committed
Explain the typical dispatch procedure in CoroutineDispatcher docs
1 parent bc48938 commit 404e155

File tree

2 files changed

+47
-22
lines changed

2 files changed

+47
-22
lines changed

kotlinx-coroutines-core/common/src/CoroutineDispatcher.kt

+43-12
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,55 @@ import kotlin.coroutines.*
66
/**
77
* Base class to be extended by all coroutine dispatcher implementations.
88
*
9+
* If `kotlinx-coroutines` is used, it is recommended to avoid [ContinuationInterceptor] instances that are not
10+
* [CoroutineDispatcher] implementations, as [CoroutineDispatcher] ensures that the
11+
* debugging facilities in the [newCoroutineContext] function work properly.
12+
*
13+
* ## Predefined dispatchers
14+
*
915
* The following standard implementations are provided by `kotlinx.coroutines` as properties on
1016
* the [Dispatchers] object:
1117
*
12-
* - [Dispatchers.Default] — is used by all standard builders if no dispatcher or any other [ContinuationInterceptor]
13-
* is specified in their context. It uses a common pool of shared background threads.
18+
* - [Dispatchers.Default] is used by all standard builders if no dispatcher or any other [ContinuationInterceptor]
19+
* is specified in their context.
20+
* It uses a common pool of shared background threads.
1421
* This is an appropriate choice for compute-intensive coroutines that consume CPU resources.
15-
* - [Dispatchers.IO] — uses a shared pool of on-demand created threads and is designed for offloading of IO-intensive _blocking_
22+
* - `Dispatchers.IO` (available on the JVM and Native targets)
23+
* uses a shared pool of on-demand created threads and is designed for offloading of IO-intensive _blocking_
1624
* operations (like file I/O and blocking socket I/O).
17-
* - [Dispatchers.Unconfined] — starts coroutine execution in the current call-frame until the first suspension,
18-
* whereupon the coroutine builder function returns.
19-
* The coroutine will later resume in whatever thread used by the
20-
* corresponding suspending function, without confining it to any specific thread or pool.
25+
* - [Dispatchers.Main] represents the UI thread if one is available.
26+
* - [Dispatchers.Unconfined] starts coroutine execution in the current call-frame until the first suspension,
27+
* at which point the coroutine builder function returns.
28+
* When the coroutine is resumed, the thread from which it is resumed will run the coroutine code until the next
29+
* suspension, and so on.
2130
* **The `Unconfined` dispatcher should not normally be used in code**.
22-
* - Private thread pools can be created with [newSingleThreadContext] and [newFixedThreadPoolContext].
23-
* - An arbitrary [Executor][java.util.concurrent.Executor] can be converted to a dispatcher with the [asCoroutineDispatcher] extension function.
31+
* - Calling [limitedParallelism] on any dispatcher creates a view of the dispatcher that limits the parallelism
32+
* to the given value.
33+
* This allows creating private thread pools without spawning new threads.
34+
* For example, `Dispatchers.IO.limitedParallelism(4)` creates a dispatcher that allows running at most
35+
* 4 tasks in parallel, reusing the existing IO dispatcher threads.
36+
* - When thread pools completely separate from [Dispatchers.Default] and [Dispatchers.IO] are required,
37+
* they can be created with `newSingleThreadContext` and `newFixedThreadPoolContext` on the JVM and Native targets.
38+
* - An arbitrary `java.util.concurrent.Executor` can be converted to a dispatcher with the
39+
* `asCoroutineDispatcher` extension function.
40+
*
41+
* ## Dispatch procedure
2442
*
25-
* This class ensures that debugging facilities in [newCoroutineContext] function work properly.
43+
* Typically, a dispatch procedure is performed as follows:
44+
*
45+
* - First, [isDispatchNeeded] is invoked to determine whether the coroutine should be dispatched
46+
* or is already in the right context.
47+
* - If [isDispatchNeeded] returns `true`, the coroutine is dispatched using the [dispatch] method.
48+
* It may take a while for the dispatcher to start the task,
49+
* but the [dispatch] method itself may return immediately, before the task has even began to execute.
50+
* - If no dispatch is needed (which is the case for [Dispatchers.Main.immediate][MainCoroutineDispatcher.immediate]
51+
* when already on the main thread and for [Dispatchers.Unconfined]),
52+
* the coroutine is resumed in the thread performing the dispatch procedure,
53+
* forming an event loop to prevent stack overflows.
54+
* See [Dispatchers.Unconfined] for a description of event loops.
55+
*
56+
* This behavior may be different on the very first dispatch procedure for a given coroutine, depending on the
57+
* [CoroutineStart] parameter of the coroutine builder.
2658
*/
2759
public abstract class CoroutineDispatcher :
2860
AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor {
@@ -205,7 +237,7 @@ public abstract class CoroutineDispatcher :
205237

206238
public final override fun releaseInterceptedContinuation(continuation: Continuation<*>) {
207239
/*
208-
* Unconditional cast is safe here: we only return DispatchedContinuation from `interceptContinuation`,
240+
* Unconditional cast is safe here: we return only DispatchedContinuation from `interceptContinuation`,
209241
* any ClassCastException can only indicate compiler bug
210242
*/
211243
val dispatched = continuation as DispatchedContinuation<*>
@@ -229,4 +261,3 @@ public abstract class CoroutineDispatcher :
229261
/** @suppress for nicer debugging */
230262
override fun toString(): String = "$classSimpleName@$hexAddress"
231263
}
232-

kotlinx-coroutines-core/common/src/CoroutineStart.kt

+4-10
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,7 @@ public enum class CoroutineStart {
2424
/**
2525
* Immediately schedules the coroutine for execution according to its context. This is usually the default option.
2626
*
27-
* The behavior of [DEFAULT] depends on the result of [CoroutineDispatcher.isDispatchNeeded] in
28-
* the context of the started coroutine.
29-
* - In the typical case where a dispatch is needed, the coroutine is dispatched for execution on that dispatcher.
30-
* It may take a while for the dispatcher to start the task; the thread that invoked the coroutine builder
31-
* does not wait for the task to start and instead continues its execution.
32-
* - If no dispatch is needed (which is the case for [Dispatchers.Main.immediate][MainCoroutineDispatcher.immediate]
33-
* when already on the main thread and for [Dispatchers.Unconfined]),
34-
* the task is executed immediately in the same thread that invoked the coroutine builder,
35-
* similarly to [UNDISPATCHED].
27+
* [DEFAULT] uses the default dispatch procedure described in the [CoroutineDispatcher] documentation.
3628
*
3729
* If the coroutine's [Job] is cancelled before it started executing, then it will not start its
3830
* execution at all and will be considered [cancelled][Job.isCancelled].
@@ -91,7 +83,8 @@ public enum class CoroutineStart {
9183
* Starting a coroutine with [LAZY] only creates the coroutine, but does not schedule it for execution.
9284
* When the completion of the coroutine is first awaited
9385
* (for example, via [Job.join]) or explicitly [started][Job.start],
94-
* the dispatch procedure described in [DEFAULT] happens in the thread that did it.
86+
* the dispatch procedure described in the [CoroutineDispatcher] documentation is performed in the thread
87+
* that did it.
9588
*
9689
* The details of what counts as waiting can be found in the documentation of the corresponding coroutine builders
9790
* like [launch][CoroutineScope.launch] and [async][CoroutineScope.async].
@@ -229,6 +222,7 @@ public enum class CoroutineStart {
229222
* - Resumptions from later suspensions will properly use the actual dispatcher from the coroutine's context.
230223
* Only the code until the first suspension point will be executed immediately.
231224
* - Even if the coroutine was cancelled already, its code will still start to be executed, similar to [ATOMIC].
225+
* - The coroutine will not form an event loop. See [Dispatchers.Unconfined] for an explanation of event loops.
232226
*
233227
* This set of behaviors makes [UNDISPATCHED] well-suited for cases where the coroutine has a distinct
234228
* initialization phase whose side effects we want to rely on later.

0 commit comments

Comments
 (0)