Skip to content

Commit 5af4f91

Browse files
committed
Improve documentation
* Clarifications of CoroutineExceptionHandler execution * Clarifications of MainCoroutineDispatcher.immediate * Outdated documentation (pointed out by Google AndoidX team) for isDispatchNeeded is rewritten * Fixes #1650 Fixes #1651 Fixes #1634
1 parent 7e895fc commit 5af4f91

File tree

5 files changed

+25
-34
lines changed

5 files changed

+25
-34
lines changed

docs/basics.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,8 @@ Here we are launching a new coroutine in the [GlobalScope], meaning that the lif
7575
coroutine is limited only by the lifetime of the whole application.
7676

7777
You can achieve the same result replacing
78-
`GlobalScope.launch { ... }` with `thread { ... }` and `delay(...)` with `Thread.sleep(...)`. Try it.
78+
`GlobalScope.launch { ... }` with `thread { ... }` and `delay(...)` with `Thread.sleep(...)`.
79+
Try it (don't forget to import `kotlin.concurrent.thread`).
7980

8081
If you start by replacing `GlobalScope.launch` by `thread`, the compiler produces the following error:
8182

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

+15-31
Original file line numberDiff line numberDiff line change
@@ -27,46 +27,30 @@ import kotlin.coroutines.*
2727
*
2828
* This class ensures that debugging facilities in [newCoroutineContext] function work properly.
2929
*/
30-
public abstract class CoroutineDispatcher :
31-
AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor {
30+
public abstract class CoroutineDispatcher
31+
: AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor {
32+
3233
/**
33-
* Returns `true` if the execution shall be dispatched onto another thread.
34+
* Returns `true` if the execution of the coroutine should be performed with [dispatch] method.
3435
* The default behavior for most dispatchers is to return `true`.
3536
*
36-
* This method should never be used from general code, it is used only by `kotlinx.coroutines`
37-
* internals and its contract with the rest of the API is an implementation detail.
38-
*
39-
* UI dispatchers _should not_ override `isDispatchNeeded`, but leave the default implementation that
40-
* returns `true`. To understand the rationale beyond this recommendation, consider the following code:
41-
*
42-
* ```kotlin
43-
* fun asyncUpdateUI() = async(Dispatchers.Main) {
44-
* // do something here that updates something in UI
45-
* }
46-
* ```
47-
*
48-
* When you invoke `asyncUpdateUI` in some background thread, it immediately continues to the next
49-
* line, while the UI update happens asynchronously in the UI thread. However, if you invoke
50-
* it in the UI thread itself, it will update the UI _synchronously_ if your `isDispatchNeeded` is
51-
* overridden with a thread check. Checking if we are already in the UI thread seems more
52-
* efficient (and it might indeed save a few CPU cycles), but this subtle and context-sensitive
53-
* difference in behavior makes the resulting async code harder to debug.
37+
* If this method returns `false`, the coroutine is resumed immediately in the current thread,
38+
* potentially forming an event-loop to prevent stack overflows.
39+
* The event loop is an advanced topic and its implications can be found in [Dispatchers.Unconfined] documentation.
5440
*
55-
* Basically, the choice here is between the "JS-style" asynchronous approach (async actions
56-
* are always postponed to be executed later in the event dispatch thread) and "C#-style" approach
57-
* (async actions are executed in the invoker thread until the first suspension point).
58-
* While the C# approach seems to be more efficient, it ends up with recommendations like
59-
* "use `yield` if you need to ....". This is error-prone. The JS-style approach is more consistent
60-
* and does not require programmers to think about whether they need to yield or not.
41+
* A dispatcher can override this method to provide a performance optimization and avoid paying a cost of an unnecessary dispatch.
42+
* E.g. [MainCoroutineDispatcher.immediate] checks whether we are already in the required UI thread in this method and avoids
43+
* an additional dispatch when it is not required. But this method should not return `false` by default, because
44+
* it may expose unexpected behaviour (e.g. with interleaved event-loops) and unexpected order of events.
6145
*
62-
* However, coroutine builders like [launch][CoroutineScope.launch] and [async][CoroutineScope.async] accept an optional [CoroutineStart]
63-
* parameter that allows one to optionally choose the C#-style [CoroutineStart.UNDISPATCHED] behavior
64-
* whenever it is needed for efficiency.
46+
* Coroutine builders like [launch][CoroutineScope.launch] and [async][CoroutineScope.async] accept an optional [CoroutineStart]
47+
* parameter that allows one to optionally choose the [undispatched][CoroutineStart.UNDISPATCHED] behavior to start coroutine immediately,
48+
* but to be resumed only in the provided dispatcher.
6549
*
6650
* This method should generally be exception-safe. An exception thrown from this method
6751
* may leave the coroutines that use this dispatcher in the inconsistent and hard to debug state.
6852
*
69-
* **Note: This is an experimental api.** Execution semantics of coroutines may change in the future when this function returns `false`.
53+
* **This is an experimental api.** Execution semantics of coroutines may change in the future when this function returns `false`.
7054
*/
7155
@ExperimentalCoroutinesApi
7256
public open fun isDispatchNeeded(context: CoroutineContext): Boolean = true

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

+6-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,12 @@ public inline fun CoroutineExceptionHandler(crossinline handler: (CoroutineConte
6565
* * if there is a [Job] in the context, then [Job.cancel] is invoked;
6666
* * Otherwise, all instances of [CoroutineExceptionHandler] found via [ServiceLoader]
6767
* * and current thread's [Thread.uncaughtExceptionHandler] are invoked.
68-
**/
68+
*
69+
* [CoroutineExceptionHandler] can be invoked from an arbitrary dispatcher used by coroutines in the current job hierarchy.
70+
* For example, if one has a `MainScope` and launches children of the scope in main and default dispatchers, then exception handler can
71+
* be invoked either in main or in default dispatcher thread regardless of
72+
* which particular dispatcher coroutine that has thrown an exception used.
73+
*/
6974
public interface CoroutineExceptionHandler : CoroutineContext.Element {
7075
/**
7176
* Key for [CoroutineExceptionHandler] instance in the coroutine context.

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public expect object Dispatchers {
6666
* Can print both "1 2 3" and "1 3 2", this is an implementation detail that can be changed.
6767
* But it is guaranteed that "Done" will be printed only when both `withContext` calls are completed.
6868
*
69-
* Note that if you need your coroutine to be confined to a particular thread or a thread-pool after resumption,
69+
* If you need your coroutine to be confined to a particular thread or a thread-pool after resumption,
7070
* but still want to execute it in the current call-frame until its first suspension, then you can use
7171
* an optional [CoroutineStart] parameter in coroutine builders like
7272
* [launch][CoroutineScope.launch] and [async][CoroutineScope.async] setting it to

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

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public abstract class MainCoroutineDispatcher : CoroutineDispatcher() {
1818
*
1919
* Immediate dispatcher is safe from stack overflows and in case of nested invocations forms event-loop similar to [Dispatchers.Unconfined].
2020
* The event loop is an advanced topic and its implications can be found in [Dispatchers.Unconfined] documentation.
21+
* The formed event-loop is shared with [Unconfined] and other immediate dispatchers, potentially overlapping tasks between them.
2122
*
2223
* Example of usage:
2324
* ```

0 commit comments

Comments
 (0)