From f7cf5f435fa96a2a3fb0539a57047879e5553b8d Mon Sep 17 00:00:00 2001 From: Ilmir Usmanov Date: Fri, 14 Aug 2020 00:32:27 +0200 Subject: [PATCH] Document coroutines codegen: suspend markers --- .../codegen/coroutines/coroutines-codegen.md | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/coroutines-codegen.md b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/coroutines-codegen.md index d800eba6c0f..77ed14ad01d 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/coroutines-codegen.md +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/coroutines-codegen.md @@ -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. \ No newline at end of file +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. \ No newline at end of file