Skip to content

Commit d562f26

Browse files
authored
Simplify internal handling of suppressed exceptions (#4007)
1 parent 53e93e8 commit d562f26

16 files changed

+29
-102
lines changed

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public fun handleCoroutineException(context: CoroutineContext, exception: Throwa
3535
internal fun handlerException(originalException: Throwable, thrownException: Throwable): Throwable {
3636
if (originalException === thrownException) return originalException
3737
return RuntimeException("Exception while trying to handle coroutine exception", thrownException).apply {
38-
addSuppressedThrowable(originalException)
38+
addSuppressed(originalException)
3939
}
4040
}
4141

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

-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,5 @@ internal expect class JobCancellationException(
2626

2727
internal class CoroutinesInternalError(message: String, cause: Throwable) : Error(message, cause)
2828

29-
internal expect fun Throwable.addSuppressedThrowable(other: Throwable)
3029
// For use in tests
3130
internal expect val RECOVER_STACK_TRACES: Boolean

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
281281
val unwrapped = unwrap(exception)
282282
if (unwrapped !== rootCause && unwrapped !== unwrappedCause &&
283283
unwrapped !is CancellationException && seenExceptions.add(unwrapped)) {
284-
rootCause.addSuppressedThrowable(unwrapped)
284+
rootCause.addSuppressed(unwrapped)
285285
}
286286
}
287287
}
@@ -369,7 +369,7 @@ public open class JobSupport constructor(active: Boolean) : Job, ChildJob, Paren
369369
try {
370370
node.invoke(cause)
371371
} catch (ex: Throwable) {
372-
exception?.apply { addSuppressedThrowable(ex) } ?: run {
372+
exception?.apply { addSuppressed(ex) } ?: run {
373373
exception = CompletionHandlerException("Exception in completion handler $node for $this", ex)
374374
}
375375
}

kotlinx-coroutines-core/common/src/channels/Deprecated.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ internal fun consumesAll(vararg channels: ReceiveChannel<*>): CompletionHandler
5656
if (exception == null) {
5757
exception = e
5858
} else {
59-
exception.addSuppressedThrowable(e)
59+
exception.addSuppressed(e)
6060
}
6161
}
6262
exception?.let { throw it }

kotlinx-coroutines-core/common/src/flow/operators/Emitters.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ private suspend fun <T> FlowCollector<T>.invokeSafely(
215215
try {
216216
action(cause)
217217
} catch (e: Throwable) {
218-
if (cause !== null && cause !== e) e.addSuppressedThrowable(cause)
218+
if (cause !== null && cause !== e) e.addSuppressed(cause)
219219
throw e
220220
}
221221
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ internal abstract class DispatchedTask<in T> internal constructor(
139139
internal fun handleFatalException(exception: Throwable?, finallyException: Throwable?) {
140140
if (exception === null && finallyException === null) return
141141
if (exception !== null && finallyException !== null) {
142-
exception.addSuppressedThrowable(finallyException)
142+
exception.addSuppressed(finallyException)
143143
}
144144

145145
val cause = exception ?: finallyException

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ internal fun <E> OnUndeliveredElement<E>.callUndeliveredElementCatchingException
1919
// undeliveredElementException.cause !== ex is an optimization in case the same exception is thrown
2020
// over and over again by on OnUndeliveredElement
2121
if (undeliveredElementException != null && undeliveredElementException.cause !== ex) {
22-
undeliveredElementException.addSuppressedThrowable(ex)
22+
undeliveredElementException.addSuppressed(ex)
2323
} else {
2424
return UndeliveredElementException("Exception in undelivered element handler for $element", ex)
2525
}

kotlinx-coroutines-core/concurrent/test/ConcurrentExceptionsStressTest.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ class ConcurrentExceptionsStressTest : TestBase() {
4848
val completionException = deferred.getCompletionExceptionOrNull()
4949
val cause = completionException as? StressException
5050
?: unexpectedException("completion", completionException)
51-
val suppressed = cause.suppressed
51+
val suppressed = cause.suppressedExceptions
5252
val indices = listOf(cause.index) + suppressed.mapIndexed { index, e ->
5353
(e as? StressException)?.index ?: unexpectedException("suppressed $index", e)
5454
}
@@ -62,6 +62,6 @@ class ConcurrentExceptionsStressTest : TestBase() {
6262
throw IllegalStateException("Unexpected $msg exception", e)
6363
}
6464

65-
private class StressException(val index: Int) : SuppressSupportingThrowable()
65+
private class StressException(val index: Int) : Throwable()
6666
}
6767

kotlinx-coroutines-core/concurrent/test/ConcurrentTestUtilities.common.kt

+18-5
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,26 @@
55
package kotlinx.coroutines.exceptions
66

77
import kotlinx.coroutines.*
8+
import kotlin.concurrent.Volatile
9+
import kotlin.random.*
810

9-
internal expect open class SuppressSupportingThrowable() : Throwable
10-
expect val Throwable.suppressed: Array<Throwable>
11-
// Unused on purpose, used manually during debugging sessions
12-
expect fun Throwable.printStackTrace()
11+
fun randomWait() {
12+
val n = Random.nextInt(1000)
13+
if (n < 500) return // no wait 50% of time
14+
repeat(n) {
15+
BlackHole.sink *= 3
16+
}
17+
// use the BlackHole value somehow, so even if the compiler gets smarter, it won't remove the object
18+
val sinkValue = if (BlackHole.sink > 16) 1 else 0
19+
if (n + sinkValue > 900) yieldThread()
20+
}
21+
22+
private object BlackHole {
23+
@Volatile
24+
var sink = 1
25+
}
1326

14-
expect fun randomWait()
27+
expect inline fun yieldThread()
1528

1629
expect fun currentThreadName(): String
1730

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

-3
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,5 @@ internal actual class JobCancellationException public actual constructor(
3535
(message!!.hashCode() * 31 + job.hashCode()) * 31 + (cause?.hashCode() ?: 0)
3636
}
3737

38-
@Suppress("NOTHING_TO_INLINE")
39-
internal actual inline fun Throwable.addSuppressedThrowable(other: Throwable) { /* empty */ }
40-
4138
// For use in tests
4239
internal actual val RECOVER_STACK_TRACES: Boolean = false

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

-4
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,3 @@ internal actual class JobCancellationException public actual constructor(
6868
override fun hashCode(): Int =
6969
(message!!.hashCode() * 31 + job.hashCode()) * 31 + (cause?.hashCode() ?: 0)
7070
}
71-
72-
@Suppress("NOTHING_TO_INLINE")
73-
internal actual inline fun Throwable.addSuppressedThrowable(other: Throwable) =
74-
addSuppressed(other)

kotlinx-coroutines-core/jvm/test/ConcurrentTestUtilities.kt

+1-21
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,6 @@
44

55
package kotlinx.coroutines.exceptions
66

7-
import kotlin.random.*
8-
9-
actual fun randomWait() {
10-
val n = Random.nextInt(1000)
11-
if (n < 500) return // no wait 50% of time
12-
repeat(n) {
13-
BlackHole.sink *= 3
14-
}
15-
if (n > 900) Thread.yield()
16-
}
17-
18-
private object BlackHole {
19-
@Volatile
20-
var sink = 1
21-
}
22-
23-
@Suppress("ACTUAL_WITHOUT_EXPECT")
24-
internal actual typealias SuppressSupportingThrowable = Throwable
25-
26-
@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "unused")
27-
actual fun Throwable.printStackTrace() = printStackTrace()
7+
actual inline fun yieldThread() { Thread.yield() }
288

299
actual fun currentThreadName(): String = Thread.currentThread().name

kotlinx-coroutines-core/jvm/test/exceptions/Exceptions.kt

-11
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,6 @@ import java.util.*
1010
import kotlin.coroutines.*
1111
import kotlin.test.*
1212

13-
/**
14-
* Proxy for [Throwable.getSuppressed] for tests, which are compiled for both JDK 1.6 and JDK 1.8,
15-
* but run only under JDK 1.8
16-
*/
17-
@Suppress("ConflictingExtensionProperty")
18-
actual val Throwable.suppressed: Array<Throwable> get() {
19-
val method = this::class.java.getMethod("getSuppressed") ?: error("This test can only be run using JDK 1.7")
20-
@Suppress("UNCHECKED_CAST")
21-
return method.invoke(this) as Array<Throwable>
22-
}
23-
2413
internal inline fun <reified T : Throwable> checkException(exception: Throwable): Boolean {
2514
assertTrue(exception is T)
2615
assertTrue(exception.suppressed.isEmpty())

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

-8
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@
44

55
package kotlinx.coroutines
66

7-
import kotlinx.coroutines.internal.*
8-
import kotlinx.coroutines.internal.SuppressSupportingThrowableImpl
9-
107
/**
118
* Thrown by cancellable suspending functions if the [Job] of the coroutine is cancelled while it is suspending.
129
* It indicates _normal_ cancellation of a coroutine.
@@ -38,10 +35,5 @@ internal actual class JobCancellationException public actual constructor(
3835
(message!!.hashCode() * 31 + job.hashCode()) * 31 + (cause?.hashCode() ?: 0)
3936
}
4037

41-
@Suppress("NOTHING_TO_INLINE")
42-
internal actual inline fun Throwable.addSuppressedThrowable(other: Throwable) {
43-
if (this is SuppressSupportingThrowableImpl) addSuppressed(other)
44-
}
45-
4638
// For use in tests
4739
internal actual val RECOVER_STACK_TRACES: Boolean = false

kotlinx-coroutines-core/native/src/internal/Concurrent.kt

-15
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,5 @@ internal actual inline fun <T> ReentrantLock.withLock(action: () -> T): T = this
1616

1717
internal actual fun <E> identitySet(expectedSize: Int): MutableSet<E> = HashSet()
1818

19-
20-
// "Suppress-supporting throwable" is currently used for tests only
21-
internal open class SuppressSupportingThrowableImpl : Throwable() {
22-
private val _suppressed = atomic<Array<Throwable>>(emptyArray())
23-
24-
val suppressed: Array<Throwable>
25-
get() = _suppressed.value
26-
27-
fun addSuppressed(other: Throwable) {
28-
_suppressed.update { current ->
29-
current + other
30-
}
31-
}
32-
}
33-
3419
@Suppress("ACTUAL_WITHOUT_EXPECT") // This suppress can be removed in 2.0: KT-59355
3520
internal actual typealias BenignDataRace = kotlin.concurrent.Volatile

kotlinx-coroutines-core/native/test/ConcurrentTestUtilities.kt

+1-25
Original file line numberDiff line numberDiff line change
@@ -4,33 +4,9 @@
44

55
package kotlinx.coroutines.exceptions
66

7-
import kotlinx.coroutines.internal.*
87
import platform.posix.*
98
import kotlin.native.concurrent.*
10-
import kotlin.random.*
119

12-
actual fun randomWait() {
13-
val n = Random.nextInt(1000)
14-
if (n < 500) return // no wait 50% of time
15-
repeat(n) {
16-
BlackHole.sink *= 3
17-
}
18-
// use the BlackHole value somehow, so even if the compiler gets smarter, it won't remove the object
19-
val sinkValue = if (BlackHole.sink > 16) 1 else 0
20-
if (n + sinkValue > 900) sched_yield()
21-
}
22-
23-
@ThreadLocal
24-
private object BlackHole {
25-
var sink = 1
26-
}
27-
28-
internal actual typealias SuppressSupportingThrowable = SuppressSupportingThrowableImpl
29-
30-
actual val Throwable.suppressed: Array<Throwable>
31-
get() = (this as? SuppressSupportingThrowableImpl)?.suppressed ?: emptyArray()
32-
33-
@Suppress("EXTENSION_SHADOWED_BY_MEMBER", "unused")
34-
actual fun Throwable.printStackTrace() = printStackTrace()
10+
actual inline fun yieldThread() { sched_yield() }
3511

3612
actual fun currentThreadName(): String = Worker.current.name

0 commit comments

Comments
 (0)