blob: 5ca28ddcc5f6df50bbc4b60e1111cb58553828b4 [file] [log] [blame]
Roman Elizarovf16fd272017-02-07 11:26:00 +03001/*
2 * Copyright 2016-2017 JetBrains s.r.o.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Roman Elizarov3754f952017-01-18 20:47:54 +030017package kotlinx.coroutines.experimental
18
Roman Elizarovaa461cf2018-04-11 13:20:29 +030019import kotlinx.coroutines.experimental.internal.*
20import kotlinx.coroutines.experimental.internalAnnotations.*
21import kotlin.coroutines.experimental.*
22import kotlin.coroutines.experimental.intrinsics.*
Roman Elizarov3754f952017-01-18 20:47:54 +030023
24// --------------- cancellable continuations ---------------
25
26/**
Vsevolod Tolstopyatovf6430f42018-04-17 17:56:32 +030027 * Cancellable continuation. It is _completed_ when it is resumed or cancelled.
Roman Elizarovd82b3a92017-06-23 21:52:08 +030028 * When [cancel] function is explicitly invoked, this continuation immediately resumes with [CancellationException] or
Roman Elizarovb7c46de2017-02-08 12:35:24 +030029 * with the specified cancel cause.
30 *
Roman Elizarovd82b3a92017-06-23 21:52:08 +030031 * Cancellable continuation has three states (as subset of [Job] states):
Roman Elizarov32d95322017-02-09 15:57:31 +030032 *
33 * | **State** | [isActive] | [isCompleted] | [isCancelled] |
Roman Elizarov7886ef62017-02-13 14:00:18 +030034 * | ----------------------------------- | ---------- | ------------- | ------------- |
Roman Elizarov32d95322017-02-09 15:57:31 +030035 * | _Active_ (initial state) | `true` | `false` | `false` |
36 * | _Resumed_ (final _completed_ state) | `false` | `true` | `false` |
37 * | _Canceled_ (final _completed_ state)| `false` | `true` | `true` |
Roman Elizarovb7c46de2017-02-08 12:35:24 +030038 *
39 * Invocation of [cancel] transitions this continuation from _active_ to _cancelled_ state, while
40 * invocation of [resume] or [resumeWithException] transitions it from _active_ to _resumed_ state.
41 *
Roman Elizarov29affbb2017-07-21 13:47:41 +030042 * A [cancelled][isCancelled] continuation implies that it is [completed][isCompleted].
Roman Elizarovd82b3a92017-06-23 21:52:08 +030043 *
Roman Elizarovb7c46de2017-02-08 12:35:24 +030044 * Invocation of [resume] or [resumeWithException] in _resumed_ state produces [IllegalStateException]
45 * but is ignored in _cancelled_ state.
Roman Elizarov29affbb2017-07-21 13:47:41 +030046 *
47 * ```
48 * +-----------+ resume +---------+
49 * | Active | ----------> | Resumed |
50 * +-----------+ +---------+
51 * |
52 * | cancel
53 * V
54 * +-----------+
55 * | Cancelled |
56 * +-----------+
57 *
58 * ```
Roman Elizarov3754f952017-01-18 20:47:54 +030059 */
Vsevolod Tolstopyatovf3a50132018-04-16 19:41:20 +030060public interface CancellableContinuation<in T> : Continuation<T> {
Roman Elizarove1c0b652017-12-01 14:02:57 +030061 /**
62 * Returns `true` when this continuation is active -- it has not completed or cancelled yet.
63 */
Vsevolod Tolstopyatovf3a50132018-04-16 19:41:20 +030064 public val isActive: Boolean
Roman Elizarove1c0b652017-12-01 14:02:57 +030065
66 /**
67 * Returns `true` when this continuation has completed for any reason. A continuation
68 * that was cancelled is also considered complete.
69 */
Vsevolod Tolstopyatovf3a50132018-04-16 19:41:20 +030070 public val isCompleted: Boolean
Roman Elizarove1c0b652017-12-01 14:02:57 +030071
Roman Elizarov834af462017-01-23 20:50:29 +030072 /**
Roman Elizarov32d95322017-02-09 15:57:31 +030073 * Returns `true` if this continuation was [cancelled][cancel].
74 *
75 * It implies that [isActive] is `false` and [isCompleted] is `true`.
Roman Elizarov834af462017-01-23 20:50:29 +030076 */
Vsevolod Tolstopyatovf3a50132018-04-16 19:41:20 +030077 public val isCancelled: Boolean
Roman Elizarov187eace2017-01-31 09:39:58 +030078
79 /**
Roman Elizarov7b2d8b02017-02-02 20:09:14 +030080 * Tries to resume this continuation with a given value 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 Elizarov1216e912017-02-22 09:57:06 +030083 *
84 * When [idempotent] is not `null`, this function performs _idempotent_ operation, so that
85 * further invocations with the same non-null reference produce the same result.
86 *
87 * @suppress **This is unstable API and it is subject to change.**
Roman Elizarov187eace2017-01-31 09:39:58 +030088 */
Roman Elizarovaa461cf2018-04-11 13:20:29 +030089 public fun tryResume(value: T, idempotent: Any? = null): Any?
Roman Elizarov7b2d8b02017-02-02 20:09:14 +030090
91 /**
Roman Elizarovf6fed2a2017-02-03 19:12:09 +030092 * Tries to resume this continuation with a given exception and returns non-null object token if it was successful,
93 * or `null` otherwise (it was already resumed or cancelled). When non-null object was returned,
94 * [completeResume] must be invoked with it.
Roman Elizarov1216e912017-02-22 09:57:06 +030095 *
96 * @suppress **This is unstable API and it is subject to change.**
Roman Elizarovf6fed2a2017-02-03 19:12:09 +030097 */
Roman Elizarova198bad2017-02-10 13:30:38 +030098 public fun tryResumeWithException(exception: Throwable): Any?
Roman Elizarovf6fed2a2017-02-03 19:12:09 +030099
100 /**
101 * Completes the execution of [tryResume] or [tryResumeWithException] on its non-null result.
Roman Elizarov1216e912017-02-22 09:57:06 +0300102 *
103 * @suppress **This is unstable API and it is subject to change.**
Roman Elizarov7b2d8b02017-02-02 20:09:14 +0300104 */
Roman Elizarovaa461cf2018-04-11 13:20:29 +0300105 public fun completeResume(token: Any)
Roman Elizarov187eace2017-01-31 09:39:58 +0300106
107 /**
108 * Makes this continuation cancellable. Use it with `holdCancellability` optional parameter to
109 * [suspendCancellableCoroutine] function. It throws [IllegalStateException] if invoked more than once.
110 */
Roman Elizarovaa461cf2018-04-11 13:20:29 +0300111 public fun initCancellability()
Roman Elizarove1c0b652017-12-01 14:02:57 +0300112
113 /**
114 * Cancels this continuation with an optional cancellation [cause]. The result is `true` if this continuation was
115 * cancelled as a result of this invocation and `false` otherwise.
116 */
Vsevolod Tolstopyatovf3a50132018-04-16 19:41:20 +0300117 public fun cancel(cause: Throwable? = null): Boolean
Roman Elizarove1c0b652017-12-01 14:02:57 +0300118
119 /**
Vsevolod Tolstopyatovf3a50132018-04-16 19:41:20 +0300120 * Registers handler that is **synchronously** invoked once on cancellation (both regular and exceptional) of this continuation.
121 * When the continuation is already cancelled, then the handler is immediately invoked
122 * with cancellation exception. Otherwise, the handler will be invoked once on cancellation if this
123 * continuation is cancelled.
Roman Elizarove1c0b652017-12-01 14:02:57 +0300124 *
125 * Installed [handler] should not throw any exceptions. If it does, they will get caught,
Vsevolod Tolstopyatovf3a50132018-04-16 19:41:20 +0300126 * wrapped into [CompletionHandlerException], and rethrown, potentially causing the crash of unrelated code.
127 *
128 * At most one [handler] can be installed on one continuation
Vsevolod Tolstopyatovbb19a262018-05-25 19:14:59 +0300129 *
130 * @param invokeImmediately not used
131 * @param onCancelling not used
132 * @return not used
Roman Elizarove1c0b652017-12-01 14:02:57 +0300133 */
Vsevolod Tolstopyatovf3a50132018-04-16 19:41:20 +0300134 @Deprecated(
135 message = "Disposable handlers on regular completion are no longer supported",
136 replaceWith = ReplaceWith("invokeOnCancellation(handler)"),
Vsevolod Tolstopyatov2cd27fb2018-06-07 13:22:17 +0300137 level = DeprecationLevel.WARNING
Vsevolod Tolstopyatovbb19a262018-05-25 19:14:59 +0300138 )
139 public fun invokeOnCompletion(
140 onCancelling: Boolean = false,
141 invokeImmediately: Boolean = true,
142 handler: CompletionHandler): DisposableHandle
Vsevolod Tolstopyatovf3a50132018-04-16 19:41:20 +0300143
144 /**
145 * Registers handler that is **synchronously** invoked once on cancellation (both regular and exceptional) of this continuation.
146 * When the continuation is already cancelled, then the handler is immediately invoked
147 * with cancellation exception. Otherwise, the handler will be invoked once on cancellation if this
148 * continuation is cancelled.
149 *
150 * Installed [handler] should not throw any exceptions. If it does, they will get caught,
151 * wrapped into [CompletionHandlerException], and rethrown, potentially causing the crash of unrelated code.
152 *
Roman Elizarov58c5fdc2018-06-08 14:01:12 +0300153 * At most one [handler] can be installed on one continuation.
Vsevolod Tolstopyatovf3a50132018-04-16 19:41:20 +0300154 */
155 public fun invokeOnCancellation(handler: CompletionHandler)
Roman Elizarova198bad2017-02-10 13:30:38 +0300156
157 /**
158 * Resumes this continuation with a given [value] in the invoker thread without going though
159 * [dispatch][CoroutineDispatcher.dispatch] function of the [CoroutineDispatcher] in the [context].
160 * This function is designed to be used only by the [CoroutineDispatcher] implementations themselves.
161 * **It should not be used in general code**.
Roman Elizarova198bad2017-02-10 13:30:38 +0300162 */
Roman Elizarovaa461cf2018-04-11 13:20:29 +0300163 public fun CoroutineDispatcher.resumeUndispatched(value: T)
Roman Elizarova198bad2017-02-10 13:30:38 +0300164
165 /**
166 * Resumes this continuation with a given [exception] in the invoker thread without going though
167 * [dispatch][CoroutineDispatcher.dispatch] function of the [CoroutineDispatcher] in the [context].
168 * This function is designed to be used only by the [CoroutineDispatcher] implementations themselves.
169 * **It should not be used in general code**.
Roman Elizarova198bad2017-02-10 13:30:38 +0300170 */
Roman Elizarovaa461cf2018-04-11 13:20:29 +0300171 public fun CoroutineDispatcher.resumeUndispatchedWithException(exception: Throwable)
Roman Elizarov834af462017-01-23 20:50:29 +0300172}
Roman Elizarov3754f952017-01-18 20:47:54 +0300173
174/**
Roman Elizarova74eb5f2017-05-11 20:15:18 +0300175 * Suspends coroutine similar to [suspendCoroutine], but provide an implementation of [CancellableContinuation] to
Roman Elizarovd82b3a92017-06-23 21:52:08 +0300176 * the [block]. This function throws [CancellationException] if the coroutine is cancelled or completed while suspended.
Roman Elizarov187eace2017-01-31 09:39:58 +0300177 *
178 * If [holdCancellability] optional parameter is `true`, then the coroutine is suspended, but it is not
179 * cancellable until [CancellableContinuation.initCancellability] is invoked.
Roman Elizarova74eb5f2017-05-11 20:15:18 +0300180 *
181 * See [suspendAtomicCancellableCoroutine] for suspending functions that need *atomic cancellation*.
Roman Elizarov3754f952017-01-18 20:47:54 +0300182 */
Roman Elizarovaa461cf2018-04-11 13:20:29 +0300183public suspend inline fun <T> suspendCancellableCoroutine(
Roman Elizarov187eace2017-01-31 09:39:58 +0300184 holdCancellability: Boolean = false,
185 crossinline block: (CancellableContinuation<T>) -> Unit
186): T =
Roman Elizarov58a7add2017-01-20 12:19:52 +0300187 suspendCoroutineOrReturn { cont ->
Roman Elizarov2b12d582017-06-22 20:12:19 +0300188 val cancellable = CancellableContinuationImpl(cont, resumeMode = MODE_CANCELLABLE)
Roman Elizarovee7c0eb2017-02-16 15:29:28 +0300189 if (!holdCancellability) cancellable.initCancellability()
190 block(cancellable)
191 cancellable.getResult()
Roman Elizarovaa461cf2018-04-11 13:20:29 +0300192 }
Roman Elizarov3754f952017-01-18 20:47:54 +0300193
Roman Elizarova74eb5f2017-05-11 20:15:18 +0300194/**
195 * Suspends coroutine similar to [suspendCancellableCoroutine], but with *atomic cancellation*.
196 *
197 * When suspended function throws [CancellationException] it means that the continuation was not resumed.
198 * As a side-effect of atomic cancellation, a thread-bound coroutine (to some UI thread, for example) may
199 * continue to execute even after it was cancelled from the same thread in the case when the continuation
200 * was already resumed and was posted for execution to the thread's queue.
201 */
Roman Elizarovaa461cf2018-04-11 13:20:29 +0300202public suspend inline fun <T> suspendAtomicCancellableCoroutine(
Roman Elizarova74eb5f2017-05-11 20:15:18 +0300203 holdCancellability: Boolean = false,
204 crossinline block: (CancellableContinuation<T>) -> Unit
205): T =
206 suspendCoroutineOrReturn { cont ->
Roman Elizarov2b12d582017-06-22 20:12:19 +0300207 val cancellable = CancellableContinuationImpl(cont, resumeMode = MODE_ATOMIC_DEFAULT)
Roman Elizarova74eb5f2017-05-11 20:15:18 +0300208 if (!holdCancellability) cancellable.initCancellability()
209 block(cancellable)
210 cancellable.getResult()
211 }
Roman Elizarov0a788392017-02-15 17:52:12 +0300212
213/**
214 * Removes a given node on cancellation.
215 * @suppress **This is unstable API and it is subject to change.**
216 */
Vsevolod Tolstopyatovf3a50132018-04-16 19:41:20 +0300217@Deprecated(
218 message = "Disposable handlers on cancellation are no longer supported",
219 replaceWith = ReplaceWith("removeOnCancellation(handler)"),
Vsevolod Tolstopyatov2cd27fb2018-06-07 13:22:17 +0300220 level = DeprecationLevel.WARNING)
Vsevolod Tolstopyatovf3a50132018-04-16 19:41:20 +0300221public fun CancellableContinuation<*>.removeOnCancel(node: LockFreeLinkedListNode): DisposableHandle {
Vsevolod Tolstopyatovf6430f42018-04-17 17:56:32 +0300222 removeOnCancellation(node)
Vsevolod Tolstopyatovf3a50132018-04-16 19:41:20 +0300223 return NonDisposableHandle
224}
225
226/**
227 * Removes a given node on cancellation.
228 * @suppress **This is unstable API and it is subject to change.**
229 */
Roman Elizarovdbd9e1c2018-04-28 15:14:18 +0300230public fun CancellableContinuation<*>.removeOnCancellation(node: LockFreeLinkedListNode) =
231 invokeOnCancellation(handler = RemoveOnCancel(node).asHandler)
Vsevolod Tolstopyatovf3a50132018-04-16 19:41:20 +0300232
Vsevolod Tolstopyatov80a29472018-04-17 16:02:02 +0300233/**
234 * Disposes a specified [handle] when this continuation is cancelled.
235 *
236 * This is a shortcut for the following code with slightly more efficient implementation (one fewer object created).
237 * ```
238 * invokeOnCancellation { handle.dispose() }
239 * ```
240 */
Vsevolod Tolstopyatovf3a50132018-04-16 19:41:20 +0300241@Deprecated(
242 message = "Disposable handlers on regular completion are no longer supported",
243 replaceWith = ReplaceWith("disposeOnCancellation(handler)"),
Vsevolod Tolstopyatov2cd27fb2018-06-07 13:22:17 +0300244 level = DeprecationLevel.WARNING)
Vsevolod Tolstopyatovf3a50132018-04-16 19:41:20 +0300245public fun CancellableContinuation<*>.disposeOnCompletion(handle: DisposableHandle): DisposableHandle {
Vsevolod Tolstopyatovf6430f42018-04-17 17:56:32 +0300246 disposeOnCancellation(handle)
Vsevolod Tolstopyatovf3a50132018-04-16 19:41:20 +0300247 return NonDisposableHandle
248}
249
Vsevolod Tolstopyatov80a29472018-04-17 16:02:02 +0300250/**
251 * Disposes a specified [handle] when this continuation is cancelled.
252 *
253 * This is a shortcut for the following code with slightly more efficient implementation (one fewer object created).
254 * ```
255 * invokeOnCancellation { handle.dispose() }
256 * ```
257 */
Roman Elizarovdbd9e1c2018-04-28 15:14:18 +0300258public fun CancellableContinuation<*>.disposeOnCancellation(handle: DisposableHandle) =
259 invokeOnCancellation(handler = DisposeOnCancel(handle).asHandler)
Roman Elizarov0a788392017-02-15 17:52:12 +0300260
Roman Elizarov3754f952017-01-18 20:47:54 +0300261// --------------- implementation details ---------------
262
Roman Elizarovdbd9e1c2018-04-28 15:14:18 +0300263private class RemoveOnCancel(private val node: LockFreeLinkedListNode) : CancelHandler() {
264 override fun invoke(cause: Throwable?) { node.remove() }
Roman Elizarov0a788392017-02-15 17:52:12 +0300265 override fun toString() = "RemoveOnCancel[$node]"
266}
267
Roman Elizarovdbd9e1c2018-04-28 15:14:18 +0300268private class DisposeOnCancel(private val handle: DisposableHandle) : CancelHandler() {
Vsevolod Tolstopyatovf3a50132018-04-16 19:41:20 +0300269 override fun invoke(cause: Throwable?) = handle.dispose()
Roman Elizarovdbd9e1c2018-04-28 15:14:18 +0300270 override fun toString(): String = "DisposeOnCancel[$handle]"
Vsevolod Tolstopyatovf3a50132018-04-16 19:41:20 +0300271}
272
Roman Elizarov3754f952017-01-18 20:47:54 +0300273@PublishedApi
Roman Elizarove22f14a2017-06-22 19:04:52 +0300274internal class CancellableContinuationImpl<in T>(
Roman Elizarovbcdd8e12017-10-20 16:42:06 +0800275 delegate: Continuation<T>,
Roman Elizarov2b12d582017-06-22 20:12:19 +0300276 resumeMode: Int
Roman Elizarovac480702017-12-28 01:09:43 +0300277) : AbstractContinuation<T>(delegate, resumeMode), CancellableContinuation<T>, Runnable {
Roman Elizarov2b12d582017-06-22 20:12:19 +0300278
Vsevolod Tolstopyatovf3a50132018-04-16 19:41:20 +0300279 public override val context: CoroutineContext = delegate.context
Roman Elizarov3754f952017-01-18 20:47:54 +0300280
Roman Elizarov187eace2017-01-31 09:39:58 +0300281 override fun initCancellability() {
Roman Elizarov6640b2b2018-01-17 19:08:55 +0300282 initParentJobInternal(delegate.context[Job])
Roman Elizarov187eace2017-01-31 09:39:58 +0300283 }
Roman Elizarov58a7add2017-01-20 12:19:52 +0300284
Vsevolod Tolstopyatovbb19a262018-05-25 19:14:59 +0300285 override fun invokeOnCompletion(onCancelling: Boolean,
286 invokeImmediately: Boolean, handler: CompletionHandler): DisposableHandle {
Vsevolod Tolstopyatovf3a50132018-04-16 19:41:20 +0300287 invokeOnCancellation(handler)
288 return NonDisposableHandle
289 }
Roman Elizarov4d626de2018-01-11 22:57:28 +0300290
Roman Elizarov1216e912017-02-22 09:57:06 +0300291 override fun tryResume(value: T, idempotent: Any?): Any? {
Vsevolod Tolstopyatovf3a50132018-04-16 19:41:20 +0300292 loopOnState { state ->
Roman Elizarov187eace2017-01-31 09:39:58 +0300293 when (state) {
Vsevolod Tolstopyatovf3a50132018-04-16 19:41:20 +0300294 is NotCompleted -> {
Roman Elizarov932e8602017-06-21 17:21:37 +0300295 val update: Any? = if (idempotent == null) value else
296 CompletedIdempotentResult(idempotent, value, state)
Vsevolod Tolstopyatovf6430f42018-04-17 17:56:32 +0300297 if (tryUpdateStateToFinal(state, update)) return state
Roman Elizarov1216e912017-02-22 09:57:06 +0300298 }
299 is CompletedIdempotentResult -> {
300 if (state.idempotentResume === idempotent) {
301 check(state.result === value) { "Non-idempotent resume" }
302 return state.token
303 } else
304 return null
305 }
Roman Elizarov7b2d8b02017-02-02 20:09:14 +0300306 else -> return null // cannot resume -- not active anymore
Roman Elizarov187eace2017-01-31 09:39:58 +0300307 }
308 }
309 }
310
Roman Elizarovf6fed2a2017-02-03 19:12:09 +0300311 override fun tryResumeWithException(exception: Throwable): Any? {
Vsevolod Tolstopyatovf3a50132018-04-16 19:41:20 +0300312 loopOnState { state ->
313 when (state) {
314 is NotCompleted -> {
Vsevolod Tolstopyatovf6430f42018-04-17 17:56:32 +0300315 if (tryUpdateStateToFinal(state, CompletedExceptionally(exception))) return state
Roman Elizarov1216e912017-02-22 09:57:06 +0300316 }
Roman Elizarovf6fed2a2017-02-03 19:12:09 +0300317 else -> return null // cannot resume -- not active anymore
318 }
319 }
320 }
321
Vsevolod Tolstopyatovf6430f42018-04-17 17:56:32 +0300322 override fun completeResume(token: Any) = completeStateUpdate(token as NotCompleted, state, resumeMode)
Roman Elizarov7b2d8b02017-02-02 20:09:14 +0300323
Roman Elizarova198bad2017-02-10 13:30:38 +0300324 override fun CoroutineDispatcher.resumeUndispatched(value: T) {
Roman Elizarov8552f0e2017-09-28 13:03:44 +0300325 val dc = delegate as? DispatchedContinuation
326 resumeImpl(value, if (dc?.dispatcher === this) MODE_UNDISPATCHED else resumeMode)
Roman Elizarova198bad2017-02-10 13:30:38 +0300327 }
328
329 override fun CoroutineDispatcher.resumeUndispatchedWithException(exception: Throwable) {
Roman Elizarov8552f0e2017-09-28 13:03:44 +0300330 val dc = delegate as? DispatchedContinuation
Roman Elizarovbcdd8e12017-10-20 16:42:06 +0800331 resumeImpl(CompletedExceptionally(exception), if (dc?.dispatcher === this) MODE_UNDISPATCHED else resumeMode)
Roman Elizarov1216e912017-02-22 09:57:06 +0300332 }
Roman Elizarov69d9c852017-07-12 15:22:21 +0300333
Roman Elizarovbcdd8e12017-10-20 16:42:06 +0800334 @Suppress("UNCHECKED_CAST")
335 override fun <T> getSuccessfulResult(state: Any?): T =
336 if (state is CompletedIdempotentResult) state.result as T else state as T
337
Vsevolod Tolstopyatovf3a50132018-04-16 19:41:20 +0300338 protected override fun nameString(): String =
Roman Elizarov8b38fa22017-09-27 17:44:31 +0300339 "CancellableContinuation(${delegate.toDebugString()})"
Roman Elizarov3754f952017-01-18 20:47:54 +0300340}
Roman Elizarove22f14a2017-06-22 19:04:52 +0300341
342private class CompletedIdempotentResult(
343 @JvmField val idempotentResume: Any?,
344 @JvmField val result: Any?,
Vsevolod Tolstopyatovf3a50132018-04-16 19:41:20 +0300345 @JvmField val token: NotCompleted
Roman Elizarove22f14a2017-06-22 19:04:52 +0300346) {
347 override fun toString(): String = "CompletedIdempotentResult[$result]"
348}