blob: 9520bbc31b4980e4371e131af04ee776a273a32f [file] [log] [blame]
Roman Elizarova7db8ec2017-12-21 22:45:12 +03001/*
2 * Copyright 2016-2017 JetBrains s.r.o.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Roman Elizarovaa461cf2018-04-11 13:20:29 +030017@file:JvmMultifileClass
18@file:JvmName("BuildersKt")
Roman Elizarovf161c9f2018-02-07 23:27:10 +030019
Roman Elizarove1c0b652017-12-01 14:02:57 +030020package kotlinx.coroutines.experimental
21
Roman Elizarovaa461cf2018-04-11 13:20:29 +030022import kotlinx.coroutines.experimental.internalAnnotations.*
Roman Elizarov23fb7282018-01-24 23:09:42 +030023import kotlinx.coroutines.experimental.intrinsics.*
Roman Elizarov9d5abcd2017-12-21 16:54:30 +030024import kotlin.coroutines.experimental.*
Roman Elizarov23fb7282018-01-24 23:09:42 +030025import kotlin.coroutines.experimental.intrinsics.*
Roman Elizarove1c0b652017-12-01 14:02:57 +030026
Roman Elizarovaa461cf2018-04-11 13:20:29 +030027// --------------- basic coroutine builders ---------------
28
Roman Elizarove1c0b652017-12-01 14:02:57 +030029/**
30 * Launches new coroutine without blocking current thread and returns a reference to the coroutine as a [Job].
31 * The coroutine is cancelled when the resulting job is [cancelled][Job.cancel].
32 *
33 * The [context] for the new coroutine can be explicitly specified.
34 * See [CoroutineDispatcher] for the standard context implementations that are provided by `kotlinx.coroutines`.
Roman Elizarov9fe5f462018-02-21 19:05:52 +030035 * The [coroutineContext](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.coroutines.experimental/coroutine-context.html)
36 * of the parent coroutine may be used,
Roman Elizarove1c0b652017-12-01 14:02:57 +030037 * in which case the [Job] of the resulting coroutine is a child of the job of the parent coroutine.
38 * The parent job may be also explicitly specified using [parent] parameter.
39 *
40 * If the context does not have any dispatcher nor any other [ContinuationInterceptor], then [DefaultDispatcher] is used.
41 *
42 * By default, the coroutine is immediately scheduled for execution.
43 * Other options can be specified via `start` parameter. See [CoroutineStart] for details.
44 * An optional [start] parameter can be set to [CoroutineStart.LAZY] to start coroutine _lazily_. In this case,
45 * the coroutine [Job] is created in _new_ state. It can be explicitly started with [start][Job.start] function
46 * and will be started implicitly on the first invocation of [join][Job.join].
47 *
48 * Uncaught exceptions in this coroutine cancel parent job in the context by default
49 * (unless [CoroutineExceptionHandler] is explicitly specified), which means that when `launch` is used with
50 * the context of another coroutine, then any uncaught exception leads to the cancellation of parent coroutine.
51 *
52 * See [newCoroutineContext] for a description of debugging facilities that are available for newly created coroutine.
53 *
54 * @param context context of the coroutine. The default value is [DefaultDispatcher].
55 * @param start coroutine start option. The default value is [CoroutineStart.DEFAULT].
56 * @param parent explicitly specifies the parent job, overrides job from the [context] (if any).
Roman Elizarovfe8ba6b2018-03-13 17:34:29 +030057 * @param onCompletion optional completion handler for the coroutine (see [Job.invokeOnCompletion]).
Roman Elizarove1c0b652017-12-01 14:02:57 +030058 * @param block the coroutine code.
59 */
Roman Elizarovaa461cf2018-04-11 13:20:29 +030060public fun launch(
Roman Elizarovfe8ba6b2018-03-13 17:34:29 +030061 context: CoroutineContext = DefaultDispatcher,
62 start: CoroutineStart = CoroutineStart.DEFAULT,
63 parent: Job? = null,
64 onCompletion: CompletionHandler? = null,
65 block: suspend CoroutineScope.() -> Unit
Roman Elizarove1c0b652017-12-01 14:02:57 +030066): Job {
67 val newContext = newCoroutineContext(context, parent)
68 val coroutine = if (start.isLazy)
69 LazyStandaloneCoroutine(newContext, block) else
70 StandaloneCoroutine(newContext, active = true)
Roman Elizarovfe8ba6b2018-03-13 17:34:29 +030071 if (onCompletion != null) coroutine.invokeOnCompletion(handler = onCompletion)
Roman Elizarovc12123e2018-01-24 22:07:12 +030072 coroutine.start(start, coroutine, block)
Roman Elizarove1c0b652017-12-01 14:02:57 +030073 return coroutine
74}
Roman Elizarovaa461cf2018-04-11 13:20:29 +030075/** @suppress **Deprecated**: Binary compatibility */
76@Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN)
77public fun launch(
78 context: CoroutineContext = DefaultDispatcher,
79 start: CoroutineStart = CoroutineStart.DEFAULT,
80 parent: Job? = null,
81 block: suspend CoroutineScope.() -> Unit
82): Job = launch(context, start, parent, block = block)
83
84/** @suppress **Deprecated**: Binary compatibility */
85@Deprecated(message = "Binary compatibility", level = DeprecationLevel.HIDDEN)
86public fun launch(
87 context: CoroutineContext = DefaultDispatcher,
88 start: CoroutineStart = CoroutineStart.DEFAULT,
89 block: suspend CoroutineScope.() -> Unit
90): Job =
91 launch(context, start, block = block)
92
93/**
94 * @suppress **Deprecated**: Use `start = CoroutineStart.XXX` parameter
95 */
96@Deprecated(message = "Use `start = CoroutineStart.XXX` parameter",
97 replaceWith = ReplaceWith("launch(context, if (start) CoroutineStart.DEFAULT else CoroutineStart.LAZY, block)"))
98public fun launch(context: CoroutineContext, start: Boolean, block: suspend CoroutineScope.() -> Unit): Job =
99 launch(context, if (start) CoroutineStart.DEFAULT else CoroutineStart.LAZY, block = block)
Roman Elizarove1c0b652017-12-01 14:02:57 +0300100
Roman Elizarova12ee152017-12-20 10:50:17 +0300101/**
Roman Elizarov9d5abcd2017-12-21 16:54:30 +0300102 * Calls the specified suspending block with a given coroutine context, suspends until it completes, and returns
103 * the result.
104 *
105 * This function immediately applies dispatcher from the new context, shifting execution of the block into the
106 * different thread inside the block, and back when it completes.
107 * The specified [context] is added onto the current coroutine context for the execution of the block.
108 *
109 * An optional `start` parameter is used only if the specified `context` uses a different [CoroutineDispatcher] than
110 * a current one, otherwise it is ignored.
111 * By default, the coroutine is immediately scheduled for execution and can be cancelled
112 * while it is waiting to be executed and it can be cancelled while the result is scheduled
113 * to be processed by the invoker context.
114 * Other options can be specified via `start` parameter. See [CoroutineStart] for details.
115 * A value of [CoroutineStart.LAZY] is not supported and produces [IllegalArgumentException].
116 */
Roman Elizarovaa461cf2018-04-11 13:20:29 +0300117public suspend fun <T> withContext(
Roman Elizarov9d5abcd2017-12-21 16:54:30 +0300118 context: CoroutineContext,
119 start: CoroutineStart = CoroutineStart.DEFAULT,
120 block: suspend () -> T
121): T = suspendCoroutineOrReturn sc@ { cont ->
122 val oldContext = cont.context
123 // fast path #1 if there is no change in the actual context:
124 if (context === oldContext || context is CoroutineContext.Element && oldContext[context.key] === context)
125 return@sc block.startCoroutineUninterceptedOrReturn(cont)
126 // compute new context
127 val newContext = oldContext + context
128 // fast path #2 if the result is actually the same
129 if (newContext === oldContext)
130 return@sc block.startCoroutineUninterceptedOrReturn(cont)
131 // fast path #3 if the new dispatcher is the same as the old one.
132 // `equals` is used by design (see equals implementation is wrapper context like ExecutorCoroutineDispatcher)
133 if (newContext[ContinuationInterceptor] == oldContext[ContinuationInterceptor]) {
134 val newContinuation = RunContinuationDirect(newContext, cont)
135 return@sc block.startCoroutineUninterceptedOrReturn(newContinuation)
136 }
137 // slowest path otherwise -- use new interceptor, sync to its result via a full-blown instance of RunCompletion
138 require(!start.isLazy) { "$start start is not supported" }
139 val completion = RunCompletion(
140 context = newContext,
141 delegate = cont,
Roman Elizarovaa461cf2018-04-11 13:20:29 +0300142 resumeMode = if (start == CoroutineStart.ATOMIC) MODE_ATOMIC_DEFAULT else MODE_CANCELLABLE
143 )
Roman Elizarovc12123e2018-01-24 22:07:12 +0300144 completion.initParentJobInternal(newContext[Job]) // attach to job
Roman Elizarovaa461cf2018-04-11 13:20:29 +0300145 @Suppress("DEPRECATION")
Roman Elizarov9d5abcd2017-12-21 16:54:30 +0300146 start(block, completion)
147 completion.getResult()
148}
149
Roman Elizarovaa461cf2018-04-11 13:20:29 +0300150/** @suppress **Deprecated**: Renamed to [withContext]. */
151@Deprecated(message = "Renamed to `withContext`", level=DeprecationLevel.WARNING,
152 replaceWith = ReplaceWith("withContext(context, start, block)"))
153public suspend fun <T> run(
154 context: CoroutineContext,
155 start: CoroutineStart = CoroutineStart.DEFAULT,
156 block: suspend () -> T
157): T =
158 withContext(context, start, block)
159
160/** @suppress **Deprecated** */
161@Deprecated(message = "It is here for binary compatibility only", level=DeprecationLevel.HIDDEN)
162public suspend fun <T> run(context: CoroutineContext, block: suspend () -> T): T =
163 withContext(context, start = CoroutineStart.ATOMIC, block = block)
164
Roman Elizarove1c0b652017-12-01 14:02:57 +0300165// --------------- implementation ---------------
166
167private open class StandaloneCoroutine(
Roman Elizarovaa461cf2018-04-11 13:20:29 +0300168 private val parentContext: CoroutineContext,
169 active: Boolean
Roman Elizarove1c0b652017-12-01 14:02:57 +0300170) : AbstractCoroutine<Unit>(parentContext, active) {
Roman Elizarov0d7323a2018-01-29 20:59:15 +0300171 override fun hasOnFinishingHandler(update: Any?) = update is CompletedExceptionally
172 override fun onFinishingInternal(update: Any?) {
Roman Elizarove1c0b652017-12-01 14:02:57 +0300173 // note the use of the parent's job context below!
Vsevolod Tolstopyatovc1092d52018-04-12 20:22:25 +0300174 if (update is CompletedExceptionally) handleCoroutineException(parentContext, update.cause)
Roman Elizarove1c0b652017-12-01 14:02:57 +0300175 }
176}
177
178private class LazyStandaloneCoroutine(
Roman Elizarovaa461cf2018-04-11 13:20:29 +0300179 parentContext: CoroutineContext,
180 private val block: suspend CoroutineScope.() -> Unit
Roman Elizarove1c0b652017-12-01 14:02:57 +0300181) : StandaloneCoroutine(parentContext, active = false) {
182 override fun onStart() {
183 block.startCoroutineCancellable(this, this)
184 }
185}
186
Roman Elizarov9d5abcd2017-12-21 16:54:30 +0300187private class RunContinuationDirect<in T>(
188 override val context: CoroutineContext,
189 continuation: Continuation<T>
190) : Continuation<T> by continuation
191
Roman Elizarov9d5abcd2017-12-21 16:54:30 +0300192@Suppress("UNCHECKED_CAST")
193private class RunCompletion<in T>(
194 override val context: CoroutineContext,
195 delegate: Continuation<T>,
196 resumeMode: Int
Vsevolod Tolstopyatov4aa18aa2018-04-17 15:43:12 +0300197) : AbstractContinuation<T>(delegate, resumeMode) {
198
199 override val useCancellingState: Boolean get() = true
200}