Skip to content

Commit 1032f58

Browse files
committed
Scoped builders and exception stacktraces synergy
* Recover stacktraces via both fast and slow paths in scoped builders * Merge stracktraces of nested scoped builders, eliminate common prefix so it looks like a flat profile * Optimize stacktrace-related machinery * Copy meaningful part of original exception into recovered one, rename "Current coroutine stacktrace" to "Coroutien boundary" * Rename Exception.kt to StackTraceRecovery.kt to avoid name clash with another Exception.kt
1 parent 25a886e commit 1032f58

File tree

19 files changed

+554
-226
lines changed

19 files changed

+554
-226
lines changed

common/kotlinx-coroutines-core-common/src/Dispatched.kt

+3-1
Original file line numberDiff line numberDiff line change
@@ -280,4 +280,6 @@ internal fun <T> DispatchedTask<T>.resume(delegate: Continuation<T>, useMode: In
280280

281281

282282
@Suppress("NOTHING_TO_INLINE")
283-
internal inline fun Continuation<*>.resumeWithStackTrace(exception: Throwable) = resumeWith(Result.failure(recoverStackTrace(exception, this)))
283+
internal inline fun Continuation<*>.resumeWithStackTrace(exception: Throwable) {
284+
resumeWith(Result.failure(recoverStackTrace(exception, this)))
285+
}

common/kotlinx-coroutines-core-common/src/JobSupport.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
243243
var suppressed = false
244244
for (exception in exceptions) {
245245
val unwrapped = unwrap(exception)
246-
if (unwrapped !== rootCause && unwrapped !is CancellationException && seenExceptions.add(exception)) {
246+
if (unwrapped !== rootCause && unwrapped !is CancellationException && seenExceptions.add(unwrapped)) {
247247
rootCause.addSuppressedThrowable(unwrapped)
248248
suppressed = true
249249
}

common/kotlinx-coroutines-core-common/src/Timeout.kt

+4-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
package kotlinx.coroutines
66

7+
import kotlinx.coroutines.internal.*
78
import kotlinx.coroutines.intrinsics.*
89
import kotlinx.coroutines.selects.*
910
import kotlin.coroutines.*
@@ -80,9 +81,10 @@ private fun <U, T: U> setupTimeout(
8081
private open class TimeoutCoroutine<U, in T: U>(
8182
@JvmField val time: Long,
8283
@JvmField val uCont: Continuation<U> // unintercepted continuation
83-
) : AbstractCoroutine<T>(uCont.context, active = true), Runnable, Continuation<T> {
84+
) : AbstractCoroutine<T>(uCont.context, active = true), Runnable, Continuation<T>, CoroutineStackFrame {
8485
override val defaultResumeMode: Int get() = MODE_DIRECT
85-
86+
override val callerFrame: CoroutineStackFrame? get() = (uCont as? CoroutineStackFrame)?.callerFrame
87+
override fun getStackTraceElement(): StackTraceElement? = (uCont as? CoroutineStackFrame)?.getStackTraceElement()
8688
@Suppress("LeakingThis", "Deprecation")
8789
override fun run() {
8890
cancel(TimeoutCancellationException(time, this))

common/kotlinx-coroutines-core-common/src/internal/Scopes.kt

+10-3
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,20 @@ internal open class ScopeCoroutine<in T>(
2121

2222
@Suppress("UNCHECKED_CAST")
2323
internal override fun onCompletionInternal(state: Any?, mode: Int, suppressed: Boolean) {
24-
if (state is CompletedExceptionally)
25-
uCont.resumeUninterceptedWithExceptionMode(state.cause, mode)
26-
else
24+
if (state is CompletedExceptionally) {
25+
val exception = if (mode == MODE_IGNORE) state.cause else recoverStackTrace(state.cause, uCont)
26+
uCont.resumeUninterceptedWithExceptionMode(exception, mode)
27+
} else {
2728
uCont.resumeUninterceptedMode(state as T, mode)
29+
}
2830
}
2931
}
3032

33+
internal fun AbstractCoroutine<*>.tryRecover(exception: Throwable): Throwable {
34+
val cont = (this as? ScopeCoroutine<*>)?.uCont ?: return exception
35+
return recoverStackTrace(exception, cont)
36+
}
37+
3138
internal class ContextScope(context: CoroutineContext) : CoroutineScope {
3239
override val coroutineContext: CoroutineContext = context
3340
}

common/kotlinx-coroutines-core-common/src/intrinsics/Undispatched.kt

+3-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package kotlinx.coroutines.intrinsics
66

77
import kotlinx.coroutines.*
8+
import kotlinx.coroutines.internal.*
89
import kotlin.coroutines.*
910
import kotlin.coroutines.intrinsics.*
1011

@@ -126,8 +127,8 @@ private inline fun <T> AbstractCoroutine<T>.undispatchedResult(
126127
val state = state
127128
if (state is CompletedExceptionally) {
128129
when {
129-
shouldThrow(state.cause) -> throw state.cause
130-
result is CompletedExceptionally -> throw result.cause
130+
shouldThrow(state.cause) -> throw tryRecover(state.cause)
131+
result is CompletedExceptionally -> throw tryRecover(result.cause)
131132
else -> result
132133
}
133134
} else {

common/kotlinx-coroutines-core-common/test/TestBase.common.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public class TestException1(message: String? = null) : Throwable(message), NonRe
2929
public class TestException2(message: String? = null) : Throwable(message), NonRecoverableThrowable
3030
public class TestException3(message: String? = null) : Throwable(message), NonRecoverableThrowable
3131
public class TestRuntimeException(message: String? = null) : RuntimeException(message), NonRecoverableThrowable
32-
public class RecoverableTestException(message: String? = null) : Throwable(message)
32+
public class RecoverableTestException(message: String? = null) : RuntimeException(message)
3333

3434
public fun wrapperDispatcher(context: CoroutineContext): CoroutineContext {
3535
val dispatcher = context[ContinuationInterceptor] as CoroutineDispatcher

core/kotlinx-coroutines-core/src/internal/Exceptions.kt

-117
This file was deleted.

core/kotlinx-coroutines-core/src/internal/ExceptionsConstuctor.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ import java.util.concurrent.locks.*
99
import kotlin.concurrent.*
1010

1111
private val cacheLock = ReentrantReadWriteLock()
12-
private val exceptionConstructors: WeakHashMap<Class<*>, (Throwable) -> Throwable?> = WeakHashMap()
12+
// Replace it with ClassValue when Java 6 support is over
13+
private val exceptionConstructors: WeakHashMap<Class<out Throwable>, (Throwable) -> Throwable?> = WeakHashMap()
1314

1415
@Suppress("UNCHECKED_CAST")
1516
internal fun <E : Throwable> tryCopyException(exception: E): E? {
@@ -24,7 +25,6 @@ internal fun <E : Throwable> tryCopyException(exception: E): E? {
2425
* Exceptions are shared among coroutines, so we should copy exception before recovering current stacktrace.
2526
*/
2627
var ctor: ((Throwable) -> Throwable?)? = null
27-
2828
val constructors = exception.javaClass.constructors.sortedByDescending { it.parameterTypes.size }
2929
for (constructor in constructors) {
3030
val parameters = constructor.parameterTypes

0 commit comments

Comments
 (0)