blob: 70a5335728e3c980977e9bc2df85b5d20e357e76 [file] [log] [blame]
Roman Elizarov3754f952017-01-18 20:47:54 +03001package kotlinx.coroutines.experimental
2
3import kotlin.coroutines.CoroutineContext
4import kotlin.coroutines.startCoroutine
5
6/**
7 * Deferred value is conceptually a non-blocking cancellable future.
8 * It is created with [defer] coroutine builder.
9 */
10public interface Deferred<out T> : Job {
11 /**
12 * Awaits for completion of this value without blocking a thread and resumes when deferred computation is complete.
13 * This suspending function is cancellable.
14 * If the [Job] of the current coroutine is completed while this suspending function is waiting, this function
Roman Elizarovc5814542017-01-19 10:19:06 +030015 * immediately resumes with [CancellationException].
Roman Elizarov3754f952017-01-18 20:47:54 +030016 */
17 public suspend fun await(): T
Roman Elizarovc5814542017-01-19 10:19:06 +030018
19 /**
20 * Returns *completed* result or throws [IllegalStateException] if this deferred value is still [isActive].
21 * It throws the corresponding exception if this deferred has completed exceptionally.
22 * This function is designed to be used from [onCompletion] handlers, when there is an absolute certainty that
23 * the value is already complete.
24 */
25 public fun getCompleted(): T
Roman Elizarov3754f952017-01-18 20:47:54 +030026}
27
28/**
29 * Starts new coroutine and returns its result as an implementation of [Deferred].
Roman Elizarov44ba4b12017-01-25 11:37:54 +030030 * The running coroutine is cancelled when the resulting object is [cancelled][Job.cancel].
31 *
32 * The [context] for the new coroutine must be explicitly specified.
Roman Elizaroved7b8642017-01-19 11:22:28 +030033 * See [CoroutineDispatcher] for the standard [context] implementations that are provided by `kotlinx.coroutines`.
Roman Elizarov44ba4b12017-01-25 11:37:54 +030034 * The [context][CoroutineScope.context] of the parent coroutine from its [scope][CoroutineScope] may be used,
35 * in which case the [Job] of the resulting coroutine is a child of the job of the parent coroutine.
Roman Elizarov3754f952017-01-18 20:47:54 +030036 */
Roman Elizarovd528e3e2017-01-23 15:40:05 +030037public fun <T> defer(context: CoroutineContext, block: suspend CoroutineScope.() -> T) : Deferred<T> =
38 DeferredCoroutine<T>(newCoroutineContext(context)).also { block.startCoroutine(it, it) }
Roman Elizarov3754f952017-01-18 20:47:54 +030039
40private class DeferredCoroutine<T>(
Roman Elizarovd528e3e2017-01-23 15:40:05 +030041 newContext: CoroutineContext
42) : AbstractCoroutine<T>(newContext), Deferred<T> {
43 init { initParentJob(newContext[Job]) }
Roman Elizarov58a7add2017-01-20 12:19:52 +030044
Roman Elizarov3754f952017-01-18 20:47:54 +030045 @Suppress("UNCHECKED_CAST")
46 suspend override fun await(): T {
47 // quick check if already complete (avoid extra object creation)
48 val state = getState()
49 if (state !is Active) {
50 if (state is CompletedExceptionally) throw state.exception
51 return state as T
52 }
53 // Note: await is cancellable itself!
54 return awaitGetValue()
55 }
56
57 @Suppress("UNCHECKED_CAST")
58 private suspend fun awaitGetValue(): T = suspendCancellableCoroutine { cont ->
59 cont.unregisterOnCompletion(onCompletion {
60 val state = getState()
61 check(state !is Active)
62 if (state is CompletedExceptionally)
63 cont.resumeWithException(state.exception)
64 else
65 cont.resume(state as T)
66 })
67 }
68
Roman Elizarovc5814542017-01-19 10:19:06 +030069 @Suppress("UNCHECKED_CAST")
70 override fun getCompleted(): T {
71 val state = getState()
72 check(state !is Active) { "This deferred value is still active" }
73 if (state is CompletedExceptionally) throw state.exception
74 return state as T
Roman Elizarov3754f952017-01-18 20:47:54 +030075 }
76}