Coroutines debug probes

This commit is contained in:
Roman Elizarov
2018-07-15 19:37:46 +03:00
committed by Denis Zharkov
parent 78192ea712
commit 0b59061bfe
3 changed files with 94 additions and 12 deletions
@@ -77,13 +77,15 @@ public actual inline fun <R, T> (suspend R.() -> T).startCoroutineUninterceptedO
@SinceKotlin("1.3")
public actual fun <T> (suspend () -> T).createCoroutineUnintercepted(
completion: Continuation<T>
): Continuation<Unit> =
if (this is BaseContinuationImpl)
create(completion)
): Continuation<Unit> {
val probeCompletion = probeCoroutineCreated(completion)
return if (this is BaseContinuationImpl)
create(probeCompletion)
else
createCoroutineFromSuspendFunction(completion) {
createCoroutineFromSuspendFunction(probeCompletion) {
(this as Function1<Continuation<T>, Any?>).invoke(it)
}
}
/**
* Creates unintercepted coroutine with receiver type [R] and result type [T].
@@ -110,14 +112,16 @@ public actual fun <T> (suspend () -> T).createCoroutineUnintercepted(
public actual fun <R, T> (suspend R.() -> T).createCoroutineUnintercepted(
receiver: R,
completion: Continuation<T>
): Continuation<Unit> =
if (this is BaseContinuationImpl)
create(receiver, completion)
): Continuation<Unit> {
val probeCompletion = probeCoroutineCreated(completion)
return if (this is BaseContinuationImpl)
create(receiver, probeCompletion)
else {
createCoroutineFromSuspendFunction(completion) {
createCoroutineFromSuspendFunction(probeCompletion) {
(this as Function2<R, Continuation<T>, Any?>).invoke(receiver, it)
}
}
}
/**
* Intercepts this continuation with [ContinuationInterceptor].
@@ -175,7 +179,7 @@ private inline fun <T> createCoroutineFromSuspendFunction(
else
object : ContinuationImpl(completion as Continuation<Any?>, context) {
private var label = 0
override fun invokeSuspend(result: SuccessOrFailure<Any?>): Any? =
when (label) {
0 -> {
@@ -13,14 +13,17 @@ import kotlin.jvm.internal.Reflection
@SinceKotlin("1.3")
internal abstract class BaseContinuationImpl(
@JvmField
protected val completion: Continuation<Any?>?
// This is `public val` so that it is private on JVM and cannot be modified by untrusted code, yet
// it has a public getter (since even untrusted code is allowed to inspect its call stack).
public val completion: Continuation<Any?>?
) : Continuation<Any?>, Serializable {
// This implementation is final. This fact is used to unroll resumeWith recursion.
public final override fun resumeWith(result: SuccessOrFailure<Any?>) {
// Invoke "resume" debug probe only once, even if previous frames are "resumed" in the loop below, too
probeCoroutineResumed(this)
// This loop unrolls recursion in current.resumeWith(param) to make saner and shorter stack traces on resume
var current = this
var param = result
// This loop unrolls recursion in current.resumeWith(param) to make saner and shorter stack traces on resume
while (true) {
with(current) {
val completion = completion!! // fail fast when trying to resume continuation without completion
@@ -0,0 +1,75 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package kotlin.coroutines.jvm.internal
import kotlin.coroutines.Continuation
import kotlin.coroutines.intrinsics.*
/**
* This probe is invoked when coroutine is being created and it can replace completion
* with its own wrapped object to intercept completion of this coroutine.
*
* This probe is invoked from stdlib implementation of [createCoroutineUnintercepted] function.
*
* Once created, coroutine is repeatedly [resumed][probeCoroutineResumed] and [suspended][probeCoroutineSuspended],
* until it is complete. On completion, the object that was returned by this probe is invoked.
*
* ```
* +-------+ probeCoroutineCreated +-----------+
* | START | ---------------------->| SUSPENDED |
* +-------+ +-----------+
* probeCoroutineResumed | ^ probeCoroutineSuspended
* V |
* +------------+ completion invoked +-----------+
* | RUNNING | ------------------->| COMPLETED |
* +------------+ +-----------+
* ```
*
* While the coroutine is resumed and suspended, it is represented by the pointer to its `frame`
* which always extends [BaseContinuationImpl] and represents a pointer to the topmost frame of the
* coroutine. Each [BaseContinuationImpl] object has [completion][BaseContinuationImpl.completion] reference
* that points either to another frame (extending [BaseContinuationImpl]) or to the completion object
* that was returned by this `probeCoroutineCreated` function.
*
* When coroutine is [suspended][probeCoroutineSuspended], then it is later [resumed][probeCoroutineResumed]
* with a reference to the same frame. However, while coroutine is running it can unwind its frames and
* invoke other suspending functions, so its next suspension can happen with a different frame pointer.
*/
@SinceKotlin("1.3")
internal fun <T> probeCoroutineCreated(completion: Continuation<T>): Continuation<T> {
/** implementation of this function is replaced by debugger */
return completion
}
/**
* This probe is invoked when coroutine is resumed using [Continuation.resumeWith].
*
* This probe is invoked from stdlib implementation of [BaseContinuationImpl.resumeWith] function.
*
* Coroutines machinery implementation guarantees that the actual [frame] instance extends
* [BaseContinuationImpl] class, despite the fact that the declared type of [frame]
* parameter in this function is `Continuation<*>`. See [probeCoroutineCreated] for details.
*/
@SinceKotlin("1.3")
internal fun probeCoroutineResumed(frame: Continuation<*>) {
/** implementation of this function is replaced by debugger */
}
/**
* This probe is invoked when coroutine is suspended using [suspendCoroutineUninterceptedOrReturn], that is
* when the corresponding `block` returns [COROUTINE_SUSPENDED].
*
* This probe is invoked from compiler-generated intrinsic for [suspendCoroutineUninterceptedOrReturn] function.
*
* Coroutines machinery implementation guarantees that the actual [frame] instance extends
* [BaseContinuationImpl] class, despite the fact that the declared type of [frame]
* parameter in this function is `Continuation<*>`. See [probeCoroutineCreated] for details.
*/
@SinceKotlin("1.3")
internal fun probeCoroutineSuspended(frame: Continuation<*>) {
/** implementation of this function is replaced by debugger */
}