SoFunction
Updated on 2025-04-04

Detailed explanation of the principle of Kotlin coroutine launch startup process

Start the process

One of the known ways to start coroutines is,SoWhat is the process? Go directlylaunchThe 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 finallyFunction1 var10000 = ();Being called

So how does this process happen?launchView 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
}

HereblockIt 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

passlaunchThe source code shows that the coroutine starts throughStarted, so what is the startup process of the coroutine?

public abstract class AbstractCoroutine&lt;in T&gt;(
    parentContext: CoroutineContext,
    initParentJob: Boolean,
    active: Boolean
) : JobSupport(active), Job, Continuation&lt;T&gt;, 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 &lt;R&gt; start(start: CoroutineStart, receiver: R, block: suspend R.() -&gt; T) {
        start(block, receiver, this)
    }
}

startThree 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 &lt;T&gt; invoke(block: suspend () -&gt; T, completion: Continuation&lt;T&gt;): Unit =
    when (this) {
        DEFAULT -&gt; (completion)
        ATOMIC -&gt; (completion)
        UNDISPATCHED -&gt; (completion)
        LAZY -&gt; 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 &lt;T&gt; (suspend () -&gt; T).startCoroutine(
    completion: Continuation&lt;T&gt;
) {
    createCoroutineUnintercepted(completion).intercepted().resume(Unit)
}

createCoroutineUninterceptedIt'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 &lt;T&gt; (suspend () -&gt; T).createCoroutineUnintercepted(
    completion: Continuation&lt;T&gt;
): Continuation&lt;Unit&gt; {
    val probeCompletion = probeCoroutineCreated(completion)
    return if (this is BaseContinuationImpl)
        create(probeCompletion)
    else
        createCoroutineFromSuspendFunction(probeCompletion) {
            (this as Function1&lt;Continuation&lt;T&gt;, Any?&gt;).invoke(it)
        }
}

actualRepresentscreateCoroutineUnintercepted() Implementation on the JVM platform.

createCoroutineUninterceptedis an extension function, the receiver type is a suspended function or Lambda with no argument and return value T.

In line 9 codethisIt represents(suspend () -> T)That isinvokein the functionblockVariable, thisblockThe variable is in the demoblockCode segment.

Line 9BaseContinuationImplIt 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")
}

thiscreateThe function throws an exception, which means thiscreate()Not rewritten, and this onecreate()The rewrite is in the decompiled Java codecreatefunction

@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 &lt;T&gt; createCoroutineFromSuspendFunction(
	completion: Continuation&lt;T&gt;,
	crossinline block: (Continuation&lt;T&gt;) -&gt; Any?
		): Continuation&lt;Unit&gt; {
	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&lt;Any?&gt;) {
		private var label = 0
		override fun invokeSuspend(result: Result&lt;Any?&gt;): Any? =
		when (label) {
			0 -&gt; {
				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 -&gt; {
				label = 2
				() // This is the result of block hang			}
			else -&gt; error("This coroutine had already completed")
		}
	}
	else
	//Create a normal coroutine	object : ContinuationImpl(completion as Continuation&lt;Any?&gt;, context) {
		private var label = 0
		override fun invokeSuspend(result: Result&lt;Any?&gt;): Any? =
		when (label) {
			0 -&gt; {
				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 -&gt; {
				label = 2
				() // This is the result of block hang			}
			else -&gt; 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,thisWhat 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,

createCoroutineUninterceptedThe process is the process of coroutine creation.

Then there is the intercepted function, the specific implementation of this function is also in progress, sointerceptedWhat did you do again

public expect fun &lt;T&gt; Continuation&lt;T&gt;.intercepted(): Continuation&lt;T&gt;
//Detailed implementation//#intercepted
public actual fun &lt;T&gt; Continuation&lt;T&gt;.intercepted(): Continuation&lt;T&gt; =
    (this as? ContinuationImpl)?.intercepted() ?: this

First of all, there is a strong turn. Through the above analysis, this strong turn will definitely be successful. HereinterceptedJust enteredContinuationImplFame

internal abstract class ContinuationImpl(
    completion: Continuation&lt;Any?&gt;?,
    private val _context: CoroutineContext?
) : BaseContinuationImpl(completion) {
	...
    @Transient
    private var intercepted: Continuation&lt;Any?&gt;? = 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&lt;Any?&gt; =
        intercepted
            ?: (context[ContinuationInterceptor]?.interceptContinuation(this) ?: this)
                .also { intercepted = it }
}

HereContinuationInterceptorIt refers to the transmission in the demo, when default value

Go back againstartContinueThe last one leftresume

/**
  * Resumes the corresponding coroutine pass value as the return value of the last pending point.
  */
public inline fun &lt;T&gt; Continuation&lt;T&gt;.resume(value: T): Unit =
	resumeWith((value))
public interface Continuation&lt;in T&gt; {
	/**
      * 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&lt;T&gt;)
}

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 belowDEFAULTThe process

//#startCoroutineCancellable
public fun <T> (suspend () -> T).startCoroutineCancellable(completion: Continuation<T>): Unit = runSafely(completion) {
    createCoroutineUnintercepted(completion).intercepted().resumeCancellableWith((Unit))
}

startCoroutineCancellableFor coroutine creation and interceptionATOMICIt's the same, the difference isresumeCancellableWith

//DispatchedContinuation#resumeCancellableWith
public fun &lt;T&gt; Continuation&lt;T&gt;.resumeCancellableWith(
	result: Result&lt;T&gt;,
	onCancellation: ((cause: Throwable) -&gt; Unit)? = null
): Unit = when (this) {
	is DispatchedContinuation -&gt; resumeCancellableWith(result, onCancellation)
	else -&gt; 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&lt;T&gt;,
	noinline onCancellation: ((cause: Throwable) -&gt; 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 &amp;&amp; !) {
		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&lt;T&gt;) {
	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!