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