Document coroutines codegen: superclasses
This commit is contained in:
committed by
Ilmir Usmanov
parent
bfd0291572
commit
0814c5cc93
@@ -1184,3 +1184,154 @@ or `yieldAll`. Furthermore, we do not want to intercept their continuations. We
|
|||||||
If we look at `sequence`, the `SequenceScope` interface is annotated with the annotation.
|
If we look at `sequence`, the `SequenceScope` interface is annotated with the annotation.
|
||||||
|
|
||||||
Since we do not want to intercept the continuations, their `context`s cannot be other than `EmptyCoroutineContext`.
|
Since we do not want to intercept the continuations, their `context`s cannot be other than `EmptyCoroutineContext`.
|
||||||
|
|
||||||
|
### Coroutine Superclasses
|
||||||
|
|
||||||
|
Below is the diagram of all continuation classes, defined in the standard library and used by the compiler:
|
||||||
|
```text
|
||||||
|
+------------+
|
||||||
|
|Continuation|
|
||||||
|
+------+-----+
|
||||||
|
^
|
||||||
|
|
|
||||||
|
+---------+----------+
|
||||||
|
|BaseContinuationImpl+<---------------+
|
||||||
|
+---------+----------+ |
|
||||||
|
^ |
|
||||||
|
| |
|
||||||
|
+-------+--------+ +-------------+------------+
|
||||||
|
|ContinuationImpl| |RestrictedContinuationImpl|
|
||||||
|
+-------+--------+ +-------------+------------+
|
||||||
|
^ ^
|
||||||
|
| |
|
||||||
|
+-----+-------+ +-----------+-----------+
|
||||||
|
|SuspendLambda| |RestrictedSuspendLambda|
|
||||||
|
+-------------+ +-----------------------+
|
||||||
|
```
|
||||||
|
|
||||||
|
The main superinterface of all coroutines is `Continuation`:
|
||||||
|
```kotlin
|
||||||
|
public interface Continuation<in T> {
|
||||||
|
public val context: CoroutineContext
|
||||||
|
public fun resumeWith(result: Result<T>)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
which is the only interface accessible by users. It is, essentially, the core of the coroutines' machinery. With the continuation-passing
|
||||||
|
style, every suspending function and lambda accept additional continuation parameter.
|
||||||
|
|
||||||
|
Every compiler generated continuation extends `BaseContinuationImpl`:
|
||||||
|
```kotlin
|
||||||
|
abstract class BaseContinuationImpl(
|
||||||
|
public val completion: Continuation<Any?>?
|
||||||
|
) : Continuation<Any?>, CoroutineStackFrame, Serializable {
|
||||||
|
public final override fun resumeWith(result: Result<Any?>)
|
||||||
|
protected abstract fun invokeSuspend(result: Result<Any?>): Any?
|
||||||
|
protected open fun releaseIntercepted()
|
||||||
|
public open fun create(completion: Continuation<*>): Continuation<Unit>
|
||||||
|
public open fun create(value: Any?, completion: Continuation<*>): Continuation<Unit>
|
||||||
|
public override fun toString(): String
|
||||||
|
public override val callerFrame: CoroutineStackFrame?
|
||||||
|
public override fun getStackTraceElement(): StackTraceElement?
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Note, its `resumeWith` function is final, but it introduces the `invokeSuspend` function. The `resumeWith` function does
|
||||||
|
the following:
|
||||||
|
1. When the user calls its `resumeWith`, it calls `invokeSuspend` with the argument, resuming the suspended coroutine with passed result or
|
||||||
|
exception.
|
||||||
|
2. It calls `completion` continuation's `resumeWith` when the coroutine completes so that execution returns to the caller.
|
||||||
|
3. It catches exceptions and resumes the `completion` with it, wrapped in `Result`, thus propagating the exception to the caller.
|
||||||
|
|
||||||
|
Additionally, `resumeWith` calls `releaseIntercepted` upon coroutine's completion to clear the interceptor up.
|
||||||
|
|
||||||
|
The compiler generates `create` overrides for suspending lambdas with zero or one parameter, since `createCoroutineUnintercepted` calls
|
||||||
|
them.
|
||||||
|
|
||||||
|
The rest (`callerFrame` and `getStackTraceElement`) come from the `CoroutineStackFrame` interface, and the debugger and kotlinx.coroutines
|
||||||
|
library use the interface to generate async stack traces.
|
||||||
|
|
||||||
|
The next class is `ContinuationImpl`. Every continuation of a suspending function, generated by the compiler, extends this class. Note that
|
||||||
|
the compiler does not generate restricted suspend functions (yet).
|
||||||
|
```kotlin
|
||||||
|
abstract class ContinuationImpl(
|
||||||
|
completion: Continuation<Any?>?,
|
||||||
|
private val _context: CoroutineContext?
|
||||||
|
) : BaseContinuationImpl(completion) {
|
||||||
|
public override val context: CoroutineContext
|
||||||
|
protected override fun releaseIntercepted()
|
||||||
|
|
||||||
|
private var intercepted: Continuation<Any?>?
|
||||||
|
public fun intercepted(): Continuation<Any?>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
It adds the `intercepted` field and `intercepted()` function covered in the corresponding section.
|
||||||
|
|
||||||
|
For restricted suspend function, there is`RestrictedContinuationImpl` class, and thus their context can only be `EmptyCoroutineContext`. It
|
||||||
|
allows us to save several bytes when one calls `startCoroutine` on a suspending functional type, which does not inherit
|
||||||
|
`BaseContinuationImpl`. For example, when
|
||||||
|
the receiver is a callable reference to suspend functions
|
||||||
|
the context of root continuation, passed to `startCoroutine` is `EmptyCoroutineContext`
|
||||||
|
```kotlin
|
||||||
|
abstract class RestrictedContinuationImpl(
|
||||||
|
completion: Continuation<Any?>?
|
||||||
|
) : BaseContinuationImpl(completion) {
|
||||||
|
public override val context: CoroutineContext
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
All non-restricted generated suspend lambdas extend `SuspendLambda`:
|
||||||
|
```kotlin
|
||||||
|
internal abstract class SuspendLambda(
|
||||||
|
public override val arity: Int,
|
||||||
|
completion: Continuation<Any?>?
|
||||||
|
) : ContinuationImpl(completion), FunctionBase<Any?>, SuspendFunction
|
||||||
|
```
|
||||||
|
since all suspend lambdas are functional types, they implement the `FunctionBase` interface. `SuspendFunction` is a marker interface used in
|
||||||
|
type checks and type conversions (see the next subsection).
|
||||||
|
|
||||||
|
All restricted generated suspend lambdas extend `RestrictedSuspendLambda`:
|
||||||
|
```kotlin
|
||||||
|
abstract class RestrictedSuspendLambda(
|
||||||
|
public override val arity: Int,
|
||||||
|
completion: Continuation<Any?>?
|
||||||
|
) : RestrictedContinuationImpl(completion), FunctionBase<Any?>, SuspendFunction
|
||||||
|
```
|
||||||
|
the only difference from `SuspendLambda` is superclass. `SuspendLambda` inherits `ContinuationImpl`, while
|
||||||
|
`RestrictedSuspendLambda` inherits `RestrictedSuspendLambda`.
|
||||||
|
|
||||||
|
#### SuspendFunction{N}
|
||||||
|
|
||||||
|
Every suspending lambda has a special suspend functional type: `SuspendFunction{N}`, where `{N}` is the number of lambda parameters. They
|
||||||
|
only exist during compile-time and are changed to `Function{N+1}` and `SuspendFunction`. Because `SuspendFunction{N}` is not present at
|
||||||
|
runtime, there would be no way to distinguish ordinary functional type
|
||||||
|
from suspend functional type if we would not use `SuspendFunction` marker interface. To be more specific, it is used in
|
||||||
|
`is SuspendFunction{N}` and `as SuspendFunction{N}` expressions. For example, if we have code like
|
||||||
|
```kotlin
|
||||||
|
fun main() {
|
||||||
|
val lambda: suspend () -> Unit = {}
|
||||||
|
val a: Any = lambda
|
||||||
|
print(a is (suspend () -> Unit))
|
||||||
|
}
|
||||||
|
```
|
||||||
|
for `is` expression we generate something like
|
||||||
|
```kotlin
|
||||||
|
a is SuspendFunction and TypeIntrinsics.isFunctionOfArity(a, 1)
|
||||||
|
```
|
||||||
|
That, by the way, is the reason why `SuspendLambda`'s constructor accepts arity.
|
||||||
|
|
||||||
|
Of course, all generated suspend lambdas implement `SuspendFunction` through `SuspendLambda` and `RestrcitedSuspendLambda`. Callable
|
||||||
|
references to suspend functions implement the interface directly.
|
||||||
|
|
||||||
|
#### 1.2: CoroutineImpl
|
||||||
|
|
||||||
|
In experimental coroutines, the diagram is much simpler.
|
||||||
|
```text
|
||||||
|
+------------+
|
||||||
|
|Continuation|
|
||||||
|
+------+-----+
|
||||||
|
^
|
||||||
|
|
|
||||||
|
+------+------+
|
||||||
|
|CoroutineImpl|
|
||||||
|
+-------------+
|
||||||
|
```
|
||||||
|
Every suspending function's continuation or suspend lambda extends `CoroutineImpl`.
|
||||||
|
|||||||
Reference in New Issue
Block a user