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.
|
||||
|
||||
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