Unroll recursion in resumeWith
Fixes KT-18987
This commit is contained in:
committed by
Denis Zharkov
parent
af9743709c
commit
7513557315
+37
-26
@@ -31,19 +31,42 @@ internal abstract class RestrictedContinuationImpl protected constructor(
|
|||||||
public override val context: CoroutineContext
|
public override val context: CoroutineContext
|
||||||
get() = EmptyCoroutineContext
|
get() = EmptyCoroutineContext
|
||||||
|
|
||||||
public override fun resumeWith(result: SuccessOrFailure<Any?>) {
|
// This implementation is final that it is fundamentally used to unroll resumeWith recursion
|
||||||
val completion = completion!! // fail fast when trying to resume continuation without completion
|
public final override fun resumeWith(result: SuccessOrFailure<Any?>) {
|
||||||
try {
|
var current = this
|
||||||
val outcome = invokeSuspend(result)
|
var param = result
|
||||||
if (outcome === CoroutineSingletons.COROUTINE_SUSPENDED) return
|
// This loop unrolls recursion in current.resumeWith(param) to make saner and shorter stack traces on resume
|
||||||
completion.resume(outcome)
|
while (true) {
|
||||||
} catch (exception: Throwable) {
|
with(current) {
|
||||||
completion.resumeWithException(exception)
|
val completion = completion!! // fail fast when trying to resume continuation without completion
|
||||||
|
val outcome: SuccessOrFailure<Any?> =
|
||||||
|
try {
|
||||||
|
val outcome = invokeSuspend(param)
|
||||||
|
if (outcome === CoroutineSingletons.COROUTINE_SUSPENDED) return
|
||||||
|
SuccessOrFailure.success(outcome)
|
||||||
|
} catch (exception: Throwable) {
|
||||||
|
SuccessOrFailure.failure(exception)
|
||||||
|
}
|
||||||
|
releaseIntercepted() // this state machine instance is terminating
|
||||||
|
if (completion is RestrictedContinuationImpl) {
|
||||||
|
// unrolling recursion via loop
|
||||||
|
current = completion
|
||||||
|
param = outcome
|
||||||
|
} else {
|
||||||
|
// top-level completion reached -- invoke and return
|
||||||
|
completion.resumeWith(outcome)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract fun invokeSuspend(result: SuccessOrFailure<Any?>): Any?
|
protected abstract fun invokeSuspend(result: SuccessOrFailure<Any?>): Any?
|
||||||
|
|
||||||
|
protected open fun releaseIntercepted() {
|
||||||
|
// does nothing here, overridden in ContinuationImpl
|
||||||
|
}
|
||||||
|
|
||||||
public open fun create(completion: Continuation<*>): Continuation<Unit> {
|
public open fun create(completion: Continuation<*>): Continuation<Unit> {
|
||||||
throw UnsupportedOperationException("create(Continuation) has not been overridden")
|
throw UnsupportedOperationException("create(Continuation) has not been overridden")
|
||||||
}
|
}
|
||||||
@@ -61,11 +84,11 @@ internal abstract class ContinuationImpl protected constructor(
|
|||||||
) : RestrictedContinuationImpl(completion) {
|
) : RestrictedContinuationImpl(completion) {
|
||||||
protected constructor(completion: Continuation<Any?>?) : this(completion, completion?.context)
|
protected constructor(completion: Continuation<Any?>?) : this(completion, completion?.context)
|
||||||
|
|
||||||
override fun validateContext() {
|
protected override fun validateContext() {
|
||||||
// nothing to do here -- supports any context
|
// nothing to do here -- supports any context
|
||||||
}
|
}
|
||||||
|
|
||||||
override val context: CoroutineContext
|
public override val context: CoroutineContext
|
||||||
get() = _context!!
|
get() = _context!!
|
||||||
|
|
||||||
@Transient
|
@Transient
|
||||||
@@ -76,20 +99,7 @@ internal abstract class ContinuationImpl protected constructor(
|
|||||||
?: (context[ContinuationInterceptor]?.interceptContinuation(this) ?: this)
|
?: (context[ContinuationInterceptor]?.interceptContinuation(this) ?: this)
|
||||||
.also { intercepted = this }
|
.also { intercepted = this }
|
||||||
|
|
||||||
public override fun resumeWith(result: SuccessOrFailure<Any?>) {
|
protected override fun releaseIntercepted() {
|
||||||
val completion = completion!! // fail fast when trying to resume continuation without completion
|
|
||||||
try {
|
|
||||||
val outcome = invokeSuspend(result)
|
|
||||||
if (outcome === CoroutineSingletons.COROUTINE_SUSPENDED) return
|
|
||||||
releaseIntercepted()
|
|
||||||
completion.resume(outcome)
|
|
||||||
} catch (exception: Throwable) {
|
|
||||||
releaseIntercepted()
|
|
||||||
completion.resumeWithException(exception)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun releaseIntercepted() {
|
|
||||||
val intercepted = intercepted
|
val intercepted = intercepted
|
||||||
if (intercepted != null && intercepted != this) {
|
if (intercepted != null && intercepted != this) {
|
||||||
context[ContinuationInterceptor]!!.releaseInterceptedContinuation(intercepted)
|
context[ContinuationInterceptor]!!.releaseInterceptedContinuation(intercepted)
|
||||||
@@ -97,13 +107,12 @@ internal abstract class ContinuationImpl protected constructor(
|
|||||||
this.intercepted = CompletedContinuation // just in case
|
this.intercepted = CompletedContinuation // just in case
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
public override fun toString(): String {
|
||||||
// todo: how continuation shall be rendered?
|
// todo: how continuation shall be rendered?
|
||||||
return "Continuation @ ${this::class.java.name}"
|
return "Continuation @ ${this::class.java.name}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: Do we really need it?
|
|
||||||
internal object CompletedContinuation : Continuation<Any?> {
|
internal object CompletedContinuation : Continuation<Any?> {
|
||||||
override val context: CoroutineContext
|
override val context: CoroutineContext
|
||||||
get() = error("This continuation is already complete")
|
get() = error("This continuation is already complete")
|
||||||
@@ -111,6 +120,8 @@ internal object CompletedContinuation : Continuation<Any?> {
|
|||||||
override fun resumeWith(result: SuccessOrFailure<Any?>) {
|
override fun resumeWith(result: SuccessOrFailure<Any?>) {
|
||||||
error("This continuation is already complete")
|
error("This continuation is already complete")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun toString(): String = "This continuation is already complete"
|
||||||
}
|
}
|
||||||
|
|
||||||
@SinceKotlin("1.3")
|
@SinceKotlin("1.3")
|
||||||
|
|||||||
Reference in New Issue
Block a user