blob: 020c44908ae3a660822fe9d3fb1fb6b5ef4104f4 [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 */
36public fun <T> defer(context: CoroutineContext, block: suspend () -> T) : Deferred<T> =
37 DeferredCoroutine<T>(newCoroutineContext(context)).also { block.startCoroutine(it) }
38
39private class DeferredCoroutine<T>(
40 parentContext: CoroutineContext
41) : JobContinuation<T>(parentContext), Deferred<T> {
42 @Suppress("UNCHECKED_CAST")
43 suspend override fun await(): T {
44 // quick check if already complete (avoid extra object creation)
45 val state = getState()
46 if (state !is Active) {
47 if (state is CompletedExceptionally) throw state.exception
48 return state as T
49 }
50 // Note: await is cancellable itself!
51 return awaitGetValue()
52 }
53
54 @Suppress("UNCHECKED_CAST")
55 private suspend fun awaitGetValue(): T = suspendCancellableCoroutine { cont ->
56 cont.unregisterOnCompletion(onCompletion {
57 val state = getState()
58 check(state !is Active)
59 if (state is CompletedExceptionally)
60 cont.resumeWithException(state.exception)
61 else
62 cont.resume(state as T)
63 })
64 }
65
Roman Elizarovc5814542017-01-19 10:19:06 +030066 @Suppress("UNCHECKED_CAST")
67 override fun getCompleted(): T {
68 val state = getState()
69 check(state !is Active) { "This deferred value is still active" }
70 if (state is CompletedExceptionally) throw state.exception
71 return state as T
Roman Elizarov3754f952017-01-18 20:47:54 +030072 }
73}