blob: e36a74bbe82b228118b0aa8a3b6e67cee834af58 [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 Elizarov4638d792017-03-14 19:39:26 +030019import kotlinx.coroutines.experimental.intrinsics.startCoroutineUndispatched
Roman Elizarov1216e912017-02-22 09:57:06 +030020import kotlinx.coroutines.experimental.selects.SelectBuilder
21import kotlinx.coroutines.experimental.selects.SelectInstance
Roman Elizarovd84dbc22017-02-22 14:56:58 +030022import kotlinx.coroutines.experimental.selects.select
Roman Elizarovea4a51b2017-01-31 12:01:25 +030023import kotlin.coroutines.experimental.CoroutineContext
24import kotlin.coroutines.experimental.startCoroutine
Roman Elizarov3754f952017-01-18 20:47:54 +030025
26/**
Roman Elizarov32d95322017-02-09 15:57:31 +030027 * Deferred value is a non-blocking cancellable future.
28 * It is created with [async] coroutine builder.
Roman Elizarov41c5c8b2017-01-25 13:37:15 +030029 * It is in [active][isActive] state while the value is being computed.
Roman Elizarovb7c46de2017-02-08 12:35:24 +030030 *
Roman Elizarov32d95322017-02-09 15:57:31 +030031 * Deferred value has four or five possible states.
Roman Elizarovb7c46de2017-02-08 12:35:24 +030032 *
Roman Elizarov32d95322017-02-09 15:57:31 +030033 * | **State** | [isActive] | [isCompleted] | [isCompletedExceptionally] | [isCancelled] |
Roman Elizarov7886ef62017-02-13 14:00:18 +030034 * | -------------------------------- | ---------- | ------------- | -------------------------- | ------------- |
Roman Elizarov32d95322017-02-09 15:57:31 +030035 * | _New_ (optional initial state) | `false` | `false` | `false` | `false` |
36 * | _Active_ (default initial state) | `true` | `false` | `false` | `false` |
37 * | _Resolved_ (final state) | `false` | `true` | `false` | `false` |
38 * | _Failed_ (final state) | `false` | `true` | `true` | `false` |
39 * | _Cancelled_ (final state) | `false` | `true` | `true` | `true` |
40 *
41 * Usually, a deferred value is created in _active_ state (it is created and started), so its only visible
42 * states are _active_ and _completed_ (_resolved_, _failed_, or _cancelled_) state.
43 * However, [async] coroutine builder has an optional `start` parameter that creates a deferred value in _new_ state
Roman Elizarovecda27f2017-04-06 23:06:26 +030044 * when this parameter is set to [CoroutineStart.LAZY].
Roman Elizarov32d95322017-02-09 15:57:31 +030045 * Such a deferred can be be made _active_ by invoking [start], [join], or [await].
Roman Elizarov3754f952017-01-18 20:47:54 +030046 */
47public interface Deferred<out T> : Job {
48 /**
Roman Elizarovb7c46de2017-02-08 12:35:24 +030049 * Returns `true` if computation of this deferred value has _completed exceptionally_ -- it had
50 * either _failed_ with exception during computation or was [cancelled][cancel].
Roman Elizarov32d95322017-02-09 15:57:31 +030051 *
52 * It implies that [isActive] is `false` and [isCompleted] is `true`.
Roman Elizarovb7c46de2017-02-08 12:35:24 +030053 */
54 val isCompletedExceptionally: Boolean
55
56 /**
57 * Returns `true` if computation of this deferred value was [cancelled][cancel].
Roman Elizarov32d95322017-02-09 15:57:31 +030058 *
59 * It implies that [isActive] is `false`, [isCompleted] is `true`, and [isCompletedExceptionally] is `true`.
Roman Elizarovb7c46de2017-02-08 12:35:24 +030060 */
61 val isCancelled: Boolean
62
63 /**
Roman Elizarovbe4cae32017-02-15 17:57:02 +030064 * Awaits for completion of this value without blocking a thread and resumes when deferred computation is complete,
65 * returning the resulting value or throwing the corresponding exception if the deferred had completed exceptionally.
Roman Elizarov32d95322017-02-09 15:57:31 +030066 *
Roman Elizarovbe4cae32017-02-15 17:57:02 +030067 * This suspending function is cancellable.
Roman Elizarov3754f952017-01-18 20:47:54 +030068 * If the [Job] of the current coroutine is completed while this suspending function is waiting, this function
Roman Elizarovc5814542017-01-19 10:19:06 +030069 * immediately resumes with [CancellationException].
Roman Elizarovd84dbc22017-02-22 14:56:58 +030070 *
71 * This function can be used in [select] invocation with [onAwait][SelectBuilder.onAwait] clause.
72 * Use [isCompleted] to check for completion of this deferred value without waiting.
Roman Elizarov3754f952017-01-18 20:47:54 +030073 */
74 public suspend fun await(): T
Roman Elizarovc5814542017-01-19 10:19:06 +030075
76 /**
Roman Elizarov1216e912017-02-22 09:57:06 +030077 * Registers [onAwait][SelectBuilder.onAwait] select clause.
78 * @suppress **This is unstable API and it is subject to change.**
79 */
80 public fun <R> registerSelectAwait(select: SelectInstance<R>, block: suspend (T) -> R)
81
82 /**
Roman Elizarov32d95322017-02-09 15:57:31 +030083 * Returns *completed* result or throws [IllegalStateException] if this deferred value has not
84 * [completed][isCompleted] yet. It throws the corresponding exception if this deferred has
85 * [completed exceptionally][isCompletedExceptionally].
86 *
Roman Elizarove7803472017-02-16 09:52:31 +030087 * This function is designed to be used from [invokeOnCompletion] handlers, when there is an absolute certainty that
Roman Elizarovc5814542017-01-19 10:19:06 +030088 * the value is already complete.
89 */
90 public fun getCompleted(): T
Roman Elizarov32d95322017-02-09 15:57:31 +030091
92 /**
Roman Elizarovfc7a9a22017-02-13 11:54:01 +030093 * @suppress **Deprecated**: Use `isActive`.
Roman Elizarov32d95322017-02-09 15:57:31 +030094 */
95 @Deprecated(message = "Use `isActive`", replaceWith = ReplaceWith("isActive"))
96 public val isComputing: Boolean get() = isActive
Roman Elizarov3754f952017-01-18 20:47:54 +030097}
98
99/**
Roman Elizarov32d95322017-02-09 15:57:31 +0300100 * Creates new coroutine and returns its future result as an implementation of [Deferred].
Roman Elizarov44ba4b12017-01-25 11:37:54 +0300101 *
Roman Elizarov32d95322017-02-09 15:57:31 +0300102 * The running coroutine is cancelled when the resulting object is [cancelled][Job.cancel].
Roman Elizarov44ba4b12017-01-25 11:37:54 +0300103 * The [context] for the new coroutine must be explicitly specified.
Roman Elizaroved7b8642017-01-19 11:22:28 +0300104 * See [CoroutineDispatcher] for the standard [context] implementations that are provided by `kotlinx.coroutines`.
Roman Elizarov44ba4b12017-01-25 11:37:54 +0300105 * The [context][CoroutineScope.context] of the parent coroutine from its [scope][CoroutineScope] may be used,
106 * in which case the [Job] of the resulting coroutine is a child of the job of the parent coroutine.
Roman Elizarov32d95322017-02-09 15:57:31 +0300107 *
Roman Elizarovecda27f2017-04-06 23:06:26 +0300108 * By default, the coroutine is immediately started.
109 * An optional [start] parameter can be set to [CoroutineStart.LAZY] to start coroutine _lazily_. In this case,,
Roman Elizarov32d95322017-02-09 15:57:31 +0300110 * the resulting [Deferred] is created in _new_ state. It can be explicitly started with [start][Job.start]
111 * function and will be started implicitly on the first invocation of [join][Job.join] or [await][Deferred.await].
112 *
Roman Elizarovecda27f2017-04-06 23:06:26 +0300113 * @param context context of the coroutine
114 * @param start coroutine start option
115 * @param block the coroutine code
Roman Elizarov3754f952017-01-18 20:47:54 +0300116 */
Roman Elizarovecda27f2017-04-06 23:06:26 +0300117public fun <T> async(
118 context: CoroutineContext,
119 start: CoroutineStart = CoroutineStart.DEFAULT,
120 block: suspend CoroutineScope.() -> T
121): Deferred<T> {
Roman Elizarov32d95322017-02-09 15:57:31 +0300122 val newContext = newCoroutineContext(context)
Roman Elizarovecda27f2017-04-06 23:06:26 +0300123 val coroutine = if (start.isLazy)
124 LazyDeferredCoroutine(newContext, block) else
125 DeferredCoroutine<T>(newContext, active = true)
Roman Elizarov32d95322017-02-09 15:57:31 +0300126 coroutine.initParentJob(context[Job])
Roman Elizarovecda27f2017-04-06 23:06:26 +0300127 start(block, coroutine, coroutine)
Roman Elizarov32d95322017-02-09 15:57:31 +0300128 return coroutine
129}
130
131/**
Roman Elizarovecda27f2017-04-06 23:06:26 +0300132 * @suppress **Deprecated**: Use `start = CoroutineStart.XXX` parameter
133 */
134@Deprecated(message = "Use `start = CoroutineStart.XXX` parameter",
135 replaceWith = ReplaceWith("async(context, if (start) CoroutineStart.DEFAULT else CoroutineStart.LAZY, block)"))
136public fun <T> async(context: CoroutineContext, start: Boolean, block: suspend CoroutineScope.() -> T): Deferred<T> =
137 async(context, if (start) CoroutineStart.DEFAULT else CoroutineStart.LAZY, block)
138
139/**
Roman Elizarovfc7a9a22017-02-13 11:54:01 +0300140 * @suppress **Deprecated**: `defer` was renamed to `async`.
Roman Elizarov32d95322017-02-09 15:57:31 +0300141 */
142@Deprecated(message = "`defer` was renamed to `async`", level = DeprecationLevel.WARNING,
143 replaceWith = ReplaceWith("async(context, block = block)"))
Roman Elizarovecda27f2017-04-06 23:06:26 +0300144public fun <T> defer(context: CoroutineContext, block: suspend CoroutineScope.() -> T): Deferred<T> =
Roman Elizarov32d95322017-02-09 15:57:31 +0300145 async(context, block = block)
Roman Elizarov3754f952017-01-18 20:47:54 +0300146
Roman Elizarov32d95322017-02-09 15:57:31 +0300147private open class DeferredCoroutine<T>(
Roman Elizarov1216e912017-02-22 09:57:06 +0300148 override val parentContext: CoroutineContext,
Roman Elizarov32d95322017-02-09 15:57:31 +0300149 active: Boolean
Roman Elizarov1216e912017-02-22 09:57:06 +0300150) : AbstractCoroutine<T>(active), Deferred<T> {
Roman Elizarovee7c0eb2017-02-16 15:29:28 +0300151 override val isCompletedExceptionally: Boolean get() = state is CompletedExceptionally
152 override val isCancelled: Boolean get() = state is Cancelled
Roman Elizarovb7c46de2017-02-08 12:35:24 +0300153
Roman Elizarov3754f952017-01-18 20:47:54 +0300154 @Suppress("UNCHECKED_CAST")
155 suspend override fun await(): T {
Roman Elizarov32d95322017-02-09 15:57:31 +0300156 // fast-path -- check state (avoid extra object creation)
157 while(true) { // lock-free loop on state
Roman Elizarovee7c0eb2017-02-16 15:29:28 +0300158 val state = this.state
Roman Elizarov32d95322017-02-09 15:57:31 +0300159 if (state !is Incomplete) {
160 // already complete -- just return result
Roman Elizarov41c5c8b2017-01-25 13:37:15 +0300161 if (state is CompletedExceptionally) throw state.exception
162 return state as T
Roman Elizarov32d95322017-02-09 15:57:31 +0300163
Roman Elizarov41c5c8b2017-01-25 13:37:15 +0300164 }
Roman Elizarov32d95322017-02-09 15:57:31 +0300165 if (startInternal(state) >= 0) break // break unless needs to retry
Roman Elizarov41c5c8b2017-01-25 13:37:15 +0300166 }
Roman Elizarov32d95322017-02-09 15:57:31 +0300167 return awaitSuspend() // slow-path
Roman Elizarov3754f952017-01-18 20:47:54 +0300168 }
169
170 @Suppress("UNCHECKED_CAST")
Roman Elizarov32d95322017-02-09 15:57:31 +0300171 private suspend fun awaitSuspend(): T = suspendCancellableCoroutine { cont ->
Roman Elizarovdaa1d9d2017-03-02 19:00:50 +0300172 cont.disposeOnCompletion(invokeOnCompletion {
Roman Elizarovee7c0eb2017-02-16 15:29:28 +0300173 val state = this.state
Roman Elizarov32d95322017-02-09 15:57:31 +0300174 check(state !is Incomplete)
Roman Elizarov3754f952017-01-18 20:47:54 +0300175 if (state is CompletedExceptionally)
176 cont.resumeWithException(state.exception)
177 else
178 cont.resume(state as T)
179 })
180 }
181
Roman Elizarovd84dbc22017-02-22 14:56:58 +0300182 @Suppress("UNCHECKED_CAST")
Roman Elizarov1216e912017-02-22 09:57:06 +0300183 override fun <R> registerSelectAwait(select: SelectInstance<R>, block: suspend (T) -> R) {
Roman Elizarovd84dbc22017-02-22 14:56:58 +0300184 // fast-path -- check state and select/return if needed
185 while (true) {
186 if (select.isSelected) return
187 val state = this.state
188 if (state !is Incomplete) {
189 // already complete -- select result
190 if (select.trySelect(idempotent = null)) {
191 if (state is CompletedExceptionally)
192 select.resumeSelectWithException(state.exception, MODE_DIRECT)
193 else
Roman Elizarov4638d792017-03-14 19:39:26 +0300194 block.startCoroutineUndispatched(state as T, select.completion)
Roman Elizarovd84dbc22017-02-22 14:56:58 +0300195 }
196 return
197 }
198 if (startInternal(state) == 0) {
199 // slow-path -- register waiter for completion
Roman Elizarovdaa1d9d2017-03-02 19:00:50 +0300200 select.disposeOnSelect(invokeOnCompletion(SelectAwaitOnCompletion(this, select, block)))
Roman Elizarovd84dbc22017-02-22 14:56:58 +0300201 return
202 }
203 }
Roman Elizarov1216e912017-02-22 09:57:06 +0300204 }
205
206 @Suppress("UNCHECKED_CAST")
Roman Elizarovd84dbc22017-02-22 14:56:58 +0300207 internal fun <R> selectAwaitCompletion(select: SelectInstance<R>, block: suspend (T) -> R, state: Any? = this.state) {
Roman Elizarov1216e912017-02-22 09:57:06 +0300208 if (select.trySelect(idempotent = null)) {
209 if (state is CompletedExceptionally)
Roman Elizarovd84dbc22017-02-22 14:56:58 +0300210 select.resumeSelectWithException(state.exception, MODE_DISPATCHED)
Roman Elizarov1216e912017-02-22 09:57:06 +0300211 else
Roman Elizarovd84dbc22017-02-22 14:56:58 +0300212 block.startCoroutine(state as T, select.completion)
Roman Elizarov1216e912017-02-22 09:57:06 +0300213 }
214 }
215
Roman Elizarovc5814542017-01-19 10:19:06 +0300216 @Suppress("UNCHECKED_CAST")
217 override fun getCompleted(): T {
Roman Elizarovee7c0eb2017-02-16 15:29:28 +0300218 val state = this.state
Roman Elizarov32d95322017-02-09 15:57:31 +0300219 check(state !is Incomplete) { "This deferred value has not completed yet" }
Roman Elizarovc5814542017-01-19 10:19:06 +0300220 if (state is CompletedExceptionally) throw state.exception
221 return state as T
Roman Elizarov3754f952017-01-18 20:47:54 +0300222 }
Roman Elizarov32d95322017-02-09 15:57:31 +0300223}
224
Roman Elizarovd84dbc22017-02-22 14:56:58 +0300225private class SelectAwaitOnCompletion<T, R>(
Roman Elizarov1216e912017-02-22 09:57:06 +0300226 deferred: DeferredCoroutine<T>,
227 private val select: SelectInstance<R>,
228 private val block: suspend (T) -> R
229) : JobNode<DeferredCoroutine<T>>(deferred) {
Roman Elizarovd84dbc22017-02-22 14:56:58 +0300230 override fun invoke(reason: Throwable?) = job.selectAwaitCompletion(select, block)
231 override fun toString(): String = "SelectAwaitOnCompletion[$select]"
Roman Elizarov1216e912017-02-22 09:57:06 +0300232}
233
Roman Elizarov32d95322017-02-09 15:57:31 +0300234private class LazyDeferredCoroutine<T>(
Roman Elizarov1216e912017-02-22 09:57:06 +0300235 parentContext: CoroutineContext,
236 private val block: suspend CoroutineScope.() -> T
237) : DeferredCoroutine<T>(parentContext, active = false) {
Roman Elizarov32d95322017-02-09 15:57:31 +0300238 override fun onStart() {
239 block.startCoroutine(this, this)
240 }
Roman Elizarov3754f952017-01-18 20:47:54 +0300241}