@@ -77,31 +77,52 @@ public fun launch(context: CoroutineContext, start: Boolean, block: suspend Coro
77
77
* This function immediately applies dispatcher from the new context, shifting execution of the block into the
78
78
* different thread inside the block, and back when it completes.
79
79
* The specified [context] is added onto the current coroutine context for the execution of the block.
80
+ *
81
+ * An optional `start` parameter is used only if the specified `context` uses a different [CoroutineDispatcher] than
82
+ * a current one, otherwise it is ignored.
83
+ * By default, the coroutine is immediately scheduled for execution and can be cancelled
84
+ * while it is waiting to be executed and it can be cancelled while the result is scheduled
85
+ * to be be processed by the invoker context.
86
+ * Other options can be specified via `start` parameter. See [CoroutineStart] for details.
87
+ * A value of [CoroutineStart.LAZY] is not supported and produces [IllegalArgumentException].
80
88
*/
81
- public suspend fun <T > run (context : CoroutineContext , block : suspend () -> T ): T =
82
- suspendCoroutineOrReturn sc@ { cont ->
83
- val oldContext = cont.context
84
- // fast path #1 if there is no change in the actual context:
85
- if (context == = oldContext || context is CoroutineContext .Element && oldContext[context.key] == = context)
86
- return @sc block.startCoroutineUninterceptedOrReturn(cont)
87
- // compute new context
88
- val newContext = oldContext + context
89
- // fast path #2 if the result is actually the same
90
- if (newContext == = oldContext)
91
- return @sc block.startCoroutineUninterceptedOrReturn(cont)
92
- // fast path #3 if the new dispatcher is the same as the old one.
93
- // `equals` is used by design (see equals implementation is wrapper context like ExecutorCoroutineDispatcher)
94
- if (newContext[ContinuationInterceptor ] == oldContext[ContinuationInterceptor ]) {
95
- val newContinuation = RunContinuationDirect (newContext, cont)
96
- return @sc block.startCoroutineUninterceptedOrReturn(newContinuation)
97
- }
98
- // slowest path otherwise -- use new interceptor, sync to its result via a
99
- // full-blown instance of CancellableContinuation
100
- val newContinuation = RunContinuationCoroutine (newContext, cont)
101
- newContinuation.initCancellability()
102
- block.startCoroutine(newContinuation)
103
- newContinuation.getResult()
89
+ public suspend fun <T > run (
90
+ context : CoroutineContext ,
91
+ start : CoroutineStart = CoroutineStart .DEFAULT ,
92
+ block : suspend () -> T
93
+ ): T = suspendCoroutineOrReturn sc@ { cont ->
94
+ val oldContext = cont.context
95
+ // fast path #1 if there is no change in the actual context:
96
+ if (context == = oldContext || context is CoroutineContext .Element && oldContext[context.key] == = context)
97
+ return @sc block.startCoroutineUninterceptedOrReturn(cont)
98
+ // compute new context
99
+ val newContext = oldContext + context
100
+ // fast path #2 if the result is actually the same
101
+ if (newContext == = oldContext)
102
+ return @sc block.startCoroutineUninterceptedOrReturn(cont)
103
+ // fast path #3 if the new dispatcher is the same as the old one.
104
+ // `equals` is used by design (see equals implementation is wrapper context like ExecutorCoroutineDispatcher)
105
+ if (newContext[ContinuationInterceptor ] == oldContext[ContinuationInterceptor ]) {
106
+ val newContinuation = RunContinuationDirect (newContext, cont)
107
+ return @sc block.startCoroutineUninterceptedOrReturn(newContinuation)
104
108
}
109
+ // slowest path otherwise -- use new interceptor, sync to its result via a
110
+ // full-blown instance of CancellableContinuation
111
+ require(! start.isLazy) { " $start start is not supported" }
112
+ val newContinuation = RunContinuationCoroutine (
113
+ parentContext = newContext,
114
+ resumeMode = if (start == CoroutineStart .ATOMIC ) MODE_ATOMIC_DEFAULT else MODE_CANCELLABLE ,
115
+ continuation = cont)
116
+ newContinuation.initCancellability() // attach to parent job
117
+ start(block, newContinuation)
118
+ newContinuation.getResult()
119
+ }
120
+
121
+ /* * @suppress **Deprecated** */
122
+ @Suppress(" DeprecatedCallableAddReplaceWith" ) // todo: the warning is incorrectly shown, see KT-17917
123
+ @Deprecated(message = " It is here for binary compatibility only" , level= DeprecationLevel .HIDDEN )
124
+ public suspend fun <T > run (context : CoroutineContext , block : suspend () -> T ): T =
125
+ run (context, start = CoroutineStart .ATOMIC , block = block)
105
126
106
127
/* *
107
128
* Runs new coroutine and **blocks** current thread _interruptibly_ until its completion.
@@ -157,8 +178,9 @@ private class RunContinuationDirect<in T>(
157
178
158
179
private class RunContinuationCoroutine <in T >(
159
180
override val parentContext : CoroutineContext ,
181
+ resumeMode : Int ,
160
182
continuation : Continuation <T >
161
- ) : CancellableContinuationImpl<T>(continuation, defaultResumeMode = MODE_CANCELLABLE , active = true )
183
+ ) : CancellableContinuationImpl<T>(continuation, defaultResumeMode = resumeMode , active = true )
162
184
163
185
private class BlockingCoroutine <T >(
164
186
override val parentContext : CoroutineContext ,
0 commit comments