blob: 2e69343c2bb6e60aee65acdbc85b325278de4355 [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 Elizarov1216e912017-02-22 09:57:06 +030019import kotlinx.coroutines.experimental.intrinsics.startUndispatchedCoroutine
20import 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
44 * when this parameter is set to `false`.
45 * 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 *
108 * An optional [start] parameter can be set to `false` to start coroutine _lazily_. When `start = false`,
109 * the resulting [Deferred] is created in _new_ state. It can be explicitly started with [start][Job.start]
110 * function and will be started implicitly on the first invocation of [join][Job.join] or [await][Deferred.await].
111 *
112 * By default, the coroutine is immediately started. Set an optional [start] parameters to `false`
113 * to create coroutine without starting it. In this case it will be _lazy_ and will start
Roman Elizarov3754f952017-01-18 20:47:54 +0300114 */
Roman Elizarov32d95322017-02-09 15:57:31 +0300115public fun <T> async(context: CoroutineContext, start: Boolean = true, block: suspend CoroutineScope.() -> T) : Deferred<T> {
116 val newContext = newCoroutineContext(context)
117 val coroutine = if (start)
118 DeferredCoroutine<T>(newContext, active = true) else
119 LazyDeferredCoroutine(newContext, block)
120 coroutine.initParentJob(context[Job])
121 if (start) block.startCoroutine(coroutine, coroutine)
122 return coroutine
123}
124
125/**
Roman Elizarovfc7a9a22017-02-13 11:54:01 +0300126 * @suppress **Deprecated**: `defer` was renamed to `async`.
Roman Elizarov32d95322017-02-09 15:57:31 +0300127 */
128@Deprecated(message = "`defer` was renamed to `async`", level = DeprecationLevel.WARNING,
129 replaceWith = ReplaceWith("async(context, block = block)"))
Roman Elizarovd528e3e2017-01-23 15:40:05 +0300130public fun <T> defer(context: CoroutineContext, block: suspend CoroutineScope.() -> T) : Deferred<T> =
Roman Elizarov32d95322017-02-09 15:57:31 +0300131 async(context, block = block)
Roman Elizarov3754f952017-01-18 20:47:54 +0300132
Roman Elizarov32d95322017-02-09 15:57:31 +0300133private open class DeferredCoroutine<T>(
Roman Elizarov1216e912017-02-22 09:57:06 +0300134 override val parentContext: CoroutineContext,
Roman Elizarov32d95322017-02-09 15:57:31 +0300135 active: Boolean
Roman Elizarov1216e912017-02-22 09:57:06 +0300136) : AbstractCoroutine<T>(active), Deferred<T> {
Roman Elizarovee7c0eb2017-02-16 15:29:28 +0300137 override val isCompletedExceptionally: Boolean get() = state is CompletedExceptionally
138 override val isCancelled: Boolean get() = state is Cancelled
Roman Elizarovb7c46de2017-02-08 12:35:24 +0300139
Roman Elizarov3754f952017-01-18 20:47:54 +0300140 @Suppress("UNCHECKED_CAST")
141 suspend override fun await(): T {
Roman Elizarov32d95322017-02-09 15:57:31 +0300142 // fast-path -- check state (avoid extra object creation)
143 while(true) { // lock-free loop on state
Roman Elizarovee7c0eb2017-02-16 15:29:28 +0300144 val state = this.state
Roman Elizarov32d95322017-02-09 15:57:31 +0300145 if (state !is Incomplete) {
146 // already complete -- just return result
Roman Elizarov41c5c8b2017-01-25 13:37:15 +0300147 if (state is CompletedExceptionally) throw state.exception
148 return state as T
Roman Elizarov32d95322017-02-09 15:57:31 +0300149
Roman Elizarov41c5c8b2017-01-25 13:37:15 +0300150 }
Roman Elizarov32d95322017-02-09 15:57:31 +0300151 if (startInternal(state) >= 0) break // break unless needs to retry
Roman Elizarov41c5c8b2017-01-25 13:37:15 +0300152 }
Roman Elizarov32d95322017-02-09 15:57:31 +0300153 return awaitSuspend() // slow-path
Roman Elizarov3754f952017-01-18 20:47:54 +0300154 }
155
156 @Suppress("UNCHECKED_CAST")
Roman Elizarov32d95322017-02-09 15:57:31 +0300157 private suspend fun awaitSuspend(): T = suspendCancellableCoroutine { cont ->
Roman Elizarovdaa1d9d2017-03-02 19:00:50 +0300158 cont.disposeOnCompletion(invokeOnCompletion {
Roman Elizarovee7c0eb2017-02-16 15:29:28 +0300159 val state = this.state
Roman Elizarov32d95322017-02-09 15:57:31 +0300160 check(state !is Incomplete)
Roman Elizarov3754f952017-01-18 20:47:54 +0300161 if (state is CompletedExceptionally)
162 cont.resumeWithException(state.exception)
163 else
164 cont.resume(state as T)
165 })
166 }
167
Roman Elizarovd84dbc22017-02-22 14:56:58 +0300168 @Suppress("UNCHECKED_CAST")
Roman Elizarov1216e912017-02-22 09:57:06 +0300169 override fun <R> registerSelectAwait(select: SelectInstance<R>, block: suspend (T) -> R) {
Roman Elizarovd84dbc22017-02-22 14:56:58 +0300170 // fast-path -- check state and select/return if needed
171 while (true) {
172 if (select.isSelected) return
173 val state = this.state
174 if (state !is Incomplete) {
175 // already complete -- select result
176 if (select.trySelect(idempotent = null)) {
177 if (state is CompletedExceptionally)
178 select.resumeSelectWithException(state.exception, MODE_DIRECT)
179 else
180 block.startUndispatchedCoroutine(state as T, select.completion)
181 }
182 return
183 }
184 if (startInternal(state) == 0) {
185 // slow-path -- register waiter for completion
Roman Elizarovdaa1d9d2017-03-02 19:00:50 +0300186 select.disposeOnSelect(invokeOnCompletion(SelectAwaitOnCompletion(this, select, block)))
Roman Elizarovd84dbc22017-02-22 14:56:58 +0300187 return
188 }
189 }
Roman Elizarov1216e912017-02-22 09:57:06 +0300190 }
191
192 @Suppress("UNCHECKED_CAST")
Roman Elizarovd84dbc22017-02-22 14:56:58 +0300193 internal fun <R> selectAwaitCompletion(select: SelectInstance<R>, block: suspend (T) -> R, state: Any? = this.state) {
Roman Elizarov1216e912017-02-22 09:57:06 +0300194 if (select.trySelect(idempotent = null)) {
195 if (state is CompletedExceptionally)
Roman Elizarovd84dbc22017-02-22 14:56:58 +0300196 select.resumeSelectWithException(state.exception, MODE_DISPATCHED)
Roman Elizarov1216e912017-02-22 09:57:06 +0300197 else
Roman Elizarovd84dbc22017-02-22 14:56:58 +0300198 block.startCoroutine(state as T, select.completion)
Roman Elizarov1216e912017-02-22 09:57:06 +0300199 }
200 }
201
Roman Elizarovc5814542017-01-19 10:19:06 +0300202 @Suppress("UNCHECKED_CAST")
203 override fun getCompleted(): T {
Roman Elizarovee7c0eb2017-02-16 15:29:28 +0300204 val state = this.state
Roman Elizarov32d95322017-02-09 15:57:31 +0300205 check(state !is Incomplete) { "This deferred value has not completed yet" }
Roman Elizarovc5814542017-01-19 10:19:06 +0300206 if (state is CompletedExceptionally) throw state.exception
207 return state as T
Roman Elizarov3754f952017-01-18 20:47:54 +0300208 }
Roman Elizarov32d95322017-02-09 15:57:31 +0300209}
210
Roman Elizarovd84dbc22017-02-22 14:56:58 +0300211private class SelectAwaitOnCompletion<T, R>(
Roman Elizarov1216e912017-02-22 09:57:06 +0300212 deferred: DeferredCoroutine<T>,
213 private val select: SelectInstance<R>,
214 private val block: suspend (T) -> R
215) : JobNode<DeferredCoroutine<T>>(deferred) {
Roman Elizarovd84dbc22017-02-22 14:56:58 +0300216 override fun invoke(reason: Throwable?) = job.selectAwaitCompletion(select, block)
217 override fun toString(): String = "SelectAwaitOnCompletion[$select]"
Roman Elizarov1216e912017-02-22 09:57:06 +0300218}
219
Roman Elizarov32d95322017-02-09 15:57:31 +0300220private class LazyDeferredCoroutine<T>(
Roman Elizarov1216e912017-02-22 09:57:06 +0300221 parentContext: CoroutineContext,
222 private val block: suspend CoroutineScope.() -> T
223) : DeferredCoroutine<T>(parentContext, active = false) {
Roman Elizarov32d95322017-02-09 15:57:31 +0300224 override fun onStart() {
225 block.startCoroutine(this, this)
226 }
Roman Elizarov3754f952017-01-18 20:47:54 +0300227}