Skip to content

Improve documentation #1672

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 28, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/basics.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ Here we are launching a new coroutine in the [GlobalScope], meaning that the lif
coroutine is limited only by the lifetime of the whole application.

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

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

Expand Down
43 changes: 13 additions & 30 deletions kotlinx-coroutines-core/common/src/CoroutineDispatcher.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,46 +29,29 @@ import kotlin.coroutines.*
*/
public abstract class CoroutineDispatcher :
AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor {

/**
* Returns `true` if the execution shall be dispatched onto another thread.
* Returns `true` if the execution of the coroutine should be performed with [dispatch] method.
* The default behavior for most dispatchers is to return `true`.
*
* This method should never be used from general code, it is used only by `kotlinx.coroutines`
* internals and its contract with the rest of the API is an implementation detail.
*
* UI dispatchers _should not_ override `isDispatchNeeded`, but leave the default implementation that
* returns `true`. To understand the rationale beyond this recommendation, consider the following code:
* If this method returns `false`, the coroutine is resumed immediately in the current thread,
* potentially forming an event-loop to prevent stack overflows.
* The event loop is an advanced topic and its implications can be found in [Dispatchers.Unconfined] documentation.
*
* ```kotlin
* fun asyncUpdateUI() = async(Dispatchers.Main) {
* // do something here that updates something in UI
* }
* ```
* A dispatcher can override this method to provide a performance optimization and avoid paying a cost of an unnecessary dispatch.
* E.g. [MainCoroutineDispatcher.immediate] checks whether we are already in the required UI thread in this method and avoids
* an additional dispatch when it is not required.
*
* When you invoke `asyncUpdateUI` in some background thread, it immediately continues to the next
* line, while the UI update happens asynchronously in the UI thread. However, if you invoke
* it in the UI thread itself, it will update the UI _synchronously_ if your `isDispatchNeeded` is
* overridden with a thread check. Checking if we are already in the UI thread seems more
* efficient (and it might indeed save a few CPU cycles), but this subtle and context-sensitive
* difference in behavior makes the resulting async code harder to debug.
* While this approach can be more efficient, it is not chosen by default to provide a consistent dispatching behaviour
* so that users won't observe unexpected and non-consistent order of events by default.
*
* Basically, the choice here is between the "JS-style" asynchronous approach (async actions
* are always postponed to be executed later in the event dispatch thread) and "C#-style" approach
* (async actions are executed in the invoker thread until the first suspension point).
* While the C# approach seems to be more efficient, it ends up with recommendations like
* "use `yield` if you need to ....". This is error-prone. The JS-style approach is more consistent
* and does not require programmers to think about whether they need to yield or not.
*
* However, coroutine builders like [launch][CoroutineScope.launch] and [async][CoroutineScope.async] accept an optional [CoroutineStart]
* parameter that allows one to optionally choose the C#-style [CoroutineStart.UNDISPATCHED] behavior
* whenever it is needed for efficiency.
* Coroutine builders like [launch][CoroutineScope.launch] and [async][CoroutineScope.async] accept an optional [CoroutineStart]
* parameter that allows one to optionally choose the [undispatched][CoroutineStart.UNDISPATCHED] behavior to start coroutine immediately,
* but to be resumed only in the provided dispatcher.
*
* This method should generally be exception-safe. An exception thrown from this method
* may leave the coroutines that use this dispatcher in the inconsistent and hard to debug state.
*
* **Note: This is an experimental api.** Execution semantics of coroutines may change in the future when this function returns `false`.
*/
@ExperimentalCoroutinesApi
public open fun isDispatchNeeded(context: CoroutineContext): Boolean = true

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,12 @@ public inline fun CoroutineExceptionHandler(crossinline handler: (CoroutineConte
* * if there is a [Job] in the context, then [Job.cancel] is invoked;
* * Otherwise, all instances of [CoroutineExceptionHandler] found via [ServiceLoader]
* * and current thread's [Thread.uncaughtExceptionHandler] are invoked.
**/
*
* [CoroutineExceptionHandler] can be invoked from an arbitrary dispatcher used by coroutines in the current job hierarchy.
* For example, if one has a `MainScope` and launches children of the scope in main and default dispatchers, then exception handler can
* be invoked either in main or in default dispatcher thread regardless of
* which particular dispatcher coroutine that has thrown an exception used.
*/
public interface CoroutineExceptionHandler : CoroutineContext.Element {
/**
* Key for [CoroutineExceptionHandler] instance in the coroutine context.
Expand Down
2 changes: 1 addition & 1 deletion kotlinx-coroutines-core/common/src/Dispatchers.common.kt
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public expect object Dispatchers {
* Can print both "1 2 3" and "1 3 2", this is an implementation detail that can be changed.
* But it is guaranteed that "Done" will be printed only when both `withContext` calls are completed.
*
* Note that if you need your coroutine to be confined to a particular thread or a thread-pool after resumption,
* If you need your coroutine to be confined to a particular thread or a thread-pool after resumption,
* but still want to execute it in the current call-frame until its first suspension, then you can use
* an optional [CoroutineStart] parameter in coroutine builders like
* [launch][CoroutineScope.launch] and [async][CoroutineScope.async] setting it to
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public abstract class MainCoroutineDispatcher : CoroutineDispatcher() {
*
* Immediate dispatcher is safe from stack overflows and in case of nested invocations forms event-loop similar to [Dispatchers.Unconfined].
* The event loop is an advanced topic and its implications can be found in [Dispatchers.Unconfined] documentation.
* The formed event-loop is shared with [Unconfined] and other immediate dispatchers, potentially overlapping tasks between them.
*
* Example of usage:
* ```
Expand Down