Skip to content

Commit 5c0e277

Browse files
qwwdfsaddee-tree
authored andcommitted
Introduce private DiagnosticCoroutineContextException and add it to the original exception prior to passing it to the Thread.currentThread().uncaughtExceptionHandler() (Kotlin#3170)
Fixes Kotlin#3153
1 parent 5c90cd7 commit 5c0e277

File tree

2 files changed

+33
-0
lines changed

2 files changed

+33
-0
lines changed

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

+21
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,24 @@ internal actual fun initializeDefaultExceptionHandlers() {
2727
CoroutineExceptionHandler
2828
}
2929

30+
/**
31+
* Private exception without stacktrace that is added to suppressed exceptions of the original exception
32+
* when it is reported to the last-ditch current thread 'uncaughtExceptionHandler'.
33+
*
34+
* The purpose of this exception is to add an otherwise inaccessible diagnostic information and to
35+
* be able to poke the failing coroutine context in the debugger.
36+
*/
37+
private class DiagnosticCoroutineContextException(private val context: CoroutineContext) : RuntimeException() {
38+
override fun getLocalizedMessage(): String {
39+
return context.toString()
40+
}
41+
42+
override fun fillInStackTrace(): Throwable {
43+
// Prevent Android <= 6.0 bug, #1866
44+
stackTrace = emptyArray()
45+
return this
46+
}
47+
}
3048

3149
internal actual fun handleCoroutineExceptionImpl(context: CoroutineContext, exception: Throwable) {
3250
// use additional extension handlers
@@ -42,5 +60,8 @@ internal actual fun handleCoroutineExceptionImpl(context: CoroutineContext, exce
4260

4361
// use thread's handler
4462
val currentThread = Thread.currentThread()
63+
// addSuppressed is never user-defined and cannot normally throw with the only exception being OOM
64+
// we do ignore that just in case to definitely deliver the exception
65+
runCatching { exception.addSuppressed(DiagnosticCoroutineContextException(context)) }
4566
currentThread.uncaughtExceptionHandler.uncaughtException(currentThread, exception)
4667
}

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

+12
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,16 @@ class CoroutineExceptionHandlerJvmTest : TestBase() {
3939

4040
finish(3)
4141
}
42+
43+
@Test
44+
fun testLastDitchHandlerContainsContextualInformation() = runBlocking {
45+
expect(1)
46+
GlobalScope.launch(CoroutineName("last-ditch")) {
47+
expect(2)
48+
throw TestException()
49+
}.join()
50+
assertTrue(caughtException is TestException)
51+
assertContains(caughtException.suppressed[0].toString(), "last-ditch")
52+
finish(3)
53+
}
4254
}

0 commit comments

Comments
 (0)