@@ -77,31 +77,52 @@ public fun launch(context: CoroutineContext, start: Boolean, block: suspend Coro
7777 * This function immediately applies dispatcher from the new context, shifting execution of the block into the
7878 * different thread inside the block, and back when it completes.
7979 * 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].
8088 */
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)
104108 }
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)
105126
106127/* *
107128 * Runs new coroutine and **blocks** current thread _interruptibly_ until its completion.
@@ -157,8 +178,9 @@ private class RunContinuationDirect<in T>(
157178
158179private class RunContinuationCoroutine <in T >(
159180 override val parentContext : CoroutineContext ,
181+ resumeMode : Int ,
160182 continuation : Continuation <T >
161- ) : CancellableContinuationImpl<T>(continuation, defaultResumeMode = MODE_CANCELLABLE , active = true )
183+ ) : CancellableContinuationImpl<T>(continuation, defaultResumeMode = resumeMode , active = true )
162184
163185private class BlockingCoroutine <T >(
164186 override val parentContext : CoroutineContext ,
0 commit comments