(This ended up being longer than I expected, so take a deep breath and dive in with an open, knowledge-thirsty mind. Oh and all the code snippets are either from the Kotlin stdlib, the Kotlin compiler source code, or kotlinx-coroutines, and all of which are easily available to check validity)
Okay there we go, I think I found the cause of confusion here. I haven’t supported the statement Continuation = Coroutine
, which is purely my fault, so sorry for that. Basically, if you look through the standard library, the only way to create a coroutine is with the aptly-named (suspend () -> T).createCoroutine
function that is an extension on suspend functions. That function is defined as follows:
public fun <T> (suspend () -> T).createCoroutine(
completion: Continuation<T>
): Continuation<Unit> =
SafeContinuation(createCoroutineUnintercepted(completion).intercepted(), COROUTINE_SUSPENDED)
(There’s also startCoroutine
that simply creates a coroutine with the above mechanism and calls resume(Unit)
on it)
As you can see, this function returns a Continuation<Unit>
, which is the only representation in the Kotlin stdlib
for a coroutine. It also uses createCoroutineUnintercepted
which is platform-specific. On the JVM, for example, it’s defined like this:
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)
}
}
where createCoroutineFromSuspendFunction
is a function defined like this:
private inline fun <T> createCoroutineFromSuspendFunction(
completion: Continuation<T>,
crossinline block: (Continuation<T>) -> Any?
): Continuation<Unit> {
// Omitted the implementation for length but it simply returns a ContinuationImpl object
For more evidence, all suspending lambdas are of this following class:
// Suspension lambdas inherit from this class
internal abstract class SuspendLambda(
public override val arity: Int,
completion: Continuation<Any?>?
) : ContinuationImpl(completion), FunctionBase<Any?>, SuspendFunction {
which clearly inherits from ContinuationImpl
On the JS side, there’s a CoroutineImpl
class that inherits from Continuation too, defined like this:
internal abstract class CoroutineImpl(private val resultContinuation: Continuation<Any?>) : Continuation<Any?>
The Kotlin compiler also makes references to suspend functions being represented as Continuations. For example, one of the transformations performed is described like this in the source code:
::SuspendLambdaLowering,
"SuspendLambda",
"Transform suspend lambdas into continuation classes"
and its implementation creates a class extending SuspendLambda
(which, if you remember, extends Continuation) and adds an invoke
method to it that calls invokeSuspend
. It also directly states in a comment:
// Invoke function in lambdas is responsible for
// 1) calling `create`
// 2) starting newly created coroutine by calling `invokeSuspend`.
// Thus, it creates a clone of suspend lambda and starts it.
invokeSuspend
is an abstract method in BaseContinuation that is implemented by the Kotlin compiler, but also you can see it implemented in the return value of createCoroutineFromSuspendFunction
like this:
object : ContinuationImpl(completion as Continuation<Any?>, context) {
private var label = 0
override fun invokeSuspend(result: Result<Any?>): Any? =
when (label) {
0 -> {
label = 1
result.getOrThrow() // Rethrow exception if trying to start with exception (will be caught by BaseContinuationImpl.resumeWith
block(this) // run the block, may return or suspend
}
1 -> {
label = 2
result.getOrThrow() // this is the result if the block had suspended
}
else -> error("This coroutine had already completed")
}
}
and for regular, named suspend functions, there’s a different AddContinuationsLowering
that has a method called generateContinuationClassForNamedFunction
that does that magic internally to create a class extending ContinuationImpl
and then add the needed invokeSuspend
method to it.
The kotlinx-coroutines library uses these intrinsic coroutine primitives to create its own coroutines. One example of that is the classic CoroutineScope.launch
call, which is defined like this:
public fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job {
val newContext = newCoroutineContext(context)
val coroutine = if (start.isLazy)
LazyStandaloneCoroutine(newContext, block) else
StandaloneCoroutine(newContext, active = true)
coroutine.start(start, coroutine, block)
return coroutine
}
StandaloneCoroutine
extends from AbstractCoroutine
, which wayyy down the rabbit hole ends up calling (suspend () -> T).startCoroutineCancellable
, which (guess what) calls createCoroutineUnintercepted
which ends up representing the primitive coroutine as a Continuation.
Simply, all coroutines are represented as Continuation<Unit>
, which is widely agreed upon in the Kotlin community too. For example, Arrow-Fx’s documentation mentions that equivalence. ProAndroidDev
also seems to have come to the same conclusions:
The compiler internally creates a private class that not only holds the data but also calls the suspend function -in our case syncData()- recursively to resume the execution of the function.
…
class SyncDataStateMachine(completion: Continuation<Any>) : CoroutineImpl(completion)
As you can see, this class extends from CoroutineImpl . But what is CoroutineImpl? Well, it’s just a subtype of continuation which expects the completion object as a constructor parameter (This is the continuation object which will be used to communicate back to the function)
…
The invokeSuspend function, which is used to resume the state of the StateMachine, by calling the syncData function with the information of the continuation to trigger the State Machine again.
and so I’m led to strongly believe that the term “Coroutine” absolutely represents execution mechanics