blob: 6b5b4485d5ab2b78065277bd2024510a95444830 [file] [log] [blame]
Roman Elizarov3754f952017-01-18 20:47:54 +03001package kotlinx.coroutines.experimental
2
Roman Elizarovea4a51b2017-01-31 12:01:25 +03003import kotlin.coroutines.experimental.CoroutineContext
4import kotlin.coroutines.experimental.startCoroutine
Roman Elizarov3754f952017-01-18 20:47:54 +03005
6/**
7 * Deferred value is conceptually a non-blocking cancellable future.
8 * It is created with [defer] coroutine builder.
Roman Elizarov41c5c8b2017-01-25 13:37:15 +03009 * It is in [active][isActive] state while the value is being computed.
Roman Elizarov3754f952017-01-18 20:47:54 +030010 */
11public interface Deferred<out T> : Job {
12 /**
13 * Awaits for completion of this value without blocking a thread and resumes when deferred computation is complete.
14 * This suspending function is cancellable.
15 * If the [Job] of the current coroutine is completed while this suspending function is waiting, this function
Roman Elizarovc5814542017-01-19 10:19:06 +030016 * immediately resumes with [CancellationException].
Roman Elizarov3754f952017-01-18 20:47:54 +030017 */
18 public suspend fun await(): T
Roman Elizarovc5814542017-01-19 10:19:06 +030019
20 /**
21 * Returns *completed* result or throws [IllegalStateException] if this deferred value is still [isActive].
22 * It throws the corresponding exception if this deferred has completed exceptionally.
23 * This function is designed to be used from [onCompletion] handlers, when there is an absolute certainty that
24 * the value is already complete.
25 */
26 public fun getCompleted(): T
Roman Elizarov3754f952017-01-18 20:47:54 +030027}
28
29/**
30 * Starts new coroutine and returns its result as an implementation of [Deferred].
Roman Elizarov44ba4b12017-01-25 11:37:54 +030031 * The running coroutine is cancelled when the resulting object is [cancelled][Job.cancel].
32 *
33 * The [context] for the new coroutine must be explicitly specified.
Roman Elizaroved7b8642017-01-19 11:22:28 +030034 * See [CoroutineDispatcher] for the standard [context] implementations that are provided by `kotlinx.coroutines`.
Roman Elizarov44ba4b12017-01-25 11:37:54 +030035 * The [context][CoroutineScope.context] of the parent coroutine from its [scope][CoroutineScope] may be used,
36 * 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 +030037 */
Roman Elizarovd528e3e2017-01-23 15:40:05 +030038public fun <T> defer(context: CoroutineContext, block: suspend CoroutineScope.() -> T) : Deferred<T> =
Roman Elizarov41c5c8b2017-01-25 13:37:15 +030039 DeferredCoroutine<T>(newCoroutineContext(context)).apply {
40 initParentJob(context[Job])
41 block.startCoroutine(this, this)
42 }
Roman Elizarov3754f952017-01-18 20:47:54 +030043
Roman Elizarov41c5c8b2017-01-25 13:37:15 +030044internal open class DeferredCoroutine<T>(
45 context: CoroutineContext
46) : AbstractCoroutine<T>(context), Deferred<T> {
47 protected open fun start(): Boolean = false // LazyDeferredCoroutine overrides
Roman Elizarov58a7add2017-01-20 12:19:52 +030048
Roman Elizarov3754f952017-01-18 20:47:54 +030049 @Suppress("UNCHECKED_CAST")
50 suspend override fun await(): T {
51 // quick check if already complete (avoid extra object creation)
Roman Elizarov41c5c8b2017-01-25 13:37:15 +030052 getState().let { state ->
53 if (state !is Active) {
54 if (state is CompletedExceptionally) throw state.exception
55 return state as T
56 }
57 }
58 if (start()) { // LazyDeferredCoroutine overrides
59 // recheck state (may have started & already completed
60 getState().let { state ->
61 if (state !is Active) {
62 if (state is CompletedExceptionally) throw state.exception
63 return state as T
64 }
65 }
Roman Elizarov3754f952017-01-18 20:47:54 +030066 }
67 // Note: await is cancellable itself!
68 return awaitGetValue()
69 }
70
71 @Suppress("UNCHECKED_CAST")
72 private suspend fun awaitGetValue(): T = suspendCancellableCoroutine { cont ->
73 cont.unregisterOnCompletion(onCompletion {
74 val state = getState()
75 check(state !is Active)
76 if (state is CompletedExceptionally)
77 cont.resumeWithException(state.exception)
78 else
79 cont.resume(state as T)
80 })
81 }
82
Roman Elizarovc5814542017-01-19 10:19:06 +030083 @Suppress("UNCHECKED_CAST")
84 override fun getCompleted(): T {
85 val state = getState()
86 check(state !is Active) { "This deferred value is still active" }
87 if (state is CompletedExceptionally) throw state.exception
88 return state as T
Roman Elizarov3754f952017-01-18 20:47:54 +030089 }
90}