Document coroutines codegen: resume with result

This commit is contained in:
Ilmir Usmanov
2020-08-14 03:03:37 +02:00
committed by Ilmir Usmanov
parent 355b36ead9
commit 7e047ac96f
@@ -381,4 +381,72 @@ functions in the views' `IrFunction`, so it only adds the continuation parameter
optimization. Specifically, it simplifies tail-call optimization analysis for functions returning `Unit`. The codegen, however, generates
them as returning `Any?`.
The continuation parameter is named `$completion` in both Kotlin Metadata and LVT.
The continuation parameter is named `$completion` in both Kotlin Metadata and LVT.
### Resume With Result
Let us consider the following example with a suspending function, returning a value, instead of `Unit`:
```kotlin
import kotlin.coroutines.*
var c: Continuation<Int>? = null
suspend fun suspendMe(): Int = suspendCoroutine { continuation ->
c = continuation
}
fun builder(c: suspend () -> Unit) {
c.startCoroutine(object: Continuation<Unit> {
override val context = EmptyCoroutineContext
override fun resumeWith(result: Result<Unit>) {
result.getOrThrow()
}
})
}
fun main() {
val a: suspend () -> Unit = { println(suspendMe()) }
builder { a() }
c?.resume(42)
}
```
if one runs the program, it prints `42`. However, `suspendMe` does not return `42`. It just suspends and returns nothing. By the way,
`suspendMe`'s continuation has type `Continuation<Int>`, i.e., the return type of the function is used as a type argument of `Continuation`
interface, as I mentioned in the previous section (about continuation-passing style).
The state-machine section touched upon the `$result` variable inside the `invokeSuspend` function. The listing shows the `invokeSuspend`
function of `a`, but, unlike the previous example, with its signature:
```kotlin
fun invokeSuspend($result: Any?): Any? {
when(this.label) {
0 -> {
this.label = 1
$result = suspendMe(this)
if ($result == COROUTINE_SUSPENDED) return COROUTINE_SUSPENDED
goto 1
}
1 -> {
println($result)
return Unit
}
else -> {
throw IllegalStateException("call to 'resume' before 'invoke' with coroutine")
}
}
}
```
The listing shows that the `$result` variable is both parameter of the function and result of suspending call. Thus, when we call
`c?.resume(42)`, the value `42` is passed to `BaseContinuationImpl.resumeImpl`, it calls `invokeSuspend` with it. Now, since `label`'s value
is `1` (`suspendMe` suspended),
`42` is printed. Note that in the first state, we ignore the argument of `invokeSuspend`, and this becomes important when we
see how we start a coroutine.
So, what happens, when we call `resume` inside `suspendCoroutine`? Like
```kotlin
suspendCoroutine<Int> { it.resume(42) }
```
Following the resume process, `resume` calls continuation's `resumeWith`, which calls `invokeSuspend` with
value `42`. Then, this will be `$result` and work the same as if `suspendMe` returned `42`. In other words, `suspendCoroutine` with an
unconditional resume will not suspend the coroutine and is semantically the same as returning the value.
It is important to note that passing `COROUTINE_SUSPENDED` to continuation's `resumeWith` leads to undefined behavior.