1 package kotlinx.coroutines.internal
2 
3 import kotlinx.coroutines.*
4 import kotlin.coroutines.*
5 
6 /**
7  * The list of globally installed [CoroutineExceptionHandler] instances that will be notified of any exceptions that
8  * were not processed in any other manner.
9  */
10 internal expect val platformExceptionHandlers: Collection<CoroutineExceptionHandler>
11 
12 /**
13  * Ensures that the given [callback] is present in the [platformExceptionHandlers] list.
14  */
ensurePlatformExceptionHandlerLoadednull15 internal expect fun ensurePlatformExceptionHandlerLoaded(callback: CoroutineExceptionHandler)
16 
17 /**
18  * The platform-dependent global exception handler, used so that the exception is logged at least *somewhere*.
19  */
20 internal expect fun propagateExceptionFinalResort(exception: Throwable)
21 
22 /**
23  * Deal with exceptions that happened in coroutines and weren't programmatically dealt with.
24  *
25  * First, it notifies every [CoroutineExceptionHandler] in the [platformExceptionHandlers] list.
26  * If one of them throws [ExceptionSuccessfullyProcessed], it means that that handler believes that the exception was
27  * dealt with sufficiently well and doesn't need any further processing.
28  * Otherwise, the platform-dependent global exception handler is also invoked.
29  */
30 internal fun handleUncaughtCoroutineException(context: CoroutineContext, exception: Throwable) {
31     // use additional extension handlers
32     for (handler in platformExceptionHandlers) {
33         try {
34             handler.handleException(context, exception)
35         } catch (_: ExceptionSuccessfullyProcessed) {
36             return
37         } catch (t: Throwable) {
38             propagateExceptionFinalResort(handlerException(exception, t))
39         }
40     }
41 
42     try {
43         exception.addSuppressed(DiagnosticCoroutineContextException(context))
44     } catch (e: Throwable) {
45         // addSuppressed is never user-defined and cannot normally throw with the only exception being OOM
46         // we do ignore that just in case to definitely deliver the exception
47     }
48     propagateExceptionFinalResort(exception)
49 }
50 
51 /**
52  * Private exception that is added to suppressed exceptions of the original exception
53  * when it is reported to the last-ditch current thread 'uncaughtExceptionHandler'.
54  *
55  * The purpose of this exception is to add an otherwise inaccessible diagnostic information and to
56  * be able to poke the context of the failing coroutine in the debugger.
57  */
58 internal expect class DiagnosticCoroutineContextException(context: CoroutineContext) : RuntimeException
59 
60 /**
61  * A dummy exception that signifies that the exception was successfully processed by the handler and no further
62  * action is required.
63  *
64  * Would be nicer if [CoroutineExceptionHandler] could return a boolean, but that would be a breaking change.
65  * For now, we will take solace in knowledge that such exceptions are exceedingly rare, even rarer than globally
66  * uncaught exceptions in general.
67  */
68 internal object ExceptionSuccessfullyProcessed : Exception()
69