Document coroutines codegen: suspend markers

This commit is contained in:
Ilmir Usmanov
2020-08-14 00:32:27 +02:00
committed by Ilmir Usmanov
parent 1d7a661624
commit f7cf5f435f
@@ -252,4 +252,26 @@ On a closing note, the state machine should be flat; in other words, there shoul
Otherwise, inner state-machine states will rewrite `label`, breaking the whole suspend-resume machinery and leading to weird behavior,
ranging from CCE to infinite loops. Similar buggy behavior happens when several suspending calls are in one state: when the first call
suspends, and then the execution resumes, skipping all the remaining code in the state. Both these bugs were quite frequent in the early
days of coroutines inlining.
days of coroutines inlining.
#### JVM: Suspend Markers
To distinguish suspending calls from ordinary ones, the codegen (more specifically, `ExpressionCodegen`) puts so-called suspend markers
around the calls. We need to generate markers both before the call and after it since callee can be `suspendCoroutineUninterceptedOrReturn`
intrinsic, and we consider its call as a suspension point, i.e., a place where a coroutine can suspend. Thus, a suspension point is either
a non-inline suspend call or an inlined `suspendCoroutineUninterceptedOrReturn` call.
It is important to note that there is no point in putting the markers around inline function calls since the compiler inlines their bodies,
and their suspend points will become inline-side's suspension points. Remember, a state-machines should be flat, without nested
state-machines.
The markers are calls of `kotlin.jvm.internal.InlineMarker.mark` with an integer parameter. The values for before and after suspending call
markers are `0` and `1` respectively. Inline suspending functions keep the markers in generated bytecode so, upon inlining suspending calls,
which they surround become suspension points. Well, at least in one copy of the inline function, to be technically correct. Because the
compiler generates two copies of it: one with a state-machine, so one can call it via reflection; and the other one without, so the inliner
can inline it without inlining the state-machine into another. Since there are libraries with inline suspend functions, the values passed
to `kotlin.jvm.internal.InlineMarker.mark`'s call cannot be changed.
The codegen generates the markers by calling `addSuspendMarker`. After generating `MethodNode` of the function, it passes the node to
`CoroutineTransformerMethodVisitor`. `CoroutineTransformerMethodVisitor` collects the suspension
points by checking these markers, and then it generates the state-machine.
FIXME: I should rename `CoroutineTransformerMethodVisitor` to `StateMachineBuilder` already.