Roman Elizarov | f16fd27 | 2017-02-07 11:26:00 +0300 | [diff] [blame] | 1 | /* |
Roman Elizarov | cd2a8d7 | 2019-02-12 15:17:17 +0300 | [diff] [blame] | 2 | * Copyright 2016-2019 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 | aa461cf | 2018-04-11 13:20:29 +0300 | [diff] [blame] | 5 | @file:JvmMultifileClass |
| 6 | @file:JvmName("JobKt") |
Vsevolod Tolstopyatov | b058ba1 | 2018-10-17 15:15:17 +0300 | [diff] [blame] | 7 | @file:Suppress("DEPRECATION_ERROR", "RedundantUnitReturnType") |
Roman Elizarov | f161c9f | 2018-02-07 23:27:10 +0300 | [diff] [blame] | 8 | |
Roman Elizarov | 0950dfa | 2018-07-13 10:33:25 +0300 | [diff] [blame] | 9 | package kotlinx.coroutines |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 10 | |
Roman Elizarov | 0950dfa | 2018-07-13 10:33:25 +0300 | [diff] [blame] | 11 | import kotlinx.coroutines.selects.* |
| 12 | import kotlin.coroutines.* |
Vsevolod Tolstopyatov | 36c3ba1 | 2018-10-14 23:45:28 +0300 | [diff] [blame] | 13 | import kotlin.jvm.* |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 14 | |
| 15 | // --------------- core job interfaces --------------- |
| 16 | |
| 17 | /** |
Roman Elizarov | ecbc85c | 2018-09-14 12:52:50 +0300 | [diff] [blame] | 18 | * A background job. Conceptually, a job is a cancellable thing with a life-cycle that |
Roman Elizarov | 852fae5 | 2018-09-25 20:13:24 +0300 | [diff] [blame] | 19 | * culminates in its completion. |
| 20 | * |
| 21 | * Jobs can be arranged into parent-child hierarchies where cancellation |
Yanis Batura | 8140cfe | 2019-04-27 21:31:14 +0700 | [diff] [blame] | 22 | * of a parent leads to immediate cancellation of all its [children]. Failure or cancellation of a child |
| 23 | * with an exception other than [CancellationException] immediately cancels its parent. This way, a parent |
Roman Elizarov | 852fae5 | 2018-09-25 20:13:24 +0300 | [diff] [blame] | 24 | * can [cancel] its own children (including all their children recursively) without cancelling itself. |
Roman Elizarov | 44ba4b1 | 2017-01-25 11:37:54 +0300 | [diff] [blame] | 25 | * |
Roman Elizarov | c32579e | 2018-09-09 19:21:43 +0300 | [diff] [blame] | 26 | * The most basic instances of [Job] are created with [launch][CoroutineScope.launch] coroutine builder or with a |
Inego | 39f92ca | 2019-04-21 13:30:55 +0700 | [diff] [blame] | 27 | * `Job()` factory function. By default, a failure of any of the job's children leads to an immediate failure |
Roman Elizarov | 21f171c | 2018-09-23 19:16:04 +0300 | [diff] [blame] | 28 | * of its parent and cancellation of the rest of its children. This behavior can be customized using [SupervisorJob]. |
| 29 | * |
Roman Elizarov | ecbc85c | 2018-09-14 12:52:50 +0300 | [diff] [blame] | 30 | * Conceptually, an execution of the job does not produce a result value. Jobs are launched solely for their |
| 31 | * side-effects. See [Deferred] interface for a job that produces a result. |
Roman Elizarov | 32d9532 | 2017-02-09 15:57:31 +0300 | [diff] [blame] | 32 | * |
Roman Elizarov | d82b3a9 | 2017-06-23 21:52:08 +0300 | [diff] [blame] | 33 | * A job has the following states: |
Roman Elizarov | 32d9532 | 2017-02-09 15:57:31 +0300 | [diff] [blame] | 34 | * |
Roman Elizarov | 6685fd0 | 2018-09-25 13:23:53 +0300 | [diff] [blame] | 35 | * | **State** | [isActive] | [isCompleted] | [isCancelled] | |
| 36 | * | -------------------------------- | ---------- | ------------- | ------------- | |
| 37 | * | _New_ (optional initial state) | `false` | `false` | `false` | |
| 38 | * | _Active_ (default initial state) | `true` | `false` | `false` | |
| 39 | * | _Completing_ (transient state) | `true` | `false` | `false` | |
| 40 | * | _Cancelling_ (transient state) | `false` | `false` | `true` | |
| 41 | * | _Cancelled_ (final state) | `false` | `true` | `true` | |
| 42 | * | _Completed_ (final state) | `false` | `true` | `false` | |
Roman Elizarov | d82b3a9 | 2017-06-23 21:52:08 +0300 | [diff] [blame] | 43 | * |
| 44 | * Usually, a job is created in _active_ state (it is created and started). However, coroutine builders |
| 45 | * that provide an optional `start` parameter create a coroutine in _new_ state when this parameter is set to |
| 46 | * [CoroutineStart.LAZY]. Such a job can be made _active_ by invoking [start] or [join]. |
| 47 | * |
Roman Elizarov | 852fae5 | 2018-09-25 20:13:24 +0300 | [diff] [blame] | 48 | * A job is _active_ while the coroutine is working. Failure of the job with exception makes it _cancelling_. |
Inego | 39f92ca | 2019-04-21 13:30:55 +0700 | [diff] [blame] | 49 | * A job can be cancelled at any time with [cancel] function that forces it to transition to |
| 50 | * _cancelling_ state immediately. The job becomes _cancelled_ when it finishes executing its work. |
Roman Elizarov | d82b3a9 | 2017-06-23 21:52:08 +0300 | [diff] [blame] | 51 | * |
| 52 | * ``` |
Roman Elizarov | ecbc85c | 2018-09-14 12:52:50 +0300 | [diff] [blame] | 53 | * wait children |
| 54 | * +-----+ start +--------+ complete +-------------+ finish +-----------+ |
| 55 | * | New | -----> | Active | ---------> | Completing | -------> | Completed | |
| 56 | * +-----+ +--------+ +-------------+ +-----------+ |
Roman Elizarov | 852fae5 | 2018-09-25 20:13:24 +0300 | [diff] [blame] | 57 | * | cancel / fail | |
Roman Elizarov | 6685fd0 | 2018-09-25 13:23:53 +0300 | [diff] [blame] | 58 | * | +----------------+ |
Roman Elizarov | ecbc85c | 2018-09-14 12:52:50 +0300 | [diff] [blame] | 59 | * | | |
| 60 | * V V |
| 61 | * +------------+ finish +-----------+ |
| 62 | * | Cancelling | --------------------------------> | Cancelled | |
| 63 | * +------------+ +-----------+ |
Roman Elizarov | d82b3a9 | 2017-06-23 21:52:08 +0300 | [diff] [blame] | 64 | * ``` |
Roman Elizarov | b7c46de | 2017-02-08 12:35:24 +0300 | [diff] [blame] | 65 | * |
Roman Elizarov | ecbc85c | 2018-09-14 12:52:50 +0300 | [diff] [blame] | 66 | * A `Job` instance in the |
Roman Elizarov | 0950dfa | 2018-07-13 10:33:25 +0300 | [diff] [blame] | 67 | * [coroutineContext](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.coroutines/coroutine-context.html) |
Roman Elizarov | 9fe5f46 | 2018-02-21 19:05:52 +0300 | [diff] [blame] | 68 | * represents the coroutine itself. |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 69 | * |
Roman Elizarov | 6685fd0 | 2018-09-25 13:23:53 +0300 | [diff] [blame] | 70 | * A job can have a _parent_ job. A job with a parent is cancelled when its parent is cancelled. |
| 71 | * Parent job waits in _completing_ or _cancelling_ state for all its children to complete before finishing. |
Inego | ebe519a | 2019-04-21 13:22:27 +0700 | [diff] [blame] | 72 | * Note that _completing_ state is purely internal to the job. For an outside observer a _completing_ job is still |
Roman Elizarov | ecbc85c | 2018-09-14 12:52:50 +0300 | [diff] [blame] | 73 | * active, while internally it is waiting for its children. |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 74 | * |
Roman Elizarov | 852fae5 | 2018-09-25 20:13:24 +0300 | [diff] [blame] | 75 | * Normal cancellation of a job is distinguished from its failure by the type of its cancellation exception cause. |
| 76 | * If the cause of cancellation is [CancellationException], then the job is considered to be _cancelled normally_. |
| 77 | * This usually happens when [cancel] is invoked without additional parameters. If the cause of cancellation is |
| 78 | * a different exception, then the job is considered to have _failed_. This usually happens when the code of the job |
| 79 | * encounters some problem and throws an exception. |
| 80 | * |
Roman Elizarov | b7c46de | 2017-02-08 12:35:24 +0300 | [diff] [blame] | 81 | * All functions on this interface and on all interfaces derived from it are **thread-safe** and can |
| 82 | * be safely invoked from concurrent coroutines without external synchronization. |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 83 | */ |
Roman Elizarov | aa461cf | 2018-04-11 13:20:29 +0300 | [diff] [blame] | 84 | public interface Job : CoroutineContext.Element { |
Roman Elizarov | d528e3e | 2017-01-23 15:40:05 +0300 | [diff] [blame] | 85 | /** |
| 86 | * Key for [Job] instance in the coroutine context. |
| 87 | */ |
Roman Elizarov | aa461cf | 2018-04-11 13:20:29 +0300 | [diff] [blame] | 88 | public companion object Key : CoroutineContext.Key<Job> { |
Roman Elizarov | 11c140a | 2017-07-21 21:12:55 +0300 | [diff] [blame] | 89 | init { |
| 90 | /* |
| 91 | * Here we make sure that CoroutineExceptionHandler is always initialized in advance, so |
| 92 | * that if a coroutine fails due to StackOverflowError we don't fail to report this error |
| 93 | * trying to initialize CoroutineExceptionHandler |
| 94 | */ |
| 95 | CoroutineExceptionHandler |
| 96 | } |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 97 | } |
| 98 | |
Roman Elizarov | d82b3a9 | 2017-06-23 21:52:08 +0300 | [diff] [blame] | 99 | // ------------ state query ------------ |
| 100 | |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 101 | /** |
Roman Elizarov | 852fae5 | 2018-09-25 20:13:24 +0300 | [diff] [blame] | 102 | * Returns `true` when this job is active -- it was already started and has not completed nor was cancelled yet. |
Roman Elizarov | 3e387b8 | 2017-12-04 13:49:11 +0300 | [diff] [blame] | 103 | * The job that is waiting for its [children] to complete is still considered to be active if it |
Roman Elizarov | 852fae5 | 2018-09-25 20:13:24 +0300 | [diff] [blame] | 104 | * was not cancelled nor failed. |
| 105 | * |
| 106 | * See [Job] documentation for more details on job states. |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 107 | */ |
Roman Elizarov | aa461cf | 2018-04-11 13:20:29 +0300 | [diff] [blame] | 108 | public val isActive: Boolean |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 109 | |
| 110 | /** |
Roman Elizarov | 852fae5 | 2018-09-25 20:13:24 +0300 | [diff] [blame] | 111 | * Returns `true` when this job has completed for any reason. A job that was cancelled or failed |
Roman Elizarov | ecbc85c | 2018-09-14 12:52:50 +0300 | [diff] [blame] | 112 | * and has finished its execution is also considered complete. Job becomes complete only after |
Roman Elizarov | 3e387b8 | 2017-12-04 13:49:11 +0300 | [diff] [blame] | 113 | * all its [children] complete. |
Roman Elizarov | 852fae5 | 2018-09-25 20:13:24 +0300 | [diff] [blame] | 114 | * |
| 115 | * See [Job] documentation for more details on job states. |
Roman Elizarov | 32d9532 | 2017-02-09 15:57:31 +0300 | [diff] [blame] | 116 | */ |
Roman Elizarov | aa461cf | 2018-04-11 13:20:29 +0300 | [diff] [blame] | 117 | public val isCompleted: Boolean |
Roman Elizarov | 32d9532 | 2017-02-09 15:57:31 +0300 | [diff] [blame] | 118 | |
| 119 | /** |
Roman Elizarov | 6685fd0 | 2018-09-25 13:23:53 +0300 | [diff] [blame] | 120 | * Returns `true` if this job was cancelled for any reason, either by explicit invocation of [cancel] or |
Roman Elizarov | 852fae5 | 2018-09-25 20:13:24 +0300 | [diff] [blame] | 121 | * because it had failed or its child or parent was cancelled. |
Roman Elizarov | ecbc85c | 2018-09-14 12:52:50 +0300 | [diff] [blame] | 122 | * In the general case, it does not imply that the |
| 123 | * job has already [completed][isCompleted], because it may still be finishing whatever it was doing and |
| 124 | * waiting for its [children] to complete. |
Roman Elizarov | 852fae5 | 2018-09-25 20:13:24 +0300 | [diff] [blame] | 125 | * |
| 126 | * See [Job] documentation for more details on cancellation and failures. |
Roman Elizarov | ecbc85c | 2018-09-14 12:52:50 +0300 | [diff] [blame] | 127 | */ |
Roman Elizarov | aa461cf | 2018-04-11 13:20:29 +0300 | [diff] [blame] | 128 | public val isCancelled: Boolean |
Roman Elizarov | d82b3a9 | 2017-06-23 21:52:08 +0300 | [diff] [blame] | 129 | |
| 130 | /** |
Roman Elizarov | 9832d3d | 2017-12-20 11:03:21 +0300 | [diff] [blame] | 131 | * Returns [CancellationException] that signals the completion of this job. This function is |
| 132 | * used by [cancellable][suspendCancellableCoroutine] suspending functions. They throw exception |
| 133 | * returned by this function when they suspend in the context of this job and this job becomes _complete_. |
Roman Elizarov | 8b38fa2 | 2017-09-27 17:44:31 +0300 | [diff] [blame] | 134 | * |
Roman Elizarov | 9832d3d | 2017-12-20 11:03:21 +0300 | [diff] [blame] | 135 | * This function returns the original [cancel] cause of this job if that `cause` was an instance of |
| 136 | * [CancellationException]. Otherwise (if this job was cancelled with a cause of a different type, or |
Vsevolod Tolstopyatov | 149ba48 | 2018-09-24 20:28:02 +0300 | [diff] [blame] | 137 | * was cancelled without a cause, or had completed normally), an instance of [CancellationException] is |
| 138 | * returned. The [CancellationException.cause] of the resulting [CancellationException] references |
Roman Elizarov | 9832d3d | 2017-12-20 11:03:21 +0300 | [diff] [blame] | 139 | * the original cancellation cause that was passed to [cancel] function. |
Roman Elizarov | 8b38fa2 | 2017-09-27 17:44:31 +0300 | [diff] [blame] | 140 | * |
Roman Elizarov | ecbc85c | 2018-09-14 12:52:50 +0300 | [diff] [blame] | 141 | * This function throws [IllegalStateException] when invoked on a job that is still active. |
| 142 | * |
Roman Elizarov | 27b8f45 | 2018-09-20 21:23:41 +0300 | [diff] [blame] | 143 | * @suppress **This an internal API and should not be used from general code.** |
Roman Elizarov | d82b3a9 | 2017-06-23 21:52:08 +0300 | [diff] [blame] | 144 | */ |
Roman Elizarov | 27b8f45 | 2018-09-20 21:23:41 +0300 | [diff] [blame] | 145 | @InternalCoroutinesApi |
Roman Elizarov | aa461cf | 2018-04-11 13:20:29 +0300 | [diff] [blame] | 146 | public fun getCancellationException(): CancellationException |
Roman Elizarov | 8b38fa2 | 2017-09-27 17:44:31 +0300 | [diff] [blame] | 147 | |
Roman Elizarov | d82b3a9 | 2017-06-23 21:52:08 +0300 | [diff] [blame] | 148 | // ------------ state update ------------ |
| 149 | |
| 150 | /** |
Roman Elizarov | 32d9532 | 2017-02-09 15:57:31 +0300 | [diff] [blame] | 151 | * Starts coroutine related to this job (if any) if it was not started yet. |
| 152 | * The result `true` if this invocation actually started coroutine or `false` |
| 153 | * if it was already started or completed. |
| 154 | */ |
Roman Elizarov | aa461cf | 2018-04-11 13:20:29 +0300 | [diff] [blame] | 155 | public fun start(): Boolean |
Roman Elizarov | 32d9532 | 2017-02-09 15:57:31 +0300 | [diff] [blame] | 156 | |
Roman Elizarov | 27b8f45 | 2018-09-20 21:23:41 +0300 | [diff] [blame] | 157 | |
| 158 | /** |
Roman Elizarov | 0aad8f1 | 2019-03-01 12:08:43 +0300 | [diff] [blame] | 159 | * Cancels this job with an optional cancellation [cause]. |
| 160 | * A cause can be used to specify an error message or to provide other details on |
Inego | 596187e | 2019-04-21 14:21:53 +0700 | [diff] [blame] | 161 | * the cancellation reason for debugging purposes. |
Vsevolod Tolstopyatov | b058ba1 | 2018-10-17 15:15:17 +0300 | [diff] [blame] | 162 | * See [Job] documentation for full explanation of cancellation machinery. |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 163 | */ |
Roman Elizarov | 0aad8f1 | 2019-03-01 12:08:43 +0300 | [diff] [blame] | 164 | public fun cancel(cause: CancellationException? = null) |
Vsevolod Tolstopyatov | b058ba1 | 2018-10-17 15:15:17 +0300 | [diff] [blame] | 165 | |
| 166 | /** |
Roman Elizarov | 0aad8f1 | 2019-03-01 12:08:43 +0300 | [diff] [blame] | 167 | * @suppress This method implements old version of JVM ABI. Use [cancel]. |
Vsevolod Tolstopyatov | b058ba1 | 2018-10-17 15:15:17 +0300 | [diff] [blame] | 168 | */ |
Roman Elizarov | 73b456b | 2019-03-06 17:18:27 +0300 | [diff] [blame] | 169 | @Deprecated(level = DeprecationLevel.HIDDEN, message = "Since 1.2.0, binary compatibility with versions <= 1.1.x") |
Roman Elizarov | 0aad8f1 | 2019-03-01 12:08:43 +0300 | [diff] [blame] | 170 | public fun cancel() = cancel(null) |
| 171 | |
| 172 | /** |
| 173 | * @suppress This method has bad semantics when cause is not a [CancellationException]. Use [cancel]. |
| 174 | */ |
Roman Elizarov | 73b456b | 2019-03-06 17:18:27 +0300 | [diff] [blame] | 175 | @Deprecated(level = DeprecationLevel.HIDDEN, message = "Since 1.2.0, binary compatibility with versions <= 1.1.x") |
Roman Elizarov | aa461cf | 2018-04-11 13:20:29 +0300 | [diff] [blame] | 176 | public fun cancel(cause: Throwable? = null): Boolean |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 177 | |
Roman Elizarov | 8b38fa2 | 2017-09-27 17:44:31 +0300 | [diff] [blame] | 178 | // ------------ parent-child ------------ |
| 179 | |
Roman Elizarov | ecbc85c | 2018-09-14 12:52:50 +0300 | [diff] [blame] | 180 | /** |
Roman Elizarov | 3e387b8 | 2017-12-04 13:49:11 +0300 | [diff] [blame] | 181 | * Returns a sequence of this job's children. |
| 182 | * |
| 183 | * A job becomes a child of this job when it is constructed with this job in its |
| 184 | * [CoroutineContext] or using an explicit `parent` parameter. |
| 185 | * |
| 186 | * A parent-child relation has the following effect: |
| 187 | * |
| 188 | * * Cancellation of parent with [cancel] or its exceptional completion (failure) |
| 189 | * immediately cancels all its children. |
| 190 | * * Parent cannot complete until all its children are complete. Parent waits for all its children to |
| 191 | * complete in _completing_ or _cancelling_ state. |
| 192 | * * Uncaught exception in a child, by default, cancels parent. In particular, this applies to |
Inego | ebe519a | 2019-04-21 13:22:27 +0700 | [diff] [blame] | 193 | * children created with [launch][CoroutineScope.launch] coroutine builder. Note that |
Roman Elizarov | c32579e | 2018-09-09 19:21:43 +0300 | [diff] [blame] | 194 | * [async][CoroutineScope.async] and other future-like |
Roman Elizarov | 3e387b8 | 2017-12-04 13:49:11 +0300 | [diff] [blame] | 195 | * coroutine builders do not have uncaught exceptions by definition, since all their exceptions are |
| 196 | * caught and are encapsulated in their result. |
| 197 | */ |
Roman Elizarov | aa461cf | 2018-04-11 13:20:29 +0300 | [diff] [blame] | 198 | public val children: Sequence<Job> |
Roman Elizarov | 3e387b8 | 2017-12-04 13:49:11 +0300 | [diff] [blame] | 199 | |
| 200 | /** |
Roman Elizarov | 8b38fa2 | 2017-09-27 17:44:31 +0300 | [diff] [blame] | 201 | * Attaches child job so that this job becomes its parent and |
| 202 | * returns a handle that should be used to detach it. |
| 203 | * |
| 204 | * A parent-child relation has the following effect: |
Roman Elizarov | a36ab94 | 2017-10-07 05:44:48 -0700 | [diff] [blame] | 205 | * * Cancellation of parent with [cancel] or its exceptional completion (failure) |
Roman Elizarov | 852fae5 | 2018-09-25 20:13:24 +0300 | [diff] [blame] | 206 | * immediately cancels all its children. |
Roman Elizarov | 8b38fa2 | 2017-09-27 17:44:31 +0300 | [diff] [blame] | 207 | * * Parent cannot complete until all its children are complete. Parent waits for all its children to |
Roman Elizarov | 6685fd0 | 2018-09-25 13:23:53 +0300 | [diff] [blame] | 208 | * complete in _completing_ or _cancelling_ states. |
Roman Elizarov | 8b38fa2 | 2017-09-27 17:44:31 +0300 | [diff] [blame] | 209 | * |
Roman Elizarov | f7a6334 | 2018-09-25 00:26:25 +0300 | [diff] [blame] | 210 | * **A child must store the resulting [ChildHandle] and [dispose][DisposableHandle.dispose] the attachment |
Roman Elizarov | 447b88a | 2017-12-01 15:37:48 +0300 | [diff] [blame] | 211 | * to its parent on its own completion.** |
Roman Elizarov | 8b38fa2 | 2017-09-27 17:44:31 +0300 | [diff] [blame] | 212 | * |
| 213 | * Coroutine builders and job factory functions that accept `parent` [CoroutineContext] parameter |
| 214 | * lookup a [Job] instance in the parent context and use this function to attach themselves as a child. |
Roman Elizarov | f7a6334 | 2018-09-25 00:26:25 +0300 | [diff] [blame] | 215 | * They also store a reference to the resulting [ChildHandle] and dispose a handle when they complete. |
Roman Elizarov | 447b88a | 2017-12-01 15:37:48 +0300 | [diff] [blame] | 216 | * |
| 217 | * @suppress This is an internal API. This method is too error prone for public API. |
Roman Elizarov | 8b38fa2 | 2017-09-27 17:44:31 +0300 | [diff] [blame] | 218 | */ |
Roman Elizarov | f7a6334 | 2018-09-25 00:26:25 +0300 | [diff] [blame] | 219 | // ChildJob and ChildHandle are made internal on purpose to further deter 3rd-party impl of Job |
Roman Elizarov | 27b8f45 | 2018-09-20 21:23:41 +0300 | [diff] [blame] | 220 | @InternalCoroutinesApi |
Roman Elizarov | f7a6334 | 2018-09-25 00:26:25 +0300 | [diff] [blame] | 221 | public fun attachChild(child: ChildJob): ChildHandle |
Roman Elizarov | 8b38fa2 | 2017-09-27 17:44:31 +0300 | [diff] [blame] | 222 | |
Roman Elizarov | d82b3a9 | 2017-06-23 21:52:08 +0300 | [diff] [blame] | 223 | // ------------ state waiting ------------ |
| 224 | |
| 225 | /** |
Inego | 596187e | 2019-04-21 14:21:53 +0700 | [diff] [blame] | 226 | * Suspends the coroutine until this job is complete. This invocation resumes normally (without exception) |
Roman Elizarov | 8b38fa2 | 2017-09-27 17:44:31 +0300 | [diff] [blame] | 227 | * when the job is complete for any reason and the [Job] of the invoking coroutine is still [active][isActive]. |
| 228 | * This function also [starts][Job.start] the corresponding coroutine if the [Job] was still in _new_ state. |
Roman Elizarov | d82b3a9 | 2017-06-23 21:52:08 +0300 | [diff] [blame] | 229 | * |
Inego | ebe519a | 2019-04-21 13:22:27 +0700 | [diff] [blame] | 230 | * Note that the job becomes complete only when all its children are complete. |
Roman Elizarov | 8b38fa2 | 2017-09-27 17:44:31 +0300 | [diff] [blame] | 231 | * |
Inego | 596187e | 2019-04-21 14:21:53 +0700 | [diff] [blame] | 232 | * This suspending function is cancellable and **always** checks for a cancellation of the invoking coroutine's Job. |
Roman Elizarov | 8b38fa2 | 2017-09-27 17:44:31 +0300 | [diff] [blame] | 233 | * If the [Job] of the invoking coroutine is cancelled or completed when this |
| 234 | * suspending function is invoked or while it is suspended, this function |
| 235 | * throws [CancellationException]. |
| 236 | * |
| 237 | * In particular, it means that a parent coroutine invoking `join` on a child coroutine that was started using |
| 238 | * `launch(coroutineContext) { ... }` builder throws [CancellationException] if the child |
Inego | 596187e | 2019-04-21 14:21:53 +0700 | [diff] [blame] | 239 | * had crashed, unless a non-standard [CoroutineExceptionHandler] is installed in the context. |
Roman Elizarov | d82b3a9 | 2017-06-23 21:52:08 +0300 | [diff] [blame] | 240 | * |
Roman Elizarov | db0e22d | 2017-08-29 18:15:57 +0300 | [diff] [blame] | 241 | * This function can be used in [select] invocation with [onJoin] clause. |
Inego | 596187e | 2019-04-21 14:21:53 +0700 | [diff] [blame] | 242 | * Use [isCompleted] to check for a completion of this job without waiting. |
Roman Elizarov | 8b38fa2 | 2017-09-27 17:44:31 +0300 | [diff] [blame] | 243 | * |
| 244 | * There is [cancelAndJoin] function that combines an invocation of [cancel] and `join`. |
Roman Elizarov | d82b3a9 | 2017-06-23 21:52:08 +0300 | [diff] [blame] | 245 | */ |
Roman Elizarov | aa461cf | 2018-04-11 13:20:29 +0300 | [diff] [blame] | 246 | public suspend fun join() |
Roman Elizarov | d82b3a9 | 2017-06-23 21:52:08 +0300 | [diff] [blame] | 247 | |
Roman Elizarov | db0e22d | 2017-08-29 18:15:57 +0300 | [diff] [blame] | 248 | /** |
| 249 | * Clause for [select] expression of [join] suspending function that selects when the job is complete. |
| 250 | * This clause never fails, even if the job completes exceptionally. |
| 251 | */ |
| 252 | public val onJoin: SelectClause0 |
| 253 | |
Roman Elizarov | d82b3a9 | 2017-06-23 21:52:08 +0300 | [diff] [blame] | 254 | // ------------ low-level state-notification ------------ |
| 255 | |
Roman Elizarov | 00e90dd | 2017-12-22 14:38:21 +0300 | [diff] [blame] | 256 | /** |
Roman Elizarov | e1c0b65 | 2017-12-01 14:02:57 +0300 | [diff] [blame] | 257 | * Registers handler that is **synchronously** invoked once on completion of this job. |
Yanis Batura | 8140cfe | 2019-04-27 21:31:14 +0700 | [diff] [blame] | 258 | * When the job is already complete, then the handler is immediately invoked |
| 259 | * with the job's exception or cancellation cause or `null`. Otherwise, the handler will be invoked once when this |
Roman Elizarov | e1c0b65 | 2017-12-01 14:02:57 +0300 | [diff] [blame] | 260 | * job is complete. |
| 261 | * |
Roman Elizarov | e89cd68 | 2018-04-25 13:03:40 +0300 | [diff] [blame] | 262 | * The meaning of `cause` that is passed to the handler: |
Yanis Batura | 8140cfe | 2019-04-27 21:31:14 +0700 | [diff] [blame] | 263 | * * Cause is `null` when the job has completed normally. |
| 264 | * * Cause is an instance of [CancellationException] when the job was cancelled _normally_. |
Roman Elizarov | e89cd68 | 2018-04-25 13:03:40 +0300 | [diff] [blame] | 265 | * **It should not be treated as an error**. In particular, it should not be reported to error logs. |
| 266 | * * Otherwise, the job had _failed_. |
| 267 | * |
Roman Elizarov | e1c0b65 | 2017-12-01 14:02:57 +0300 | [diff] [blame] | 268 | * The resulting [DisposableHandle] can be used to [dispose][DisposableHandle.dispose] the |
| 269 | * registration of this handler and release its memory if its invocation is no longer needed. |
| 270 | * There is no need to dispose the handler after completion of this job. The references to |
| 271 | * all the handlers are released when this job completes. |
| 272 | * |
| 273 | * Installed [handler] should not throw any exceptions. If it does, they will get caught, |
| 274 | * wrapped into [CompletionHandlerException], and rethrown, potentially causing crash of unrelated code. |
| 275 | * |
Roman Elizarov | 2b8218a | 2019-04-11 01:41:22 +0300 | [diff] [blame] | 276 | * **Note**: Implementation of `CompletionHandler` must be fast, non-blocking, and thread-safe. |
| 277 | * This handler can be invoked concurrently with the surrounding code. |
| 278 | * There is no guarantee on the execution context in which the [handler] is invoked. |
Roman Elizarov | e1c0b65 | 2017-12-01 14:02:57 +0300 | [diff] [blame] | 279 | */ |
| 280 | public fun invokeOnCompletion(handler: CompletionHandler): DisposableHandle |
Roman Elizarov | 8b38fa2 | 2017-09-27 17:44:31 +0300 | [diff] [blame] | 281 | |
Roman Elizarov | 29affbb | 2017-07-21 13:47:41 +0300 | [diff] [blame] | 282 | /** |
Roman Elizarov | 852fae5 | 2018-09-25 20:13:24 +0300 | [diff] [blame] | 283 | * Registers handler that is **synchronously** invoked once on cancellation or completion of this job. |
Yanis Batura | 8140cfe | 2019-04-27 21:31:14 +0700 | [diff] [blame] | 284 | * when the job was already cancelled and is completed its execution, then the handler is immediately invoked |
| 285 | * with the job's cancellation cause or `null` unless [invokeImmediately] is set to false. |
Roman Elizarov | 852fae5 | 2018-09-25 20:13:24 +0300 | [diff] [blame] | 286 | * Otherwise, handler will be invoked once when this job is cancelled or is complete. |
Roman Elizarov | 29affbb | 2017-07-21 13:47:41 +0300 | [diff] [blame] | 287 | * |
Roman Elizarov | e89cd68 | 2018-04-25 13:03:40 +0300 | [diff] [blame] | 288 | * The meaning of `cause` that is passed to the handler: |
Yanis Batura | 8140cfe | 2019-04-27 21:31:14 +0700 | [diff] [blame] | 289 | * * Cause is `null` when the job has completed normally. |
| 290 | * * Cause is an instance of [CancellationException] when the job was cancelled _normally_. |
Roman Elizarov | e89cd68 | 2018-04-25 13:03:40 +0300 | [diff] [blame] | 291 | * **It should not be treated as an error**. In particular, it should not be reported to error logs. |
| 292 | * * Otherwise, the job had _failed_. |
| 293 | * |
Roman Elizarov | 852fae5 | 2018-09-25 20:13:24 +0300 | [diff] [blame] | 294 | * Invocation of this handler on a transition to a _cancelling_ state |
Roman Elizarov | 6685fd0 | 2018-09-25 13:23:53 +0300 | [diff] [blame] | 295 | * is controlled by [onCancelling] boolean parameter. |
Roman Elizarov | 852fae5 | 2018-09-25 20:13:24 +0300 | [diff] [blame] | 296 | * The handler is invoked when the job becomes _cancelling_ if [onCancelling] parameter is set to `true`. |
Roman Elizarov | d82b3a9 | 2017-06-23 21:52:08 +0300 | [diff] [blame] | 297 | * |
| 298 | * The resulting [DisposableHandle] can be used to [dispose][DisposableHandle.dispose] the |
| 299 | * registration of this handler and release its memory if its invocation is no longer needed. |
| 300 | * There is no need to dispose the handler after completion of this job. The references to |
| 301 | * all the handlers are released when this job completes. |
| 302 | * |
Roman Elizarov | 8b38fa2 | 2017-09-27 17:44:31 +0300 | [diff] [blame] | 303 | * Installed [handler] should not throw any exceptions. If it does, they will get caught, |
| 304 | * wrapped into [CompletionHandlerException], and rethrown, potentially causing crash of unrelated code. |
| 305 | * |
Roman Elizarov | d82b3a9 | 2017-06-23 21:52:08 +0300 | [diff] [blame] | 306 | * **Note**: This function is a part of internal machinery that supports parent-child hierarchies |
| 307 | * and allows for implementation of suspending functions that wait on the Job's state. |
| 308 | * This function should not be used in general application code. |
Roman Elizarov | 2b8218a | 2019-04-11 01:41:22 +0300 | [diff] [blame] | 309 | * Implementation of `CompletionHandler` must be fast, non-blocking, and thread-safe. |
| 310 | * This handler can be invoked concurrently with the surrounding code. |
| 311 | * There is no guarantee on the execution context in which the [handler] is invoked. |
Roman Elizarov | e1c0b65 | 2017-12-01 14:02:57 +0300 | [diff] [blame] | 312 | * |
Roman Elizarov | 852fae5 | 2018-09-25 20:13:24 +0300 | [diff] [blame] | 313 | * @param onCancelling when `true`, then the [handler] is invoked as soon as this job transitions to _cancelling_ state; |
Roman Elizarov | e1c0b65 | 2017-12-01 14:02:57 +0300 | [diff] [blame] | 314 | * when `false` then the [handler] is invoked only when it transitions to _completed_ state. |
Roman Elizarov | 6685fd0 | 2018-09-25 13:23:53 +0300 | [diff] [blame] | 315 | * @param invokeImmediately when `true` and this job is already in the desired state (depending on [onCancelling]), |
Vsevolod Tolstopyatov | 149ba48 | 2018-09-24 20:28:02 +0300 | [diff] [blame] | 316 | * then the [handler] is immediately and synchronously invoked and no-op [DisposableHandle] is returned; |
| 317 | * when `false` then no-op [DisposableHandle] is returned, but the [handler] is not invoked. |
Roman Elizarov | e1c0b65 | 2017-12-01 14:02:57 +0300 | [diff] [blame] | 318 | * @param handler the handler. |
Roman Elizarov | 1e0a2f0 | 2018-09-25 16:10:26 +0300 | [diff] [blame] | 319 | * |
Roman Elizarov | 27b8f45 | 2018-09-20 21:23:41 +0300 | [diff] [blame] | 320 | * @suppress **This an internal API and should not be used from general code.** |
Roman Elizarov | d82b3a9 | 2017-06-23 21:52:08 +0300 | [diff] [blame] | 321 | */ |
Roman Elizarov | 27b8f45 | 2018-09-20 21:23:41 +0300 | [diff] [blame] | 322 | @InternalCoroutinesApi |
Roman Elizarov | aa461cf | 2018-04-11 13:20:29 +0300 | [diff] [blame] | 323 | public fun invokeOnCompletion( |
Roman Elizarov | 6685fd0 | 2018-09-25 13:23:53 +0300 | [diff] [blame] | 324 | onCancelling: Boolean = false, |
Roman Elizarov | e1c0b65 | 2017-12-01 14:02:57 +0300 | [diff] [blame] | 325 | invokeImmediately: Boolean = true, |
| 326 | handler: CompletionHandler): DisposableHandle |
Roman Elizarov | d82b3a9 | 2017-06-23 21:52:08 +0300 | [diff] [blame] | 327 | |
| 328 | // ------------ unstable internal API ------------ |
| 329 | |
| 330 | /** |
Roman Elizarov | fc7a9a2 | 2017-02-13 11:54:01 +0300 | [diff] [blame] | 331 | * @suppress **Error**: Operator '+' on two Job objects is meaningless. |
Roman Elizarov | 32d9532 | 2017-02-09 15:57:31 +0300 | [diff] [blame] | 332 | * Job is a coroutine context element and `+` is a set-sum operator for coroutine contexts. |
| 333 | * The job to the right of `+` just replaces the job the left of `+`. |
| 334 | */ |
Roman Elizarov | 79a2868 | 2017-02-01 18:43:32 +0300 | [diff] [blame] | 335 | @Suppress("DeprecatedCallableAddReplaceWith") |
| 336 | @Deprecated(message = "Operator '+' on two Job objects is meaningless. " + |
Roman Elizarov | aa461cf | 2018-04-11 13:20:29 +0300 | [diff] [blame] | 337 | "Job is a coroutine context element and `+` is a set-sum operator for coroutine contexts. " + |
| 338 | "The job to the right of `+` just replaces the job the left of `+`.", |
| 339 | level = DeprecationLevel.ERROR) |
Roman Elizarov | 79a2868 | 2017-02-01 18:43:32 +0300 | [diff] [blame] | 340 | public operator fun plus(other: Job) = other |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 341 | } |
| 342 | |
Roman Elizarov | 32d9532 | 2017-02-09 15:57:31 +0300 | [diff] [blame] | 343 | /** |
Inego | 564a5a6 | 2019-04-21 14:34:55 +0700 | [diff] [blame] | 344 | * Creates a job object in an active state. |
Roman Elizarov | 21f171c | 2018-09-23 19:16:04 +0300 | [diff] [blame] | 345 | * A failure of any child of this job immediately causes this job to fail, too, and cancels the rest of its children. |
| 346 | * |
| 347 | * To handle children failure independently of each other use [SupervisorJob]. |
| 348 | * |
| 349 | * If [parent] job is specified, then this job becomes a child job of its parent and |
| 350 | * is cancelled when its parent fails or is cancelled. All this job's children are cancelled in this case, too. |
| 351 | * The invocation of [cancel][Job.cancel] with exception (other than [CancellationException]) on this job also cancels parent. |
| 352 | * |
Roman Elizarov | cd2a8d7 | 2019-02-12 15:17:17 +0300 | [diff] [blame] | 353 | * Conceptually, the resulting job works in the same way as the job created by the `launch { body }` invocation |
| 354 | * (see [launch]), but without any code in the body. It is active until cancelled or completed. Invocation of |
| 355 | * [CompletableJob.complete] or [CompletableJob.completeExceptionally] corresponds to the successful or |
| 356 | * failed completion of the body of the coroutine. |
| 357 | * |
Roman Elizarov | 21f171c | 2018-09-23 19:16:04 +0300 | [diff] [blame] | 358 | * @param parent an optional parent job. |
Roman Elizarov | eb6bf84 | 2017-07-21 18:10:22 +0300 | [diff] [blame] | 359 | */ |
Roman Elizarov | 8b38fa2 | 2017-09-27 17:44:31 +0300 | [diff] [blame] | 360 | @Suppress("FunctionName") |
Roman Elizarov | cd2a8d7 | 2019-02-12 15:17:17 +0300 | [diff] [blame] | 361 | public fun Job(parent: Job? = null): CompletableJob = JobImpl(parent) |
| 362 | |
| 363 | /** @suppress Binary compatibility only */ |
| 364 | @Suppress("FunctionName") |
Roman Elizarov | 73b456b | 2019-03-06 17:18:27 +0300 | [diff] [blame] | 365 | @Deprecated(level = DeprecationLevel.HIDDEN, message = "Since 1.2.0, binary compatibility with versions <= 1.1.x") |
Roman Elizarov | cd2a8d7 | 2019-02-12 15:17:17 +0300 | [diff] [blame] | 366 | @JvmName("Job") |
| 367 | public fun Job0(parent: Job? = null): Job = Job(parent) |
Roman Elizarov | eb6bf84 | 2017-07-21 18:10:22 +0300 | [diff] [blame] | 368 | |
| 369 | /** |
Roman Elizarov | daa1d9d | 2017-03-02 19:00:50 +0300 | [diff] [blame] | 370 | * A handle to an allocated object that can be disposed to make it eligible for garbage collection. |
| 371 | */ |
Roman Elizarov | aa461cf | 2018-04-11 13:20:29 +0300 | [diff] [blame] | 372 | public interface DisposableHandle { |
Roman Elizarov | daa1d9d | 2017-03-02 19:00:50 +0300 | [diff] [blame] | 373 | /** |
| 374 | * Disposes the corresponding object, making it eligible for garbage collection. |
| 375 | * Repeated invocation of this function has no effect. |
| 376 | */ |
Roman Elizarov | aa461cf | 2018-04-11 13:20:29 +0300 | [diff] [blame] | 377 | public fun dispose() |
Roman Elizarov | daa1d9d | 2017-03-02 19:00:50 +0300 | [diff] [blame] | 378 | } |
| 379 | |
Roman Elizarov | 5d18d02 | 2018-09-22 22:13:05 +0300 | [diff] [blame] | 380 | /** |
Roman Elizarov | 27b8f45 | 2018-09-20 21:23:41 +0300 | [diff] [blame] | 381 | * @suppress **This an internal API and should not be used from general code.** |
| 382 | */ |
| 383 | @Suppress("FunctionName") |
| 384 | @InternalCoroutinesApi |
| 385 | public inline fun DisposableHandle(crossinline block: () -> Unit) = |
| 386 | object : DisposableHandle { |
| 387 | override fun dispose() { |
| 388 | block() |
| 389 | } |
| 390 | } |
| 391 | |
Roman Elizarov | 1e0a2f0 | 2018-09-25 16:10:26 +0300 | [diff] [blame] | 392 | // -------------------- Parent-child communication -------------------- |
| 393 | |
| 394 | /** |
Roman Elizarov | 852fae5 | 2018-09-25 20:13:24 +0300 | [diff] [blame] | 395 | * A reference that parent receives from its child so that it can report its cancellation. |
Roman Elizarov | f7a6334 | 2018-09-25 00:26:25 +0300 | [diff] [blame] | 396 | * |
Roman Elizarov | 5d18d02 | 2018-09-22 22:13:05 +0300 | [diff] [blame] | 397 | * @suppress **This is unstable API and it is subject to change.** |
| 398 | */ |
Vsevolod Tolstopyatov | b5d10d4 | 2018-09-28 16:28:16 +0300 | [diff] [blame] | 399 | @InternalCoroutinesApi |
Vsevolod Tolstopyatov | 7fb590d | 2018-10-11 18:22:46 +0300 | [diff] [blame] | 400 | @Deprecated(level = DeprecationLevel.ERROR, message = "This is internal API and may be removed in the future releases") |
| 401 | public interface ChildJob : Job { |
Roman Elizarov | f7a6334 | 2018-09-25 00:26:25 +0300 | [diff] [blame] | 402 | /** |
Roman Elizarov | 852fae5 | 2018-09-25 20:13:24 +0300 | [diff] [blame] | 403 | * Parent is cancelling its child by invoking this method. |
Roman Elizarov | b5c9c9f | 2018-09-29 13:16:47 +0300 | [diff] [blame] | 404 | * Child finds the cancellation cause using [ParentJob.getChildJobCancellationCause]. |
Roman Elizarov | 852fae5 | 2018-09-25 20:13:24 +0300 | [diff] [blame] | 405 | * This method does nothing is the child is already being cancelled. |
Roman Elizarov | f7a6334 | 2018-09-25 00:26:25 +0300 | [diff] [blame] | 406 | * |
| 407 | * @suppress **This is unstable API and it is subject to change.** |
| 408 | */ |
Vsevolod Tolstopyatov | b5d10d4 | 2018-09-28 16:28:16 +0300 | [diff] [blame] | 409 | @InternalCoroutinesApi |
Roman Elizarov | b5c9c9f | 2018-09-29 13:16:47 +0300 | [diff] [blame] | 410 | public fun parentCancelled(parentJob: ParentJob) |
| 411 | } |
| 412 | |
| 413 | /** |
| 414 | * A reference that child receives from its parent when it is being cancelled by the parent. |
| 415 | * |
| 416 | * @suppress **This is unstable API and it is subject to change.** |
| 417 | */ |
| 418 | @InternalCoroutinesApi |
Vsevolod Tolstopyatov | 7fb590d | 2018-10-11 18:22:46 +0300 | [diff] [blame] | 419 | @Deprecated(level = DeprecationLevel.ERROR, message = "This is internal API and may be removed in the future releases") |
| 420 | public interface ParentJob : Job { |
Roman Elizarov | b5c9c9f | 2018-09-29 13:16:47 +0300 | [diff] [blame] | 421 | /** |
| 422 | * Child job is using this method to learn its cancellation cause when the parent cancels it with [ChildJob.parentCancelled]. |
| 423 | * This method is invoked only if the child was not already being cancelled. |
| 424 | * |
| 425 | * @suppress **This is unstable API and it is subject to change.** |
| 426 | */ |
| 427 | @InternalCoroutinesApi |
| 428 | public fun getChildJobCancellationCause(): Throwable |
Roman Elizarov | f7a6334 | 2018-09-25 00:26:25 +0300 | [diff] [blame] | 429 | } |
| 430 | |
| 431 | /** |
Roman Elizarov | 852fae5 | 2018-09-25 20:13:24 +0300 | [diff] [blame] | 432 | * A handle that child keep onto its parent so that it is able to report its cancellation. |
Roman Elizarov | 1e0a2f0 | 2018-09-25 16:10:26 +0300 | [diff] [blame] | 433 | * |
Roman Elizarov | f7a6334 | 2018-09-25 00:26:25 +0300 | [diff] [blame] | 434 | * @suppress **This is unstable API and it is subject to change.** |
| 435 | */ |
Vsevolod Tolstopyatov | b5d10d4 | 2018-09-28 16:28:16 +0300 | [diff] [blame] | 436 | @InternalCoroutinesApi |
Vsevolod Tolstopyatov | 7fb590d | 2018-10-11 18:22:46 +0300 | [diff] [blame] | 437 | @Deprecated(level = DeprecationLevel.ERROR, message = "This is internal API and may be removed in the future releases") |
| 438 | public interface ChildHandle : DisposableHandle { |
Roman Elizarov | 5d18d02 | 2018-09-22 22:13:05 +0300 | [diff] [blame] | 439 | /** |
Roman Elizarov | 852fae5 | 2018-09-25 20:13:24 +0300 | [diff] [blame] | 440 | * Child is cancelling its parent by invoking this method. |
Roman Elizarov | 5d18d02 | 2018-09-22 22:13:05 +0300 | [diff] [blame] | 441 | * This method is invoked by the child twice. The first time child report its root cause as soon as possible, |
Roman Elizarov | 852fae5 | 2018-09-25 20:13:24 +0300 | [diff] [blame] | 442 | * so that all its siblings and the parent can start cancelling their work asap. The second time |
| 443 | * child invokes this method when it had aggregated and determined its final cancellation cause. |
Roman Elizarov | 5d18d02 | 2018-09-22 22:13:05 +0300 | [diff] [blame] | 444 | * |
| 445 | * @suppress **This is unstable API and it is subject to change.** |
| 446 | */ |
Vsevolod Tolstopyatov | b5d10d4 | 2018-09-28 16:28:16 +0300 | [diff] [blame] | 447 | @InternalCoroutinesApi |
Roman Elizarov | 6685fd0 | 2018-09-25 13:23:53 +0300 | [diff] [blame] | 448 | public fun childCancelled(cause: Throwable): Boolean |
Roman Elizarov | 5d18d02 | 2018-09-22 22:13:05 +0300 | [diff] [blame] | 449 | } |
| 450 | |
Roman Elizarov | 3e387b8 | 2017-12-04 13:49:11 +0300 | [diff] [blame] | 451 | // -------------------- Job extensions -------------------- |
| 452 | |
Roman Elizarov | 8b38fa2 | 2017-09-27 17:44:31 +0300 | [diff] [blame] | 453 | /** |
Roman Elizarov | daa1d9d | 2017-03-02 19:00:50 +0300 | [diff] [blame] | 454 | * Disposes a specified [handle] when this job is complete. |
| 455 | * |
| 456 | * This is a shortcut for the following code with slightly more efficient implementation (one fewer object created). |
| 457 | * ``` |
| 458 | * invokeOnCompletion { handle.dispose() } |
| 459 | * ``` |
| 460 | */ |
Vsevolod Tolstopyatov | 1f7b2d8 | 2018-10-09 15:57:51 +0300 | [diff] [blame] | 461 | internal fun Job.disposeOnCompletion(handle: DisposableHandle): DisposableHandle = |
Roman Elizarov | aa461cf | 2018-04-11 13:20:29 +0300 | [diff] [blame] | 462 | invokeOnCompletion(handler = DisposeOnCompletion(this, handle).asHandler) |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 463 | |
Roman Elizarov | f024608 | 2017-02-10 18:02:38 +0300 | [diff] [blame] | 464 | /** |
Inego | 596187e | 2019-04-21 14:21:53 +0700 | [diff] [blame] | 465 | * Cancels the job and suspends the invoking coroutine until the cancelled job is complete. |
Roman Elizarov | 44ba5d0 | 2017-09-06 15:45:24 +0300 | [diff] [blame] | 466 | * |
Inego | 596187e | 2019-04-21 14:21:53 +0700 | [diff] [blame] | 467 | * This suspending function is cancellable and **always** checks for a cancellation of the invoking coroutine's Job. |
Roman Elizarov | 8b38fa2 | 2017-09-27 17:44:31 +0300 | [diff] [blame] | 468 | * If the [Job] of the invoking coroutine is cancelled or completed when this |
| 469 | * suspending function is invoked or while it is suspended, this function |
| 470 | * throws [CancellationException]. |
| 471 | * |
| 472 | * In particular, it means that a parent coroutine invoking `cancelAndJoin` on a child coroutine that was started using |
| 473 | * `launch(coroutineContext) { ... }` builder throws [CancellationException] if the child |
paolop | 81c7521 | 2018-06-02 10:03:33 +0000 | [diff] [blame] | 474 | * had crashed, unless a non-standard [CoroutineExceptionHandler] is installed in the context. |
Roman Elizarov | 44ba5d0 | 2017-09-06 15:45:24 +0300 | [diff] [blame] | 475 | * |
| 476 | * This is a shortcut for the invocation of [cancel][Job.cancel] followed by [join][Job.join]. |
| 477 | */ |
Roman Elizarov | aa461cf | 2018-04-11 13:20:29 +0300 | [diff] [blame] | 478 | public suspend fun Job.cancelAndJoin() { |
Roman Elizarov | 44ba5d0 | 2017-09-06 15:45:24 +0300 | [diff] [blame] | 479 | cancel() |
| 480 | return join() |
| 481 | } |
| 482 | |
| 483 | /** |
Roman Elizarov | 0aad8f1 | 2019-03-01 12:08:43 +0300 | [diff] [blame] | 484 | * Cancels all [children][Job.children] jobs of this coroutine using [Job.cancel] for all of them |
| 485 | * with an optional cancellation [cause]. |
| 486 | * Unlike [Job.cancel] on this job as a whole, the state of this job itself is not affected. |
Roman Elizarov | 3e387b8 | 2017-12-04 13:49:11 +0300 | [diff] [blame] | 487 | */ |
Roman Elizarov | 0aad8f1 | 2019-03-01 12:08:43 +0300 | [diff] [blame] | 488 | public fun Job.cancelChildren(cause: CancellationException? = null) { |
Roman Elizarov | 3e387b8 | 2017-12-04 13:49:11 +0300 | [diff] [blame] | 489 | children.forEach { it.cancel(cause) } |
| 490 | } |
| 491 | |
Vsevolod Tolstopyatov | b058ba1 | 2018-10-17 15:15:17 +0300 | [diff] [blame] | 492 | /** |
Roman Elizarov | 0aad8f1 | 2019-03-01 12:08:43 +0300 | [diff] [blame] | 493 | * @suppress This method implements old version of JVM ABI. Use [cancel]. |
Vsevolod Tolstopyatov | b058ba1 | 2018-10-17 15:15:17 +0300 | [diff] [blame] | 494 | */ |
Roman Elizarov | 73b456b | 2019-03-06 17:18:27 +0300 | [diff] [blame] | 495 | @Deprecated(level = DeprecationLevel.HIDDEN, message = "Since 1.2.0, binary compatibility with versions <= 1.1.x") |
Roman Elizarov | 0aad8f1 | 2019-03-01 12:08:43 +0300 | [diff] [blame] | 496 | public fun Job.cancelChildren() = cancelChildren(null) |
| 497 | |
| 498 | /** |
| 499 | * @suppress This method has bad semantics when cause is not a [CancellationException]. Use [Job.cancelChildren]. |
| 500 | */ |
Roman Elizarov | 73b456b | 2019-03-06 17:18:27 +0300 | [diff] [blame] | 501 | @Deprecated(level = DeprecationLevel.HIDDEN, message = "Since 1.2.0, binary compatibility with versions <= 1.1.x") |
Roman Elizarov | 0aad8f1 | 2019-03-01 12:08:43 +0300 | [diff] [blame] | 502 | public fun Job.cancelChildren(cause: Throwable? = null) { |
| 503 | children.forEach { (it as? JobSupport)?.cancelInternal(cause) } |
Vsevolod Tolstopyatov | b058ba1 | 2018-10-17 15:15:17 +0300 | [diff] [blame] | 504 | } |
| 505 | |
Roman Elizarov | 3e387b8 | 2017-12-04 13:49:11 +0300 | [diff] [blame] | 506 | // -------------------- CoroutineContext extensions -------------------- |
| 507 | |
| 508 | /** |
Roman Elizarov | 9fe5f46 | 2018-02-21 19:05:52 +0300 | [diff] [blame] | 509 | * Returns `true` when the [Job] of the coroutine in this context is still active |
| 510 | * (has not completed and was not cancelled yet). |
| 511 | * |
| 512 | * Check this property in long-running computation loops to support cancellation |
| 513 | * when [CoroutineScope.isActive] is not available: |
| 514 | * |
| 515 | * ``` |
| 516 | * while (coroutineContext.isActive) { |
| 517 | * // do some computation |
| 518 | * } |
| 519 | * ``` |
| 520 | * |
| 521 | * The `coroutineContext.isActive` expression is a shortcut for `coroutineContext[Job]?.isActive == true`. |
| 522 | * See [Job.isActive]. |
| 523 | */ |
Roman Elizarov | aa461cf | 2018-04-11 13:20:29 +0300 | [diff] [blame] | 524 | public val CoroutineContext.isActive: Boolean |
Roman Elizarov | 9fe5f46 | 2018-02-21 19:05:52 +0300 | [diff] [blame] | 525 | get() = this[Job]?.isActive == true |
| 526 | |
Roman Elizarov | 27b8f45 | 2018-09-20 21:23:41 +0300 | [diff] [blame] | 527 | /** |
Roman Elizarov | 0aad8f1 | 2019-03-01 12:08:43 +0300 | [diff] [blame] | 528 | * Cancels [Job] of this context with an optional cancellation cause. |
| 529 | * See [Job.cancel] for details. |
Roman Elizarov | 8b38fa2 | 2017-09-27 17:44:31 +0300 | [diff] [blame] | 530 | */ |
Roman Elizarov | 0aad8f1 | 2019-03-01 12:08:43 +0300 | [diff] [blame] | 531 | public fun CoroutineContext.cancel(cause: CancellationException? = null) { |
| 532 | this[Job]?.cancel(cause) |
Vsevolod Tolstopyatov | b058ba1 | 2018-10-17 15:15:17 +0300 | [diff] [blame] | 533 | } |
| 534 | |
| 535 | /** |
Roman Elizarov | 0aad8f1 | 2019-03-01 12:08:43 +0300 | [diff] [blame] | 536 | * @suppress This method implements old version of JVM ABI. Use [CoroutineContext.cancel]. |
Vsevolod Tolstopyatov | b058ba1 | 2018-10-17 15:15:17 +0300 | [diff] [blame] | 537 | */ |
Roman Elizarov | 73b456b | 2019-03-06 17:18:27 +0300 | [diff] [blame] | 538 | @Deprecated(level = DeprecationLevel.HIDDEN, message = "Since 1.2.0, binary compatibility with versions <= 1.1.x") |
Roman Elizarov | 0aad8f1 | 2019-03-01 12:08:43 +0300 | [diff] [blame] | 539 | public fun CoroutineContext.cancel() = cancel(null) |
Vsevolod Tolstopyatov | b058ba1 | 2018-10-17 15:15:17 +0300 | [diff] [blame] | 540 | |
| 541 | /** |
Vsevolod Tolstopyatov | 5ae3947 | 2019-03-01 15:56:57 +0300 | [diff] [blame] | 542 | * Ensures that current job is [active][Job.isActive]. |
| 543 | * If the job is no longer active, throws [CancellationException]. |
| 544 | * If the job was cancelled, thrown exception contains the original cancellation cause. |
| 545 | * |
| 546 | * This method is a drop-in replacement for the following code, but with more precise exception: |
| 547 | * ``` |
| 548 | * if (!job.isActive) { |
| 549 | * throw CancellationException() |
| 550 | * } |
| 551 | * ``` |
| 552 | */ |
| 553 | public fun Job.ensureActive(): Unit { |
| 554 | if (!isActive) throw getCancellationException() |
| 555 | } |
| 556 | |
| 557 | /** |
| 558 | * Ensures that job in the current context is [active][Job.isActive]. |
| 559 | * Throws [IllegalStateException] if the context does not have a job in it. |
| 560 | * |
| 561 | * If the job is no longer active, throws [CancellationException]. |
| 562 | * If the job was cancelled, thrown exception contains the original cancellation cause. |
| 563 | * |
| 564 | * This method is a drop-in replacement for the following code, but with more precise exception: |
| 565 | * ``` |
| 566 | * if (!isActive) { |
| 567 | * throw CancellationException() |
| 568 | * } |
| 569 | * ``` |
| 570 | */ |
| 571 | public fun CoroutineContext.ensureActive(): Unit { |
| 572 | val job = get(Job) ?: error("Context cannot be checked for liveness because it does not have a job: $this") |
| 573 | job.ensureActive() |
| 574 | } |
| 575 | |
| 576 | /** |
Roman Elizarov | 0aad8f1 | 2019-03-01 12:08:43 +0300 | [diff] [blame] | 577 | * @suppress This method has bad semantics when cause is not a [CancellationException]. Use [CoroutineContext.cancel]. |
Vsevolod Tolstopyatov | b058ba1 | 2018-10-17 15:15:17 +0300 | [diff] [blame] | 578 | */ |
Roman Elizarov | 73b456b | 2019-03-06 17:18:27 +0300 | [diff] [blame] | 579 | @Deprecated(level = DeprecationLevel.HIDDEN, message = "Since 1.2.0, binary compatibility with versions <= 1.1.x") |
Roman Elizarov | aa461cf | 2018-04-11 13:20:29 +0300 | [diff] [blame] | 580 | public fun CoroutineContext.cancel(cause: Throwable? = null): Boolean = |
Vsevolod Tolstopyatov | 6b2e90b | 2018-11-12 15:30:45 +0300 | [diff] [blame] | 581 | @Suppress("DEPRECATION") |
Roman Elizarov | 0aad8f1 | 2019-03-01 12:08:43 +0300 | [diff] [blame] | 582 | (this[Job] as? JobSupport)?.cancelInternal(cause) ?: false |
Roman Elizarov | 8b38fa2 | 2017-09-27 17:44:31 +0300 | [diff] [blame] | 583 | |
| 584 | /** |
Vitus | 3428774 | 2019-05-03 22:23:03 +0200 | [diff] [blame^] | 585 | * Cancels all children of the [Job] in this context, without touching the state of this job itself |
Roman Elizarov | 0aad8f1 | 2019-03-01 12:08:43 +0300 | [diff] [blame] | 586 | * with an optional cancellation cause. See [Job.cancel]. |
Roman Elizarov | 8b38fa2 | 2017-09-27 17:44:31 +0300 | [diff] [blame] | 587 | * It does not do anything if there is no job in the context or it has no children. |
Roman Elizarov | 8b38fa2 | 2017-09-27 17:44:31 +0300 | [diff] [blame] | 588 | */ |
Roman Elizarov | 0aad8f1 | 2019-03-01 12:08:43 +0300 | [diff] [blame] | 589 | public fun CoroutineContext.cancelChildren(cause: CancellationException? = null) { |
| 590 | this[Job]?.children?.forEach { it.cancel(cause) } |
Roman Elizarov | 27b8f45 | 2018-09-20 21:23:41 +0300 | [diff] [blame] | 591 | } |
| 592 | |
Roman Elizarov | 0aad8f1 | 2019-03-01 12:08:43 +0300 | [diff] [blame] | 593 | /** |
| 594 | * @suppress This method implements old version of JVM ABI. Use [CoroutineContext.cancelChildren]. |
| 595 | */ |
Roman Elizarov | 73b456b | 2019-03-06 17:18:27 +0300 | [diff] [blame] | 596 | @Deprecated(level = DeprecationLevel.HIDDEN, message = "Since 1.2.0, binary compatibility with versions <= 1.1.x") |
Roman Elizarov | 0aad8f1 | 2019-03-01 12:08:43 +0300 | [diff] [blame] | 597 | public fun CoroutineContext.cancelChildren() = cancelChildren(null) |
| 598 | |
| 599 | /** |
| 600 | * @suppress This method has bad semantics when cause is not a [CancellationException]. Use [CoroutineContext.cancelChildren]. |
| 601 | */ |
Roman Elizarov | 73b456b | 2019-03-06 17:18:27 +0300 | [diff] [blame] | 602 | @Deprecated(level = DeprecationLevel.HIDDEN, message = "Since 1.2.0, binary compatibility with versions <= 1.1.x") |
Roman Elizarov | aa461cf | 2018-04-11 13:20:29 +0300 | [diff] [blame] | 603 | public fun CoroutineContext.cancelChildren(cause: Throwable? = null) { |
Roman Elizarov | 0aad8f1 | 2019-03-01 12:08:43 +0300 | [diff] [blame] | 604 | this[Job]?.children?.forEach { (it as? JobSupport)?.cancelInternal(cause) } |
Roman Elizarov | 8b38fa2 | 2017-09-27 17:44:31 +0300 | [diff] [blame] | 605 | } |
| 606 | |
| 607 | /** |
Roman Elizarov | daa1d9d | 2017-03-02 19:00:50 +0300 | [diff] [blame] | 608 | * No-op implementation of [DisposableHandle]. |
Roman Elizarov | 27b8f45 | 2018-09-20 21:23:41 +0300 | [diff] [blame] | 609 | * @suppress **This an internal API and should not be used from general code.** |
Roman Elizarov | daa1d9d | 2017-03-02 19:00:50 +0300 | [diff] [blame] | 610 | */ |
Vsevolod Tolstopyatov | 149ba48 | 2018-09-24 20:28:02 +0300 | [diff] [blame] | 611 | @InternalCoroutinesApi |
Roman Elizarov | 5d18d02 | 2018-09-22 22:13:05 +0300 | [diff] [blame] | 612 | public object NonDisposableHandle : DisposableHandle, ChildHandle { |
| 613 | /** |
| 614 | * Does not do anything. |
| 615 | * @suppress |
| 616 | */ |
Roman Elizarov | aa461cf | 2018-04-11 13:20:29 +0300 | [diff] [blame] | 617 | override fun dispose() {} |
Roman Elizarov | daa1d9d | 2017-03-02 19:00:50 +0300 | [diff] [blame] | 618 | |
Roman Elizarov | 5d18d02 | 2018-09-22 22:13:05 +0300 | [diff] [blame] | 619 | /** |
| 620 | * Returns `false`. |
| 621 | * @suppress |
| 622 | */ |
Roman Elizarov | 6685fd0 | 2018-09-25 13:23:53 +0300 | [diff] [blame] | 623 | override fun childCancelled(cause: Throwable): Boolean = false |
Roman Elizarov | 5d18d02 | 2018-09-22 22:13:05 +0300 | [diff] [blame] | 624 | |
| 625 | /** |
| 626 | * Returns "NonDisposableHandle" string. |
| 627 | * @suppress |
| 628 | */ |
Roman Elizarov | daa1d9d | 2017-03-02 19:00:50 +0300 | [diff] [blame] | 629 | override fun toString(): String = "NonDisposableHandle" |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 630 | } |