Start the process
One of the known ways to start coroutines is,So
What is the process? Go directly
launch
The source code starts to appear.
fun main() { coroutineTest() (2000L) } val block = suspend { println("Hello") delay(1000L) println("Kotlin") } private fun coroutineTest() { CoroutineScope(Job()).launch { withContext() { () } } }
Decompiled Java code
public final class CoroutineDemoKt { @NotNull private static final Function1 block; public static final void main() { coroutineTest(); (2000L); } // $FF: synthetic method public static void main(String[] var0) { main(); } @NotNull public static final Function1 getBlock() { return block; } private static final void coroutineTest() { $default(((CoroutineContext)$default((Job)null, 1, (Object)null)), (CoroutineContext)null, (CoroutineStart)null, (Function2)(new Function2((Continuation)null) { int label; @Nullable public final Object invokeSuspend(@NotNull Object $result) { Object var2 = IntrinsicsKt.getCOROUTINE_SUSPENDED(); switch() { case 0: ($result); CoroutineContext var10000 = (CoroutineContext)(); Function2 var10001 = (Function2)(new Function2((Continuation)null) { int label; @Nullable public final Object invokeSuspend(@NotNull Object $result) { Object var2 = IntrinsicsKt.getCOROUTINE_SUSPENDED(); switch() { case 0: ($result); Function1 var10000 = (); = 1; if ((this) == var2) { return var2; } break; case 1: ($result); break; default: throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine"); } return ; } @NotNull public final Continuation create(@Nullable Object value, @NotNull Continuation completion) { (completion, "completion"); Function2 var3 = new <anonymous constructor>(completion); return var3; } public final Object invoke(Object var1, Object var2) { return ((<undefinedtype>)(var1, (Continuation)var2)).invokeSuspend(); } }); = 1; if ((var10000, var10001, this) == var2) { return var2; } break; case 1: ($result); break; default: throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine"); } return ; } @NotNull public final Continuation create(@Nullable Object value, @NotNull Continuation completion) { (completion, "completion"); Function2 var3 = new <anonymous constructor>(completion); return var3; } public final Object invoke(Object var1, Object var2) { return ((<undefinedtype>)(var1, (Continuation)var2)).invokeSuspend(); } }), 3, (Object)null); } static { Function1 var0 = (Function1)(new Function1((Continuation)null) { int label; @Nullable public final Object invokeSuspend(@NotNull Object $result) { Object var3 = IntrinsicsKt.getCOROUTINE_SUSPENDED(); String var2; switch() { case 0: ($result); var2 = "Hello"; (var2); = 1; if ((1000L, this) == var3) { return var3; } break; case 1: ($result); break; default: throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine"); } var2 = "Kotlin"; (var2); return ; } @NotNull public final Continuation create(@NotNull Continuation completion) { (completion, "completion"); Function1 var2 = new <anonymous constructor>(completion); return var2; } public final Object invoke(Object var1) { return ((<undefinedtype>)((Continuation)var1)).invokeSuspend(); } }); block = var0; } }
Let’s analyze the process of the above code first:
- First, a block variable of Function1 type is declared. This variable is the block in the demo, and then it will be assigned in the static function.
- Next is the call to the coroutineTest function. The first line of code in this function is the parameter passing of CoroutineScope and some default values
- Then, through the invoke line 89, enter the process of flowing the outer state machine
- The static line 95 indicates that the internal suspend function is in the demo. It is implemented in an anonymous internal class, and then the internal state machine flow process is executed, and finally the block is assigned a value.
- After the block is assigned, it is finally
Function1 var10000 = ();
Being called
So how does this process happen?launch
View the source code:
public fun ( context: CoroutineContext = EmptyCoroutineContext, start: CoroutineStart = , block: suspend CoroutineScope.() -> Unit ): Job { val newContext = newCoroutineContext(context) val coroutine = if () LazyStandaloneCoroutine(newContext, block) else StandaloneCoroutine(newContext, active = true) (start, coroutine, block) return coroutine }
Hereblock
It refers to the block code segment in the demo
Let’s take a look at the meaning of the few lines of code inside:
- newCoroutineContext:Create a new Context through the default or passed context;
- coroutine:launch will create corresponding coroutine objects based on the incoming startup mode. There are two types here, one is standard and the other is lazy loading.
- :Try to start coroutine
2. How is coroutine started
passlaunch
The source code shows that the coroutine starts throughStarted, so what is the startup process of the coroutine?
public abstract class AbstractCoroutine<in T>( parentContext: CoroutineContext, initParentJob: Boolean, active: Boolean ) : JobSupport(active), Job, Continuation<T>, CoroutineScope { ... /** * Start this coroutine with the given code block and start the policy. This function is called at most once on this coroutine. */ public fun <R> start(start: CoroutineStart, receiver: R, block: suspend R.() -> T) { start(block, receiver, this) } }
start
Three parameters are passed into the function, and you only need to pay attention to the first parameter.
public enum class CoroutineStart { ... /** * Use this coroutine startup strategy to start the corresponding block as coroutine. */ public operator fun <T> invoke(block: suspend () -> T, completion: Continuation<T>): Unit = when (this) { DEFAULT -> (completion) ATOMIC -> (completion) UNDISPATCHED -> (completion) LAZY -> Unit // will start lazily } }
There are three specific ways to implement the startup strategy, here only needs to be analyzed.startCoroutine
, the other two are actually added to it. The former means that the coroutine can be cancelled when waiting for scheduling after starting the coroutine, and the latter means that the coroutine will not be distributed after starting it.
/** * Create a coroutine with no receiver and the result type T. This function creates a new pending instance every time it is called. */ public fun <T> (suspend () -> T).startCoroutine( completion: Continuation<T> ) { createCoroutineUnintercepted(completion).intercepted().resume(Unit) }
createCoroutineUnintercepted
It's just a declaration in the source code, and its specific implementation is in the file.
//#createCoroutineUnintercepted /** * Create a non-interceptor coroutine with no receiver and result type T. This function creates a new pending instance every time it is called. */ public actual fun <T> (suspend () -> T).createCoroutineUnintercepted( completion: Continuation<T> ): Continuation<Unit> { val probeCompletion = probeCoroutineCreated(completion) return if (this is BaseContinuationImpl) create(probeCompletion) else createCoroutineFromSuspendFunction(probeCompletion) { (this as Function1<Continuation<T>, Any?>).invoke(it) } }
actual
RepresentscreateCoroutineUnintercepted()
Implementation on the JVM platform.
createCoroutineUnintercepted
is an extension function, the receiver type is a suspended function or Lambda with no argument and return value T.
In line 9 codethis
It represents(suspend () -> T)
That isinvoke
in the functionblock
Variable, thisblock
The variable is in the demoblock
Code segment.
Line 9BaseContinuationImpl
It is an abstract class that implementsContinuation
。
aboutif (this is BaseContinuationImpl)
The results will not be analyzed for the time being. First, the create function in two cases is analyzed:
- create(probeCompletion):
//#create public open fun create(completion: Continuation<*>): Continuation<Unit> { throw UnsupportedOperationException("create(Continuation) has not been overridden") } public open fun create(value: Any?, completion: Continuation<*>): Continuation<Unit> { throw UnsupportedOperationException("create(Any?;Continuation) has not been overridden") }
thiscreate
The function throws an exception, which means thiscreate()
Not rewritten, and this onecreate()
The rewrite is in the decompiled Java codecreate
function
@NotNull public final Continuation create(@Nullable Object value, @NotNull Continuation completion) { (completion, "completion"); Function2 var3 = new <anonymous constructor>(completion); return var3; }
- createCoroutineFromSuspendFunction(probeCompletion):
//#createCoroutineFromSuspendFunction /** * When a suspend-modified lambda expression does not inherit the BaseContinuationImpl class, a coroutine is created through this method. * * It happens in two situations: * Other suspended methods are called in the expression * 2. The suspend method is implemented through Java * * It must be encapsulated into an instance that extends [BaseContinuationImpl], because this is the expectation of all coroutine mechanisms. */ private inline fun <T> createCoroutineFromSuspendFunction( completion: Continuation<T>, crossinline block: (Continuation<T>) -> Any? ): Continuation<Unit> { val context = // Create a restricted coroutine for context for null return if (context === EmptyCoroutineContext) //Restricted coroutines: Only the suspend method provided in the coroutine scope can be called to suspend, and other suspend methods cannot be called object : RestrictedContinuationImpl(completion as Continuation<Any?>) { private var label = 0 override fun invokeSuspend(result: Result<Any?>): Any? = when (label) { 0 -> { label = 1 () // If you try to start with an exception, the exception is re-thrown (will be caught) block(this) // Run the block, can return or hang } 1 -> { label = 2 () // This is the result of block hang } else -> error("This coroutine had already completed") } } else //Create a normal coroutine object : ContinuationImpl(completion as Continuation<Any?>, context) { private var label = 0 override fun invokeSuspend(result: Result<Any?>): Any? = when (label) { 0 -> { label = 1 () // If you try to start with an exception, the exception is re-thrown (will be caught) block(this) // Run the block, can return or hang } 1 -> { label = 2 () // This is the result of block hang } else -> error("This coroutine had already completed") } } }
createCoroutineFromSuspendFunction is to create a different type of coroutine when a Lambda expression modified by suspend does not inherit BaseContinuationImpl, and then create a different type of coroutine based on whether the context is empty.
Both cases have been analyzed, so nowif (this is BaseContinuationImpl)
Which one will be executed? First of all,this
What it refers to is the block code segment in the demo. After the Kotlin compiler compiles, it will automatically generate a class, which is the abovestatic
, it will inherit the SuspendLambda class, and this SuspendLambda class inherits from ContinuationImpl, and ContinuationImpl inherits from BaseContinuationImpl, so you can get the judgment resulttrue,
createCoroutineUnintercepted
The process is the process of coroutine creation.
Then there is the intercepted function, the specific implementation of this function is also in progress, sointercepted
What did you do again
public expect fun <T> Continuation<T>.intercepted(): Continuation<T> //Detailed implementation//#intercepted public actual fun <T> Continuation<T>.intercepted(): Continuation<T> = (this as? ContinuationImpl)?.intercepted() ?: this
First of all, there is a strong turn. Through the above analysis, this strong turn will definitely be successful. Hereintercepted
Just enteredContinuationImpl
Fame
internal abstract class ContinuationImpl( completion: Continuation<Any?>?, private val _context: CoroutineContext? ) : BaseContinuationImpl(completion) { ... @Transient private var intercepted: Continuation<Any?>? = null //If there is no cache, get the interceptor from the context and call interceptContinuation to intercept //Save the obtained content to a global variable public fun intercepted(): Continuation<Any?> = intercepted ?: (context[ContinuationInterceptor]?.interceptContinuation(this) ?: this) .also { intercepted = it } }
HereContinuationInterceptor
It refers to the transmission in the demo, when default value
。
Go back againstartContinue
The last one leftresume
:
/** * Resumes the corresponding coroutine pass value as the return value of the last pending point. */ public inline fun <T> Continuation<T>.resume(value: T): Unit = resumeWith((value)) public interface Continuation<in T> { /** * The context of the coroutine corresponding to this continuation. */ public val context: CoroutineContext /** * Resumes the corresponding coroutine pass value as the return value of the last pending point. */ public fun resumeWith(result: Result<T>) }
Hereresume(Unit)
The effect is equivalent to starting a coroutine.
In the above startup process, for the convenience of analysis,, and the default is
, let's analyze it below
DEFAULT
The process
//#startCoroutineCancellable public fun <T> (suspend () -> T).startCoroutineCancellable(completion: Continuation<T>): Unit = runSafely(completion) { createCoroutineUnintercepted(completion).intercepted().resumeCancellableWith((Unit)) }
startCoroutineCancellable
For coroutine creation and interceptionATOMIC
It's the same, the difference isresumeCancellableWith
//DispatchedContinuation#resumeCancellableWith public fun <T> Continuation<T>.resumeCancellableWith( result: Result<T>, onCancellation: ((cause: Throwable) -> Unit)? = null ): Unit = when (this) { is DispatchedContinuation -> resumeCancellableWith(result, onCancellation) else -> resumeWith(result) } // We inline it to save an entry on the stack, in the case it displays (unlimited scheduler)// It is only used in Continuation<T>.resumeCancellableWith@Suppress("NOTHING_TO_INLINE") inline fun resumeCancellableWith( result: Result<T>, noinline onCancellation: ((cause: Throwable) -> Unit)? ) { val state = (onCancellation) //Does it need to be distributed if ((context)) { _state = state resumeMode = MODE_CANCELLABLE //Distribute the execution of the runnable block to another thread in the given context (context, this) } else { executeUnconfined(state, MODE_CANCELLABLE) { //The coroutine has not been cancelled if (!resumeCancelled(state)) { // Resuming execution resumeUndispatchedWith(result) } } } } //Before resume execution, determine whether the coroutine has been cancelled.inline fun resumeCancelled(state: Any?): Boolean { //Get the current coroutine task val job = context[Job] //If it is not empty and is not active if (job != null && !) { val cause = () cancelCompletedResult(state, cause) //throw an exception resumeWithException(cause) return true } return false } //We need to inline it to save an entry in the stackinline fun resumeUndispatchedWith(result: Result<T>) { withContinuationContext(continuation, countOrElement) { (result) } }
The above is the detailed explanation of the principles of the Kotlin coroutine launch process. For more information about the Kotlin coroutine launch process, please pay attention to my other related articles!