Document coroutines codegen: resume with result
This commit is contained in:
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.
|
||||
Reference in New Issue
Block a user