blob: 4c0c5e37b66c00de5e7da75bdb6aa4ba5cfbd92a [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
15 * immediately resumes with [CancellationException] .
16 */
17 public suspend fun await(): T
18}
19
20/**
21 * Starts new coroutine and returns its result as an implementation of [Deferred].
22 * The running coroutine is cancelled when the resulting job is cancelled.
23 * The [context] for the new coroutine must be explicitly specified and must include [CoroutineDispatcher] element.
24 * The specified context is added to the context of the parent running coroutine (if any) inside which this function
25 * is invoked. The [Job] of the resulting coroutine is a child of the job of the parent coroutine (if any).
26 */
27public fun <T> defer(context: CoroutineContext, block: suspend () -> T) : Deferred<T> =
28 DeferredCoroutine<T>(newCoroutineContext(context)).also { block.startCoroutine(it) }
29
30private class DeferredCoroutine<T>(
31 parentContext: CoroutineContext
32) : JobContinuation<T>(parentContext), Deferred<T> {
33 @Suppress("UNCHECKED_CAST")
34 suspend override fun await(): T {
35 // quick check if already complete (avoid extra object creation)
36 val state = getState()
37 if (state !is Active) {
38 if (state is CompletedExceptionally) throw state.exception
39 return state as T
40 }
41 // Note: await is cancellable itself!
42 return awaitGetValue()
43 }
44
45 @Suppress("UNCHECKED_CAST")
46 private suspend fun awaitGetValue(): T = suspendCancellableCoroutine { cont ->
47 cont.unregisterOnCompletion(onCompletion {
48 val state = getState()
49 check(state !is Active)
50 if (state is CompletedExceptionally)
51 cont.resumeWithException(state.exception)
52 else
53 cont.resume(state as T)
54 })
55 }
56
57 override fun afterCompletion(state: Any?, closeException: Throwable?) {
58 if (closeException != null) handleCoroutineException(context, closeException)
59 }
60}