Skip to content

Commit 897f02e

Browse files
authored
Get rid of NonRecoverableThrowable, mention way to opt-out stacktrace… (#1420)
Get rid of NonRecoverableThrowable, mention way to opt-out stacktrace recovery in debugging.md
1 parent 0905c62 commit 897f02e

File tree

5 files changed

+16
-22
lines changed

5 files changed

+16
-22
lines changed

docs/debugging.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ It is easy to demonstrate with actual stacktraces of the same program that await
4545

4646
The only downside of this approach is losing referential transparency of the exception.
4747

48-
### Stacktrace recovery machinery
48+
### Stacktrace recovery machinery
4949

5050
This section explains the inner mechanism of stacktrace recovery and can be skipped.
5151

@@ -56,6 +56,7 @@ and then throws the resulting exception instead of the original one.
5656

5757
Exception copy logic is straightforward:
5858
1) If the exception class implements [CopyableThrowable], [CopyableThrowable.createCopy] is used.
59+
`null` can be returned from `createCopy` to opt-out specific exception from being recovered.
5960
2) If the exception class has class-specific fields not inherited from Throwable, the exception is not copied.
6061
3) Otherwise, one of the public exception's constructor is invoked reflectively with an optional `initCause` call.
6162

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

-6
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,3 @@ internal expect interface CoroutineStackFrame {
4242
public val callerFrame: CoroutineStackFrame?
4343
public fun getStackTraceElement(): StackTraceElement?
4444
}
45-
46-
/**
47-
* Marker that indicates that stacktrace of the exception should not be recovered.
48-
* Currently internal, but may become public in the future
49-
*/
50-
internal interface NonRecoverableThrowable

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

+10-7
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22
* Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
33
*/
44

5+
@file:Suppress("unused")
6+
57
package kotlinx.coroutines
68

79
import kotlinx.coroutines.flow.*
810
import kotlin.coroutines.*
9-
import kotlinx.coroutines.internal.*
1011
import kotlin.test.*
1112

1213
public expect open class TestBase constructor() {
@@ -56,12 +57,14 @@ public suspend inline fun <reified T : Throwable> assertFailsWith(flow: Flow<*>)
5657
public suspend fun Flow<Int>.sum() = fold(0) { acc, value -> acc + value }
5758
public suspend fun Flow<Long>.longSum() = fold(0L) { acc, value -> acc + value }
5859

59-
public class TestException(message: String? = null) : Throwable(message), NonRecoverableThrowable
60-
public class TestException1(message: String? = null) : Throwable(message), NonRecoverableThrowable
61-
public class TestException2(message: String? = null) : Throwable(message), NonRecoverableThrowable
62-
public class TestException3(message: String? = null) : Throwable(message), NonRecoverableThrowable
63-
public class TestCancellationException(message: String? = null) : CancellationException(message), NonRecoverableThrowable
64-
public class TestRuntimeException(message: String? = null) : RuntimeException(message), NonRecoverableThrowable
60+
61+
// data is added to avoid stacktrace recovery because CopyableThrowable is not accessible from common modules
62+
public class TestException(message: String? = null, private val data: Any? = null) : Throwable(message)
63+
public class TestException1(message: String? = null, private val data: Any? = null) : Throwable(message)
64+
public class TestException2(message: String? = null, private val data: Any? = null) : Throwable(message)
65+
public class TestException3(message: String? = null, private val data: Any? = null) : Throwable(message)
66+
public class TestCancellationException(message: String? = null, private val data: Any? = null) : CancellationException(message)
67+
public class TestRuntimeException(message: String? = null, private val data: Any? = null) : RuntimeException(message)
6568
public class RecoverableTestException(message: String? = null) : RuntimeException(message)
6669
public class RecoverableTestCancellationException(message: String? = null) : CancellationException(message)
6770

kotlinx-coroutines-core/jvm/src/Debug.kt

-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ public const val DEBUG_PROPERTY_NAME = "kotlinx.coroutines.debug"
4242
* Stacktrace recovery mode wraps every exception into the exception of the same type with original exception
4343
* as cause, but with stacktrace of the current coroutine.
4444
* Exception is instantiated using reflection by using no-arg, cause or cause and message constructor.
45-
* Stacktrace is not recovered if exception is an instance of [CancellationException] or [NonRecoverableThrowable].
4645
*
4746
* This mechanism is currently supported for channels, [async], [launch], [coroutineScope], [supervisorScope]
4847
* and [withContext] builders.

kotlinx-coroutines-core/jvm/src/internal/StackTraceRecovery.kt

+4-7
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ private val stackTraceRecoveryClassName = runCatching {
2727
}.getOrElse { stackTraceRecoveryClass }
2828

2929
internal actual fun <E : Throwable> recoverStackTrace(exception: E): E {
30-
if (recoveryDisabled(exception)) return exception
30+
if (!RECOVER_STACK_TRACES) return exception
3131
// No unwrapping on continuation-less path: exception is not reported multiple times via slow paths
3232
val copy = tryCopyException(exception) ?: return exception
3333
return copy.sanitizeStackTrace()
@@ -53,7 +53,7 @@ private fun <E : Throwable> E.sanitizeStackTrace(): E {
5353
}
5454

5555
internal actual fun <E : Throwable> recoverStackTrace(exception: E, continuation: Continuation<*>): E {
56-
if (recoveryDisabled(exception) || continuation !is CoroutineStackFrame) return exception
56+
if (!RECOVER_STACK_TRACES || continuation !is CoroutineStackFrame) return exception
5757
return recoverFromStackFrame(exception, continuation)
5858
}
5959

@@ -147,15 +147,15 @@ private fun mergeRecoveredTraces(recoveredStacktrace: Array<StackTraceElement>,
147147

148148
@Suppress("NOTHING_TO_INLINE")
149149
internal actual suspend inline fun recoverAndThrow(exception: Throwable): Nothing {
150-
if (recoveryDisabled(exception)) throw exception
150+
if (!RECOVER_STACK_TRACES) throw exception
151151
suspendCoroutineUninterceptedOrReturn<Nothing> {
152152
if (it !is CoroutineStackFrame) throw exception
153153
throw recoverFromStackFrame(exception, it)
154154
}
155155
}
156156

157157
internal actual fun <E : Throwable> unwrap(exception: E): E {
158-
if (recoveryDisabled(exception)) return exception
158+
if (!RECOVER_STACK_TRACES) return exception
159159
val cause = exception.cause
160160
// Fast-path to avoid array cloning
161161
if (cause == null || cause.javaClass != exception.javaClass) {
@@ -170,9 +170,6 @@ internal actual fun <E : Throwable> unwrap(exception: E): E {
170170
}
171171
}
172172

173-
private fun <E : Throwable> recoveryDisabled(exception: E) =
174-
!RECOVER_STACK_TRACES || exception is NonRecoverableThrowable
175-
176173
private fun createStackTrace(continuation: CoroutineStackFrame): ArrayDeque<StackTraceElement> {
177174
val stack = ArrayDeque<StackTraceElement>()
178175
continuation.getStackTraceElement()?.let { stack.add(it) }

0 commit comments

Comments
 (0)