`defer` coroutine builder is renamed to `async`.
`lazyDefer` is deprecated, `async` has an optional `start` parameter instead.
`LazyDeferred` interface is deprecated, lazy start functionality is integrated into `Job` interface.
`launch` has an optional `start` parameter for lazily started coroutines.
`Job.start` and `Job.isCompleted` are introduced.
`Job.join` is now a member function.
Internal `JobSupport` state machine is enhanced to support _new_ (not-started-yet) state.
So, lazy coroutines do not need a separate state variable to track their started/not-started (new/active) status.
Example on async-style functions is added to coroutines guide.
diff --git a/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Deferred.kt b/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Deferred.kt
index ab61173..515da4d 100644
--- a/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Deferred.kt
+++ b/kotlinx-coroutines-core/src/main/kotlin/kotlinx/coroutines/experimental/Deferred.kt
@@ -20,103 +20,129 @@
import kotlin.coroutines.experimental.startCoroutine
/**
- * Deferred value is conceptually a non-blocking cancellable future.
- * It is created with [defer] coroutine builder.
+ * Deferred value is a non-blocking cancellable future.
+ * It is created with [async] coroutine builder.
* It is in [active][isActive] state while the value is being computed.
*
- * Deferred value has four states:
+ * Deferred value has four or five possible states.
*
- * * _Active_ (initial state) -- [isActive] `true`, [isCompletedExceptionally] `false`,
- * and [isCancelled] `false`.
- * Both [getCompleted] and [getCompletionException] throw [IllegalStateException].
- * * _Computed_ (final _completed_ state) -- [isActive] `false`,
- * [isCompletedExceptionally] `false`, [isCancelled] `false`.
- * * _Failed_ (final _completed_ state) -- [isActive] `false`,
- * [isCompletedExceptionally] `true`, [isCancelled] `false`.
- * * _Canceled_ (final _completed_ state) -- [isActive] `false`,
- * [isCompletedExceptionally] `true`, [isCancelled] `true`.
+ * | **State** | [isActive] | [isCompleted] | [isCompletedExceptionally] | [isCancelled] |
+ * | _New_ (optional initial state) | `false` | `false` | `false` | `false` |
+ * | _Active_ (default initial state) | `true` | `false` | `false` | `false` |
+ * | _Resolved_ (final state) | `false` | `true` | `false` | `false` |
+ * | _Failed_ (final state) | `false` | `true` | `true` | `false` |
+ * | _Cancelled_ (final state) | `false` | `true` | `true` | `true` |
+ *
+ * Usually, a deferred value is created in _active_ state (it is created and started), so its only visible
+ * states are _active_ and _completed_ (_resolved_, _failed_, or _cancelled_) state.
+ * However, [async] coroutine builder has an optional `start` parameter that creates a deferred value in _new_ state
+ * when this parameter is set to `false`.
+ * Such a deferred can be be made _active_ by invoking [start], [join], or [await].
*/
public interface Deferred<out T> : Job {
/**
* Returns `true` if computation of this deferred value has _completed exceptionally_ -- it had
* either _failed_ with exception during computation or was [cancelled][cancel].
- * It implies that [isActive] is `false`.
+ *
+ * It implies that [isActive] is `false` and [isCompleted] is `true`.
*/
val isCompletedExceptionally: Boolean
/**
* Returns `true` if computation of this deferred value was [cancelled][cancel].
- * It implies that [isActive] is `false` and [isCompletedExceptionally] is `true`.
+ *
+ * It implies that [isActive] is `false`, [isCompleted] is `true`, and [isCompletedExceptionally] is `true`.
*/
val isCancelled: Boolean
/**
* Awaits for completion of this value without blocking a thread and resumes when deferred computation is complete.
* This suspending function is cancellable.
+ *
* If the [Job] of the current coroutine is completed while this suspending function is waiting, this function
* immediately resumes with [CancellationException].
*/
public suspend fun await(): T
/**
- * Returns *completed* result or throws [IllegalStateException] if this deferred value is still [isActive].
- * It throws the corresponding exception if this deferred has completed exceptionally.
+ * Returns *completed* result or throws [IllegalStateException] if this deferred value has not
+ * [completed][isCompleted] yet. It throws the corresponding exception if this deferred has
+ * [completed exceptionally][isCompletedExceptionally].
+ *
* This function is designed to be used from [onCompletion] handlers, when there is an absolute certainty that
* the value is already complete.
*/
public fun getCompleted(): T
+
+ /**
+ * **Deprecated**: Use `isActive`.
+ */
+ @Deprecated(message = "Use `isActive`", replaceWith = ReplaceWith("isActive"))
+ public val isComputing: Boolean get() = isActive
}
/**
- * Starts new coroutine and returns its result as an implementation of [Deferred].
- * The running coroutine is cancelled when the resulting object is [cancelled][Job.cancel].
+ * Creates new coroutine and returns its future result as an implementation of [Deferred].
*
+ * The running coroutine is cancelled when the resulting object is [cancelled][Job.cancel].
* The [context] for the new coroutine must be explicitly specified.
* See [CoroutineDispatcher] for the standard [context] implementations that are provided by `kotlinx.coroutines`.
* The [context][CoroutineScope.context] of the parent coroutine from its [scope][CoroutineScope] may be used,
* in which case the [Job] of the resulting coroutine is a child of the job of the parent coroutine.
+ *
+ * An optional [start] parameter can be set to `false` to start coroutine _lazily_. When `start = false`,
+ * the resulting [Deferred] is created in _new_ state. It can be explicitly started with [start][Job.start]
+ * function and will be started implicitly on the first invocation of [join][Job.join] or [await][Deferred.await].
+ *
+ * By default, the coroutine is immediately started. Set an optional [start] parameters to `false`
+ * to create coroutine without starting it. In this case it will be _lazy_ and will start
*/
+public fun <T> async(context: CoroutineContext, start: Boolean = true, block: suspend CoroutineScope.() -> T) : Deferred<T> {
+ val newContext = newCoroutineContext(context)
+ val coroutine = if (start)
+ DeferredCoroutine<T>(newContext, active = true) else
+ LazyDeferredCoroutine(newContext, block)
+ coroutine.initParentJob(context[Job])
+ if (start) block.startCoroutine(coroutine, coroutine)
+ return coroutine
+}
+
+/**
+ * **Deprecated**: `defer` was renamed to `async`.
+ */
+@Deprecated(message = "`defer` was renamed to `async`", level = DeprecationLevel.WARNING,
+ replaceWith = ReplaceWith("async(context, block = block)"))
public fun <T> defer(context: CoroutineContext, block: suspend CoroutineScope.() -> T) : Deferred<T> =
- DeferredCoroutine<T>(newCoroutineContext(context)).apply {
- initParentJob(context[Job])
- block.startCoroutine(this, this)
- }
+ async(context, block = block)
-internal open class DeferredCoroutine<T>(
- context: CoroutineContext
-) : AbstractCoroutine<T>(context), Deferred<T> {
- protected open fun start(): Boolean = false // LazyDeferredCoroutine overrides
-
+private open class DeferredCoroutine<T>(
+ context: CoroutineContext,
+ active: Boolean
+) : AbstractCoroutine<T>(context, active), Deferred<T> {
override val isCompletedExceptionally: Boolean get() = getState() is CompletedExceptionally
override val isCancelled: Boolean get() = getState() is Cancelled
@Suppress("UNCHECKED_CAST")
suspend override fun await(): T {
- // quick check if already complete (avoid extra object creation)
- getState().let { state ->
- if (state !is Active) {
+ // fast-path -- check state (avoid extra object creation)
+ while(true) { // lock-free loop on state
+ val state = this.getState()
+ if (state !is Incomplete) {
+ // already complete -- just return result
if (state is CompletedExceptionally) throw state.exception
return state as T
+
}
+ if (startInternal(state) >= 0) break // break unless needs to retry
}
- if (start()) { // LazyDeferredCoroutine overrides
- // recheck state (may have started & already completed
- getState().let { state ->
- if (state !is Active) {
- if (state is CompletedExceptionally) throw state.exception
- return state as T
- }
- }
- }
- // Note: await is cancellable itself!
- return awaitGetValue()
+ return awaitSuspend() // slow-path
}
@Suppress("UNCHECKED_CAST")
- private suspend fun awaitGetValue(): T = suspendCancellableCoroutine { cont ->
+ private suspend fun awaitSuspend(): T = suspendCancellableCoroutine { cont ->
cont.unregisterOnCompletion(onCompletion {
val state = getState()
- check(state !is Active)
+ check(state !is Incomplete)
if (state is CompletedExceptionally)
cont.resumeWithException(state.exception)
else
@@ -127,12 +153,24 @@
@Suppress("UNCHECKED_CAST")
override fun getCompleted(): T {
val state = getState()
- check(state !is Active) { "This deferred value is still active" }
+ check(state !is Incomplete) { "This deferred value has not completed yet" }
if (state is CompletedExceptionally) throw state.exception
return state as T
}
// for nicer debugging
- override fun toString(): String = "${javaClass.simpleName}{" +
- (if (isActive) "isActive=true" else "completed=${getState()}") + "}"
+ override fun toString(): String {
+ val state = getState()
+ val result = if (state is Incomplete) "" else "[$state]"
+ return "${javaClass.simpleName}{${describeState(state)}}$result@${Integer.toHexString(System.identityHashCode(this))}"
+ }
+}
+
+private class LazyDeferredCoroutine<T>(
+ context: CoroutineContext,
+ val block: suspend CoroutineScope.() -> T
+) : DeferredCoroutine<T>(context, active = false) {
+ override fun onStart() {
+ block.startCoroutine(this, this)
+ }
}