blob: d1727aa560c8f4194666ccc8bd9a0a6f4f312055 [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].
30 * The running coroutine is cancelled when the resulting job is cancelled.
31 * The [context] for the new coroutine must be explicitly specified and must include [CoroutineDispatcher] element.
Roman Elizaroved7b8642017-01-19 11:22:28 +030032 * See [CoroutineDispatcher] for the standard [context] implementations that are provided by `kotlinx.coroutines`.
Roman Elizarov3754f952017-01-18 20:47:54 +030033 * The specified context is added to the context of the parent running coroutine (if any) inside which this function
34 * is invoked. The [Job] of the resulting coroutine is a child of the job of the parent coroutine (if any).
35 */
Roman Elizarovd528e3e2017-01-23 15:40:05 +030036public fun <T> defer(context: CoroutineContext, block: suspend CoroutineScope.() -> T) : Deferred<T> =
37 DeferredCoroutine<T>(newCoroutineContext(context)).also { block.startCoroutine(it, it) }
Roman Elizarov3754f952017-01-18 20:47:54 +030038
39private class DeferredCoroutine<T>(
Roman Elizarovd528e3e2017-01-23 15:40:05 +030040 newContext: CoroutineContext
41) : AbstractCoroutine<T>(newContext), Deferred<T> {
42 init { initParentJob(newContext[Job]) }
Roman Elizarov58a7add2017-01-20 12:19:52 +030043
Roman Elizarov3754f952017-01-18 20:47:54 +030044 @Suppress("UNCHECKED_CAST")
45 suspend override fun await(): T {
46 // quick check if already complete (avoid extra object creation)
47 val state = getState()
48 if (state !is Active) {
49 if (state is CompletedExceptionally) throw state.exception
50 return state as T
51 }
52 // Note: await is cancellable itself!
53 return awaitGetValue()
54 }
55
56 @Suppress("UNCHECKED_CAST")
57 private suspend fun awaitGetValue(): T = suspendCancellableCoroutine { cont ->
58 cont.unregisterOnCompletion(onCompletion {
59 val state = getState()
60 check(state !is Active)
61 if (state is CompletedExceptionally)
62 cont.resumeWithException(state.exception)
63 else
64 cont.resume(state as T)
65 })
66 }
67
Roman Elizarovc5814542017-01-19 10:19:06 +030068 @Suppress("UNCHECKED_CAST")
69 override fun getCompleted(): T {
70 val state = getState()
71 check(state !is Active) { "This deferred value is still active" }
72 if (state is CompletedExceptionally) throw state.exception
73 return state as T
Roman Elizarov3754f952017-01-18 20:47:54 +030074 }
75}