| > Kotlin's co-routines are simply syntactic sugar for simple callbacks That's only partly true. And yeah they're callbacks but not like e.g. we know from JS. As far as I know CPS basically means that the Kotlin compiler turns a function like that: suspend fun myFunction(param: String) {
val asyncVal = someSuspendingFunction()
val asyncVal2 = anotherSuspendingFunctio(asyncVal)
return someCalculation(asyncVal2)
}
Into a modified method and *class* like that: // The different parts of the function get put into different ifs
// The state that is normally in the JVM stack frame is holded in a custom class which is a subclass of https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.coroutines/-continuation/
// Some integer tracks where in the function we are
fun myFunction(param: String, continuation: Something<MyFunctionClass>?) {
continuation = continuation ?: MyFunctionContinuation(param)
if (continuation.state == 0) {
contination.state = 1
someSuspendingFunction(continuation)
return SUSPEND
}
if (continuation.state == 1) {
// The result mechanism is in the Contiuation interface/abstract class. It can be used to later receive the async result
// of callig annother suspending function
continuation.asyncVal = contiuation.getResult()
anotherSuspendingFunction(continuation.asyncVal, continuation)
return SUSPEND
}
if (continuation.state == 2) {
continuation.asyncVal2 = contiuation.getResult()
return FINISHED(someCalculation(continuation.asyncVal2))
}
}
class MyFunctionContinuation: Continuation {
// Function arguments
val param: String
// Every suspending function needs this. It tracks where in the function we currently are
val state: Int = 0
// Local variables inside the function. Those would normally live in the JVM function stack
// But the Coroutine runtime needs to be able to "rehydrate" the JVM function stack once a non-blocking call finishes
var asyncVal: <Don't remember>
var asyncVal2: <Don't remember>
}
I hope the basic idea comes across.I read this sometime in some book about coroutines. But you can see that there is some overhead. The basic idea is to replicate the function call stack in a custom datastructure. This way we can be deep inside a normal JVM callstack and also have a separate Kotlin Coroutine callstack. Once the coroutine wants to suspend it can simply return from the JVM callstack but the Kotlin callstack is preserved. Later when we want to continue we can call the same function again, with the Kotlin callstack now containing the result of the async operation. If the JVM can do this natively we wouldn't need this second Kotlin coroutine callstack because the native JVM callstack would support this. |