Roman Elizarov | f16fd27 | 2017-02-07 11:26:00 +0300 | [diff] [blame] | 1 | /* |
Roman Elizarov | 1f74a2d | 2018-06-29 19:19:45 +0300 | [diff] [blame^] | 2 | * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. |
Roman Elizarov | f16fd27 | 2017-02-07 11:26:00 +0300 | [diff] [blame] | 3 | */ |
| 4 | |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 5 | package kotlinx.coroutines.experimental |
| 6 | |
Roman Elizarov | f29203c | 2018-01-11 12:39:36 +0300 | [diff] [blame] | 7 | import kotlin.coroutines.experimental.* |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 8 | |
| 9 | /** |
Roman Elizarov | ed7b864 | 2017-01-19 11:22:28 +0300 | [diff] [blame] | 10 | * Base class that shall be extended by all coroutine dispatcher implementations. |
| 11 | * |
| 12 | * The following standard implementations are provided by `kotlinx.coroutines`: |
Roman Elizarov | 2f6d7c9 | 2017-02-03 15:16:07 +0300 | [diff] [blame] | 13 | * * [Unconfined] -- starts coroutine execution in the current call-frame until the first suspension. |
| 14 | * On first suspension the coroutine builder function returns. |
| 15 | * The coroutine will resume in whatever thread that is used by the |
| 16 | * corresponding suspending function, without confining it to any specific thread or pool. |
Roman Elizarov | ed7b864 | 2017-01-19 11:22:28 +0300 | [diff] [blame] | 17 | * This in an appropriate choice for IO-intensive coroutines that do not consume CPU resources. |
Roman Elizarov | c0d559b | 2017-09-28 14:27:05 +0300 | [diff] [blame] | 18 | * * [DefaultDispatcher] -- is used by all standard builder if no dispatcher nor any other [ContinuationInterceptor] |
| 19 | * is specified in their context. It is currently equal to [CommonPool] (subject to change). |
Roman Elizarov | ed7b864 | 2017-01-19 11:22:28 +0300 | [diff] [blame] | 20 | * * [CommonPool] -- immediately returns from the coroutine builder and schedules coroutine execution to |
| 21 | * a common pool of shared background threads. |
| 22 | * This is an appropriate choice for compute-intensive coroutines that consume a lot of CPU resources. |
| 23 | * * Private thread pools can be created with [newSingleThreadContext] and [newFixedThreadPoolContext]. |
Roman Elizarov | 38b5ea1 | 2017-03-09 12:03:39 +0300 | [diff] [blame] | 24 | * * An arbitrary [Executor][java.util.concurrent.Executor] can be converted to dispatcher with [asCoroutineDispatcher] extension function. |
Roman Elizarov | ed7b864 | 2017-01-19 11:22:28 +0300 | [diff] [blame] | 25 | * |
Roman Elizarov | 44ba4b1 | 2017-01-25 11:37:54 +0300 | [diff] [blame] | 26 | * This class ensures that debugging facilities in [newCoroutineContext] function work properly. |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 27 | */ |
Roman Elizarov | aa461cf | 2018-04-11 13:20:29 +0300 | [diff] [blame] | 28 | public abstract class CoroutineDispatcher : |
Roman Elizarov | f29203c | 2018-01-11 12:39:36 +0300 | [diff] [blame] | 29 | AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor { |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 30 | /** |
Roman Elizarov | a198bad | 2017-02-10 13:30:38 +0300 | [diff] [blame] | 31 | * Returns `true` if execution shall be dispatched onto another thread. |
Roman Elizarov | 7cf452e | 2017-01-29 21:58:33 +0300 | [diff] [blame] | 32 | * The default behaviour for most dispatchers is to return `true`. |
Roman Elizarov | a198bad | 2017-02-10 13:30:38 +0300 | [diff] [blame] | 33 | * |
| 34 | * UI dispatchers _should not_ override `isDispatchNeeded`, but leave a default implementation that |
| 35 | * returns `true`. To understand the rationale beyond this recommendation, consider the following code: |
| 36 | * |
| 37 | * ```kotlin |
| 38 | * fun asyncUpdateUI() = async(MainThread) { |
| 39 | * // do something here that updates something in UI |
| 40 | * } |
| 41 | * ``` |
| 42 | * |
| 43 | * When you invoke `asyncUpdateUI` in some background thread, it immediately continues to the next |
| 44 | * line, while UI update happens asynchronously in the UI thread. However, if you invoke |
| 45 | * it in the UI thread itself, it updates UI _synchronously_ if your `isDispatchNeeded` is |
| 46 | * overridden with a thread check. Checking if we are already in the UI thread seems more |
| 47 | * efficient (and it might indeed save a few CPU cycles), but this subtle and context-sensitive |
| 48 | * difference in behavior makes the resulting async code harder to debug. |
| 49 | * |
| 50 | * Basically, the choice here is between "JS-style" asynchronous approach (async actions |
| 51 | * are always postponed to be executed later in the even dispatch thread) and "C#-style" approach |
| 52 | * (async actions are executed in the invoker thread until the first suspension point). |
| 53 | * While, C# approach seems to be more efficient, it ends up with recommendations like |
| 54 | * "use `yield` if you need to ....". This is error-prone. JS-style approach is more consistent |
| 55 | * and does not require programmers to think about whether they need to yield or not. |
Roman Elizarov | ecda27f | 2017-04-06 23:06:26 +0300 | [diff] [blame] | 56 | * |
| 57 | * However, coroutine builders like [launch] and [async] accept an optional [CoroutineStart] |
| 58 | * parameter that allows one to optionally choose C#-style [CoroutineStart.UNDISPATCHED] behaviour |
| 59 | * whenever it is needed for efficiency. |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 60 | */ |
Roman Elizarov | aa461cf | 2018-04-11 13:20:29 +0300 | [diff] [blame] | 61 | public open fun isDispatchNeeded(context: CoroutineContext): Boolean = true |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 62 | |
| 63 | /** |
Roman Elizarov | 67891d8 | 2017-01-23 16:47:20 +0300 | [diff] [blame] | 64 | * Dispatches execution of a runnable [block] onto another thread in the given [context]. |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 65 | */ |
Roman Elizarov | aa461cf | 2018-04-11 13:20:29 +0300 | [diff] [blame] | 66 | public abstract fun dispatch(context: CoroutineContext, block: Runnable) |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 67 | |
Roman Elizarov | f138bbc | 2017-02-09 19:13:08 +0300 | [diff] [blame] | 68 | /** |
| 69 | * Returns continuation that wraps the original [continuation], thus intercepting all resumptions. |
| 70 | */ |
Roman Elizarov | aa461cf | 2018-04-11 13:20:29 +0300 | [diff] [blame] | 71 | public override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> = |
Roman Elizarov | f29203c | 2018-01-11 12:39:36 +0300 | [diff] [blame] | 72 | DispatchedContinuation(this, continuation) |
Roman Elizarov | 79a2868 | 2017-02-01 18:43:32 +0300 | [diff] [blame] | 73 | |
Roman Elizarov | 32d9532 | 2017-02-09 15:57:31 +0300 | [diff] [blame] | 74 | /** |
Roman Elizarov | fc7a9a2 | 2017-02-13 11:54:01 +0300 | [diff] [blame] | 75 | * @suppress **Error**: Operator '+' on two CoroutineDispatcher objects is meaningless. |
Roman Elizarov | 32d9532 | 2017-02-09 15:57:31 +0300 | [diff] [blame] | 76 | * CoroutineDispatcher is a coroutine context element and `+` is a set-sum operator for coroutine contexts. |
| 77 | * The dispatcher to the right of `+` just replaces the dispatcher the left of `+`. |
| 78 | */ |
Roman Elizarov | 79a2868 | 2017-02-01 18:43:32 +0300 | [diff] [blame] | 79 | @Suppress("DeprecatedCallableAddReplaceWith") |
Roman Elizarov | f29203c | 2018-01-11 12:39:36 +0300 | [diff] [blame] | 80 | @Deprecated( |
| 81 | message = "Operator '+' on two CoroutineDispatcher objects is meaningless. " + |
Roman Elizarov | 79a2868 | 2017-02-01 18:43:32 +0300 | [diff] [blame] | 82 | "CoroutineDispatcher is a coroutine context element and `+` is a set-sum operator for coroutine contexts. " + |
Roman Elizarov | 32d9532 | 2017-02-09 15:57:31 +0300 | [diff] [blame] | 83 | "The dispatcher to the right of `+` just replaces the dispatcher the left of `+`.", |
Roman Elizarov | f29203c | 2018-01-11 12:39:36 +0300 | [diff] [blame] | 84 | level = DeprecationLevel.ERROR |
| 85 | ) |
Roman Elizarov | 79a2868 | 2017-02-01 18:43:32 +0300 | [diff] [blame] | 86 | public operator fun plus(other: CoroutineDispatcher) = other |
Roman Elizarov | dc9fd1c | 2017-04-07 10:35:28 +0300 | [diff] [blame] | 87 | |
| 88 | // for nicer debugging |
Roman Elizarov | aa461cf | 2018-04-11 13:20:29 +0300 | [diff] [blame] | 89 | override fun toString(): String = "$classSimpleName@$hexAddress" |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 90 | } |
| 91 | |