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 | aa461cf | 2018-04-11 13:20:29 +0300 | [diff] [blame] | 7 | import kotlinx.coroutines.experimental.internal.* |
| 8 | import kotlinx.coroutines.experimental.internalAnnotations.* |
| 9 | import kotlin.coroutines.experimental.* |
| 10 | import kotlin.coroutines.experimental.intrinsics.* |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 11 | |
| 12 | // --------------- cancellable continuations --------------- |
| 13 | |
| 14 | /** |
Vsevolod Tolstopyatov | f6430f4 | 2018-04-17 17:56:32 +0300 | [diff] [blame] | 15 | * Cancellable continuation. It is _completed_ when it is resumed or cancelled. |
Roman Elizarov | d82b3a9 | 2017-06-23 21:52:08 +0300 | [diff] [blame] | 16 | * When [cancel] function is explicitly invoked, this continuation immediately resumes with [CancellationException] or |
Roman Elizarov | b7c46de | 2017-02-08 12:35:24 +0300 | [diff] [blame] | 17 | * with the specified cancel cause. |
| 18 | * |
Roman Elizarov | d82b3a9 | 2017-06-23 21:52:08 +0300 | [diff] [blame] | 19 | * Cancellable continuation has three states (as subset of [Job] states): |
Roman Elizarov | 32d9532 | 2017-02-09 15:57:31 +0300 | [diff] [blame] | 20 | * |
| 21 | * | **State** | [isActive] | [isCompleted] | [isCancelled] | |
Roman Elizarov | 7886ef6 | 2017-02-13 14:00:18 +0300 | [diff] [blame] | 22 | * | ----------------------------------- | ---------- | ------------- | ------------- | |
Roman Elizarov | 32d9532 | 2017-02-09 15:57:31 +0300 | [diff] [blame] | 23 | * | _Active_ (initial state) | `true` | `false` | `false` | |
| 24 | * | _Resumed_ (final _completed_ state) | `false` | `true` | `false` | |
| 25 | * | _Canceled_ (final _completed_ state)| `false` | `true` | `true` | |
Roman Elizarov | b7c46de | 2017-02-08 12:35:24 +0300 | [diff] [blame] | 26 | * |
| 27 | * Invocation of [cancel] transitions this continuation from _active_ to _cancelled_ state, while |
| 28 | * invocation of [resume] or [resumeWithException] transitions it from _active_ to _resumed_ state. |
| 29 | * |
Roman Elizarov | 29affbb | 2017-07-21 13:47:41 +0300 | [diff] [blame] | 30 | * A [cancelled][isCancelled] continuation implies that it is [completed][isCompleted]. |
Roman Elizarov | d82b3a9 | 2017-06-23 21:52:08 +0300 | [diff] [blame] | 31 | * |
Roman Elizarov | b7c46de | 2017-02-08 12:35:24 +0300 | [diff] [blame] | 32 | * Invocation of [resume] or [resumeWithException] in _resumed_ state produces [IllegalStateException] |
| 33 | * but is ignored in _cancelled_ state. |
Roman Elizarov | 29affbb | 2017-07-21 13:47:41 +0300 | [diff] [blame] | 34 | * |
| 35 | * ``` |
| 36 | * +-----------+ resume +---------+ |
| 37 | * | Active | ----------> | Resumed | |
| 38 | * +-----------+ +---------+ |
| 39 | * | |
| 40 | * | cancel |
| 41 | * V |
| 42 | * +-----------+ |
| 43 | * | Cancelled | |
| 44 | * +-----------+ |
| 45 | * |
| 46 | * ``` |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 47 | */ |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 48 | public interface CancellableContinuation<in T> : Continuation<T> { |
Roman Elizarov | e1c0b65 | 2017-12-01 14:02:57 +0300 | [diff] [blame] | 49 | /** |
| 50 | * Returns `true` when this continuation is active -- it has not completed or cancelled yet. |
| 51 | */ |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 52 | public val isActive: Boolean |
Roman Elizarov | e1c0b65 | 2017-12-01 14:02:57 +0300 | [diff] [blame] | 53 | |
| 54 | /** |
| 55 | * Returns `true` when this continuation has completed for any reason. A continuation |
| 56 | * that was cancelled is also considered complete. |
| 57 | */ |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 58 | public val isCompleted: Boolean |
Roman Elizarov | e1c0b65 | 2017-12-01 14:02:57 +0300 | [diff] [blame] | 59 | |
Roman Elizarov | 834af46 | 2017-01-23 20:50:29 +0300 | [diff] [blame] | 60 | /** |
Roman Elizarov | 32d9532 | 2017-02-09 15:57:31 +0300 | [diff] [blame] | 61 | * Returns `true` if this continuation was [cancelled][cancel]. |
| 62 | * |
| 63 | * It implies that [isActive] is `false` and [isCompleted] is `true`. |
Roman Elizarov | 834af46 | 2017-01-23 20:50:29 +0300 | [diff] [blame] | 64 | */ |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 65 | public val isCancelled: Boolean |
Roman Elizarov | 187eace | 2017-01-31 09:39:58 +0300 | [diff] [blame] | 66 | |
| 67 | /** |
Roman Elizarov | 7b2d8b0 | 2017-02-02 20:09:14 +0300 | [diff] [blame] | 68 | * Tries to resume this continuation with a given value and returns non-null object token if it was successful, |
| 69 | * or `null` otherwise (it was already resumed or cancelled). When non-null object was returned, |
| 70 | * [completeResume] must be invoked with it. |
Roman Elizarov | 1216e91 | 2017-02-22 09:57:06 +0300 | [diff] [blame] | 71 | * |
| 72 | * When [idempotent] is not `null`, this function performs _idempotent_ operation, so that |
| 73 | * further invocations with the same non-null reference produce the same result. |
| 74 | * |
| 75 | * @suppress **This is unstable API and it is subject to change.** |
Roman Elizarov | 187eace | 2017-01-31 09:39:58 +0300 | [diff] [blame] | 76 | */ |
Roman Elizarov | aa461cf | 2018-04-11 13:20:29 +0300 | [diff] [blame] | 77 | public fun tryResume(value: T, idempotent: Any? = null): Any? |
Roman Elizarov | 7b2d8b0 | 2017-02-02 20:09:14 +0300 | [diff] [blame] | 78 | |
| 79 | /** |
Roman Elizarov | f6fed2a | 2017-02-03 19:12:09 +0300 | [diff] [blame] | 80 | * Tries to resume this continuation with a given exception and returns non-null object token if it was successful, |
| 81 | * or `null` otherwise (it was already resumed or cancelled). When non-null object was returned, |
| 82 | * [completeResume] must be invoked with it. |
Roman Elizarov | 1216e91 | 2017-02-22 09:57:06 +0300 | [diff] [blame] | 83 | * |
| 84 | * @suppress **This is unstable API and it is subject to change.** |
Roman Elizarov | f6fed2a | 2017-02-03 19:12:09 +0300 | [diff] [blame] | 85 | */ |
Roman Elizarov | a198bad | 2017-02-10 13:30:38 +0300 | [diff] [blame] | 86 | public fun tryResumeWithException(exception: Throwable): Any? |
Roman Elizarov | f6fed2a | 2017-02-03 19:12:09 +0300 | [diff] [blame] | 87 | |
| 88 | /** |
| 89 | * Completes the execution of [tryResume] or [tryResumeWithException] on its non-null result. |
Roman Elizarov | 1216e91 | 2017-02-22 09:57:06 +0300 | [diff] [blame] | 90 | * |
| 91 | * @suppress **This is unstable API and it is subject to change.** |
Roman Elizarov | 7b2d8b0 | 2017-02-02 20:09:14 +0300 | [diff] [blame] | 92 | */ |
Roman Elizarov | aa461cf | 2018-04-11 13:20:29 +0300 | [diff] [blame] | 93 | public fun completeResume(token: Any) |
Roman Elizarov | 187eace | 2017-01-31 09:39:58 +0300 | [diff] [blame] | 94 | |
| 95 | /** |
| 96 | * Makes this continuation cancellable. Use it with `holdCancellability` optional parameter to |
| 97 | * [suspendCancellableCoroutine] function. It throws [IllegalStateException] if invoked more than once. |
| 98 | */ |
Roman Elizarov | aa461cf | 2018-04-11 13:20:29 +0300 | [diff] [blame] | 99 | public fun initCancellability() |
Roman Elizarov | e1c0b65 | 2017-12-01 14:02:57 +0300 | [diff] [blame] | 100 | |
| 101 | /** |
| 102 | * Cancels this continuation with an optional cancellation [cause]. The result is `true` if this continuation was |
| 103 | * cancelled as a result of this invocation and `false` otherwise. |
| 104 | */ |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 105 | public fun cancel(cause: Throwable? = null): Boolean |
Roman Elizarov | e1c0b65 | 2017-12-01 14:02:57 +0300 | [diff] [blame] | 106 | |
| 107 | /** |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 108 | * Registers handler that is **synchronously** invoked once on cancellation (both regular and exceptional) of this continuation. |
| 109 | * When the continuation is already cancelled, then the handler is immediately invoked |
| 110 | * with cancellation exception. Otherwise, the handler will be invoked once on cancellation if this |
| 111 | * continuation is cancelled. |
Roman Elizarov | e1c0b65 | 2017-12-01 14:02:57 +0300 | [diff] [blame] | 112 | * |
| 113 | * Installed [handler] should not throw any exceptions. If it does, they will get caught, |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 114 | * wrapped into [CompletionHandlerException], and rethrown, potentially causing the crash of unrelated code. |
| 115 | * |
| 116 | * At most one [handler] can be installed on one continuation |
Vsevolod Tolstopyatov | bb19a26 | 2018-05-25 19:14:59 +0300 | [diff] [blame] | 117 | * |
| 118 | * @param invokeImmediately not used |
| 119 | * @param onCancelling not used |
| 120 | * @return not used |
Roman Elizarov | e1c0b65 | 2017-12-01 14:02:57 +0300 | [diff] [blame] | 121 | */ |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 122 | @Deprecated( |
| 123 | message = "Disposable handlers on regular completion are no longer supported", |
| 124 | replaceWith = ReplaceWith("invokeOnCancellation(handler)"), |
Vsevolod Tolstopyatov | 2cd27fb | 2018-06-07 13:22:17 +0300 | [diff] [blame] | 125 | level = DeprecationLevel.WARNING |
Vsevolod Tolstopyatov | bb19a26 | 2018-05-25 19:14:59 +0300 | [diff] [blame] | 126 | ) |
| 127 | public fun invokeOnCompletion( |
| 128 | onCancelling: Boolean = false, |
| 129 | invokeImmediately: Boolean = true, |
| 130 | handler: CompletionHandler): DisposableHandle |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 131 | |
| 132 | /** |
| 133 | * Registers handler that is **synchronously** invoked once on cancellation (both regular and exceptional) of this continuation. |
| 134 | * When the continuation is already cancelled, then the handler is immediately invoked |
| 135 | * with cancellation exception. Otherwise, the handler will be invoked once on cancellation if this |
| 136 | * continuation is cancelled. |
| 137 | * |
| 138 | * Installed [handler] should not throw any exceptions. If it does, they will get caught, |
| 139 | * wrapped into [CompletionHandlerException], and rethrown, potentially causing the crash of unrelated code. |
| 140 | * |
Roman Elizarov | 58c5fdc | 2018-06-08 14:01:12 +0300 | [diff] [blame] | 141 | * At most one [handler] can be installed on one continuation. |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 142 | */ |
| 143 | public fun invokeOnCancellation(handler: CompletionHandler) |
Roman Elizarov | a198bad | 2017-02-10 13:30:38 +0300 | [diff] [blame] | 144 | |
| 145 | /** |
| 146 | * Resumes this continuation with a given [value] in the invoker thread without going though |
| 147 | * [dispatch][CoroutineDispatcher.dispatch] function of the [CoroutineDispatcher] in the [context]. |
| 148 | * This function is designed to be used only by the [CoroutineDispatcher] implementations themselves. |
| 149 | * **It should not be used in general code**. |
Roman Elizarov | a198bad | 2017-02-10 13:30:38 +0300 | [diff] [blame] | 150 | */ |
Roman Elizarov | aa461cf | 2018-04-11 13:20:29 +0300 | [diff] [blame] | 151 | public fun CoroutineDispatcher.resumeUndispatched(value: T) |
Roman Elizarov | a198bad | 2017-02-10 13:30:38 +0300 | [diff] [blame] | 152 | |
| 153 | /** |
| 154 | * Resumes this continuation with a given [exception] in the invoker thread without going though |
| 155 | * [dispatch][CoroutineDispatcher.dispatch] function of the [CoroutineDispatcher] in the [context]. |
| 156 | * This function is designed to be used only by the [CoroutineDispatcher] implementations themselves. |
| 157 | * **It should not be used in general code**. |
Roman Elizarov | a198bad | 2017-02-10 13:30:38 +0300 | [diff] [blame] | 158 | */ |
Roman Elizarov | aa461cf | 2018-04-11 13:20:29 +0300 | [diff] [blame] | 159 | public fun CoroutineDispatcher.resumeUndispatchedWithException(exception: Throwable) |
Roman Elizarov | 834af46 | 2017-01-23 20:50:29 +0300 | [diff] [blame] | 160 | } |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 161 | |
| 162 | /** |
Roman Elizarov | a74eb5f | 2017-05-11 20:15:18 +0300 | [diff] [blame] | 163 | * Suspends coroutine similar to [suspendCoroutine], but provide an implementation of [CancellableContinuation] to |
Roman Elizarov | d82b3a9 | 2017-06-23 21:52:08 +0300 | [diff] [blame] | 164 | * the [block]. This function throws [CancellationException] if the coroutine is cancelled or completed while suspended. |
Roman Elizarov | 187eace | 2017-01-31 09:39:58 +0300 | [diff] [blame] | 165 | * |
| 166 | * If [holdCancellability] optional parameter is `true`, then the coroutine is suspended, but it is not |
| 167 | * cancellable until [CancellableContinuation.initCancellability] is invoked. |
Roman Elizarov | a74eb5f | 2017-05-11 20:15:18 +0300 | [diff] [blame] | 168 | * |
| 169 | * See [suspendAtomicCancellableCoroutine] for suspending functions that need *atomic cancellation*. |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 170 | */ |
Roman Elizarov | aa461cf | 2018-04-11 13:20:29 +0300 | [diff] [blame] | 171 | public suspend inline fun <T> suspendCancellableCoroutine( |
Roman Elizarov | 187eace | 2017-01-31 09:39:58 +0300 | [diff] [blame] | 172 | holdCancellability: Boolean = false, |
| 173 | crossinline block: (CancellableContinuation<T>) -> Unit |
| 174 | ): T = |
Roman Elizarov | 58a7add | 2017-01-20 12:19:52 +0300 | [diff] [blame] | 175 | suspendCoroutineOrReturn { cont -> |
Roman Elizarov | 2b12d58 | 2017-06-22 20:12:19 +0300 | [diff] [blame] | 176 | val cancellable = CancellableContinuationImpl(cont, resumeMode = MODE_CANCELLABLE) |
Roman Elizarov | ee7c0eb | 2017-02-16 15:29:28 +0300 | [diff] [blame] | 177 | if (!holdCancellability) cancellable.initCancellability() |
| 178 | block(cancellable) |
| 179 | cancellable.getResult() |
Roman Elizarov | aa461cf | 2018-04-11 13:20:29 +0300 | [diff] [blame] | 180 | } |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 181 | |
Roman Elizarov | a74eb5f | 2017-05-11 20:15:18 +0300 | [diff] [blame] | 182 | /** |
| 183 | * Suspends coroutine similar to [suspendCancellableCoroutine], but with *atomic cancellation*. |
| 184 | * |
| 185 | * When suspended function throws [CancellationException] it means that the continuation was not resumed. |
| 186 | * As a side-effect of atomic cancellation, a thread-bound coroutine (to some UI thread, for example) may |
| 187 | * continue to execute even after it was cancelled from the same thread in the case when the continuation |
| 188 | * was already resumed and was posted for execution to the thread's queue. |
| 189 | */ |
Roman Elizarov | aa461cf | 2018-04-11 13:20:29 +0300 | [diff] [blame] | 190 | public suspend inline fun <T> suspendAtomicCancellableCoroutine( |
Roman Elizarov | a74eb5f | 2017-05-11 20:15:18 +0300 | [diff] [blame] | 191 | holdCancellability: Boolean = false, |
| 192 | crossinline block: (CancellableContinuation<T>) -> Unit |
| 193 | ): T = |
| 194 | suspendCoroutineOrReturn { cont -> |
Roman Elizarov | 2b12d58 | 2017-06-22 20:12:19 +0300 | [diff] [blame] | 195 | val cancellable = CancellableContinuationImpl(cont, resumeMode = MODE_ATOMIC_DEFAULT) |
Roman Elizarov | a74eb5f | 2017-05-11 20:15:18 +0300 | [diff] [blame] | 196 | if (!holdCancellability) cancellable.initCancellability() |
| 197 | block(cancellable) |
| 198 | cancellable.getResult() |
| 199 | } |
Roman Elizarov | 0a78839 | 2017-02-15 17:52:12 +0300 | [diff] [blame] | 200 | |
| 201 | /** |
| 202 | * Removes a given node on cancellation. |
| 203 | * @suppress **This is unstable API and it is subject to change.** |
| 204 | */ |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 205 | @Deprecated( |
| 206 | message = "Disposable handlers on cancellation are no longer supported", |
| 207 | replaceWith = ReplaceWith("removeOnCancellation(handler)"), |
Vsevolod Tolstopyatov | 2cd27fb | 2018-06-07 13:22:17 +0300 | [diff] [blame] | 208 | level = DeprecationLevel.WARNING) |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 209 | public fun CancellableContinuation<*>.removeOnCancel(node: LockFreeLinkedListNode): DisposableHandle { |
Vsevolod Tolstopyatov | f6430f4 | 2018-04-17 17:56:32 +0300 | [diff] [blame] | 210 | removeOnCancellation(node) |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 211 | return NonDisposableHandle |
| 212 | } |
| 213 | |
| 214 | /** |
| 215 | * Removes a given node on cancellation. |
| 216 | * @suppress **This is unstable API and it is subject to change.** |
| 217 | */ |
Roman Elizarov | dbd9e1c | 2018-04-28 15:14:18 +0300 | [diff] [blame] | 218 | public fun CancellableContinuation<*>.removeOnCancellation(node: LockFreeLinkedListNode) = |
| 219 | invokeOnCancellation(handler = RemoveOnCancel(node).asHandler) |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 220 | |
Vsevolod Tolstopyatov | 80a2947 | 2018-04-17 16:02:02 +0300 | [diff] [blame] | 221 | /** |
| 222 | * Disposes a specified [handle] when this continuation is cancelled. |
| 223 | * |
| 224 | * This is a shortcut for the following code with slightly more efficient implementation (one fewer object created). |
| 225 | * ``` |
| 226 | * invokeOnCancellation { handle.dispose() } |
| 227 | * ``` |
| 228 | */ |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 229 | @Deprecated( |
| 230 | message = "Disposable handlers on regular completion are no longer supported", |
| 231 | replaceWith = ReplaceWith("disposeOnCancellation(handler)"), |
Vsevolod Tolstopyatov | 2cd27fb | 2018-06-07 13:22:17 +0300 | [diff] [blame] | 232 | level = DeprecationLevel.WARNING) |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 233 | public fun CancellableContinuation<*>.disposeOnCompletion(handle: DisposableHandle): DisposableHandle { |
Vsevolod Tolstopyatov | f6430f4 | 2018-04-17 17:56:32 +0300 | [diff] [blame] | 234 | disposeOnCancellation(handle) |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 235 | return NonDisposableHandle |
| 236 | } |
| 237 | |
Vsevolod Tolstopyatov | 80a2947 | 2018-04-17 16:02:02 +0300 | [diff] [blame] | 238 | /** |
| 239 | * Disposes a specified [handle] when this continuation is cancelled. |
| 240 | * |
| 241 | * This is a shortcut for the following code with slightly more efficient implementation (one fewer object created). |
| 242 | * ``` |
| 243 | * invokeOnCancellation { handle.dispose() } |
| 244 | * ``` |
| 245 | */ |
Roman Elizarov | dbd9e1c | 2018-04-28 15:14:18 +0300 | [diff] [blame] | 246 | public fun CancellableContinuation<*>.disposeOnCancellation(handle: DisposableHandle) = |
| 247 | invokeOnCancellation(handler = DisposeOnCancel(handle).asHandler) |
Roman Elizarov | 0a78839 | 2017-02-15 17:52:12 +0300 | [diff] [blame] | 248 | |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 249 | // --------------- implementation details --------------- |
| 250 | |
Roman Elizarov | dbd9e1c | 2018-04-28 15:14:18 +0300 | [diff] [blame] | 251 | private class RemoveOnCancel(private val node: LockFreeLinkedListNode) : CancelHandler() { |
| 252 | override fun invoke(cause: Throwable?) { node.remove() } |
Roman Elizarov | 0a78839 | 2017-02-15 17:52:12 +0300 | [diff] [blame] | 253 | override fun toString() = "RemoveOnCancel[$node]" |
| 254 | } |
| 255 | |
Roman Elizarov | dbd9e1c | 2018-04-28 15:14:18 +0300 | [diff] [blame] | 256 | private class DisposeOnCancel(private val handle: DisposableHandle) : CancelHandler() { |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 257 | override fun invoke(cause: Throwable?) = handle.dispose() |
Roman Elizarov | dbd9e1c | 2018-04-28 15:14:18 +0300 | [diff] [blame] | 258 | override fun toString(): String = "DisposeOnCancel[$handle]" |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 259 | } |
| 260 | |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 261 | @PublishedApi |
Roman Elizarov | e22f14a | 2017-06-22 19:04:52 +0300 | [diff] [blame] | 262 | internal class CancellableContinuationImpl<in T>( |
Roman Elizarov | bcdd8e1 | 2017-10-20 16:42:06 +0800 | [diff] [blame] | 263 | delegate: Continuation<T>, |
Roman Elizarov | 2b12d58 | 2017-06-22 20:12:19 +0300 | [diff] [blame] | 264 | resumeMode: Int |
Roman Elizarov | ac48070 | 2017-12-28 01:09:43 +0300 | [diff] [blame] | 265 | ) : AbstractContinuation<T>(delegate, resumeMode), CancellableContinuation<T>, Runnable { |
Roman Elizarov | 2b12d58 | 2017-06-22 20:12:19 +0300 | [diff] [blame] | 266 | |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 267 | public override val context: CoroutineContext = delegate.context |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 268 | |
Roman Elizarov | 187eace | 2017-01-31 09:39:58 +0300 | [diff] [blame] | 269 | override fun initCancellability() { |
Roman Elizarov | 6640b2b | 2018-01-17 19:08:55 +0300 | [diff] [blame] | 270 | initParentJobInternal(delegate.context[Job]) |
Roman Elizarov | 187eace | 2017-01-31 09:39:58 +0300 | [diff] [blame] | 271 | } |
Roman Elizarov | 58a7add | 2017-01-20 12:19:52 +0300 | [diff] [blame] | 272 | |
Vsevolod Tolstopyatov | bb19a26 | 2018-05-25 19:14:59 +0300 | [diff] [blame] | 273 | override fun invokeOnCompletion(onCancelling: Boolean, |
| 274 | invokeImmediately: Boolean, handler: CompletionHandler): DisposableHandle { |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 275 | invokeOnCancellation(handler) |
| 276 | return NonDisposableHandle |
| 277 | } |
Roman Elizarov | 4d626de | 2018-01-11 22:57:28 +0300 | [diff] [blame] | 278 | |
Roman Elizarov | 1216e91 | 2017-02-22 09:57:06 +0300 | [diff] [blame] | 279 | override fun tryResume(value: T, idempotent: Any?): Any? { |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 280 | loopOnState { state -> |
Roman Elizarov | 187eace | 2017-01-31 09:39:58 +0300 | [diff] [blame] | 281 | when (state) { |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 282 | is NotCompleted -> { |
Roman Elizarov | 932e860 | 2017-06-21 17:21:37 +0300 | [diff] [blame] | 283 | val update: Any? = if (idempotent == null) value else |
| 284 | CompletedIdempotentResult(idempotent, value, state) |
Vsevolod Tolstopyatov | f6430f4 | 2018-04-17 17:56:32 +0300 | [diff] [blame] | 285 | if (tryUpdateStateToFinal(state, update)) return state |
Roman Elizarov | 1216e91 | 2017-02-22 09:57:06 +0300 | [diff] [blame] | 286 | } |
| 287 | is CompletedIdempotentResult -> { |
| 288 | if (state.idempotentResume === idempotent) { |
| 289 | check(state.result === value) { "Non-idempotent resume" } |
| 290 | return state.token |
| 291 | } else |
| 292 | return null |
| 293 | } |
Roman Elizarov | 7b2d8b0 | 2017-02-02 20:09:14 +0300 | [diff] [blame] | 294 | else -> return null // cannot resume -- not active anymore |
Roman Elizarov | 187eace | 2017-01-31 09:39:58 +0300 | [diff] [blame] | 295 | } |
| 296 | } |
| 297 | } |
| 298 | |
Roman Elizarov | f6fed2a | 2017-02-03 19:12:09 +0300 | [diff] [blame] | 299 | override fun tryResumeWithException(exception: Throwable): Any? { |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 300 | loopOnState { state -> |
| 301 | when (state) { |
| 302 | is NotCompleted -> { |
Vsevolod Tolstopyatov | f6430f4 | 2018-04-17 17:56:32 +0300 | [diff] [blame] | 303 | if (tryUpdateStateToFinal(state, CompletedExceptionally(exception))) return state |
Roman Elizarov | 1216e91 | 2017-02-22 09:57:06 +0300 | [diff] [blame] | 304 | } |
Roman Elizarov | f6fed2a | 2017-02-03 19:12:09 +0300 | [diff] [blame] | 305 | else -> return null // cannot resume -- not active anymore |
| 306 | } |
| 307 | } |
| 308 | } |
| 309 | |
Vsevolod Tolstopyatov | f6430f4 | 2018-04-17 17:56:32 +0300 | [diff] [blame] | 310 | override fun completeResume(token: Any) = completeStateUpdate(token as NotCompleted, state, resumeMode) |
Roman Elizarov | 7b2d8b0 | 2017-02-02 20:09:14 +0300 | [diff] [blame] | 311 | |
Roman Elizarov | a198bad | 2017-02-10 13:30:38 +0300 | [diff] [blame] | 312 | override fun CoroutineDispatcher.resumeUndispatched(value: T) { |
Roman Elizarov | 8552f0e | 2017-09-28 13:03:44 +0300 | [diff] [blame] | 313 | val dc = delegate as? DispatchedContinuation |
| 314 | resumeImpl(value, if (dc?.dispatcher === this) MODE_UNDISPATCHED else resumeMode) |
Roman Elizarov | a198bad | 2017-02-10 13:30:38 +0300 | [diff] [blame] | 315 | } |
| 316 | |
| 317 | override fun CoroutineDispatcher.resumeUndispatchedWithException(exception: Throwable) { |
Roman Elizarov | 8552f0e | 2017-09-28 13:03:44 +0300 | [diff] [blame] | 318 | val dc = delegate as? DispatchedContinuation |
Roman Elizarov | bcdd8e1 | 2017-10-20 16:42:06 +0800 | [diff] [blame] | 319 | resumeImpl(CompletedExceptionally(exception), if (dc?.dispatcher === this) MODE_UNDISPATCHED else resumeMode) |
Roman Elizarov | 1216e91 | 2017-02-22 09:57:06 +0300 | [diff] [blame] | 320 | } |
Roman Elizarov | 69d9c85 | 2017-07-12 15:22:21 +0300 | [diff] [blame] | 321 | |
Roman Elizarov | bcdd8e1 | 2017-10-20 16:42:06 +0800 | [diff] [blame] | 322 | @Suppress("UNCHECKED_CAST") |
| 323 | override fun <T> getSuccessfulResult(state: Any?): T = |
| 324 | if (state is CompletedIdempotentResult) state.result as T else state as T |
| 325 | |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 326 | protected override fun nameString(): String = |
Roman Elizarov | 8b38fa2 | 2017-09-27 17:44:31 +0300 | [diff] [blame] | 327 | "CancellableContinuation(${delegate.toDebugString()})" |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 328 | } |
Roman Elizarov | e22f14a | 2017-06-22 19:04:52 +0300 | [diff] [blame] | 329 | |
| 330 | private class CompletedIdempotentResult( |
| 331 | @JvmField val idempotentResume: Any?, |
| 332 | @JvmField val result: Any?, |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 333 | @JvmField val token: NotCompleted |
Roman Elizarov | e22f14a | 2017-06-22 19:04:52 +0300 | [diff] [blame] | 334 | ) { |
| 335 | override fun toString(): String = "CompletedIdempotentResult[$result]" |
| 336 | } |