@@ -76,7 +76,6 @@ internal abstract class DispatchedTask<in T> internal constructor(
76
76
77
77
final override fun run () {
78
78
assert { resumeMode != MODE_UNINITIALIZED } // should have been set before dispatching
79
- var fatalException: Throwable ? = null
80
79
try {
81
80
val delegate = delegate as DispatchedContinuation <T >
82
81
val continuation = delegate.continuation
@@ -102,11 +101,10 @@ internal abstract class DispatchedTask<in T> internal constructor(
102
101
}
103
102
}
104
103
}
104
+ } catch (e: DispatchException ) {
105
+ handleCoroutineException(delegate.context, e.cause)
105
106
} catch (e: Throwable ) {
106
- // This instead of runCatching to have nicer stacktrace and debug experience
107
- fatalException = e
108
- } finally {
109
- fatalException?.let { handleFatalException(it) }
107
+ handleFatalException(e)
110
108
}
111
109
}
112
110
@@ -143,8 +141,8 @@ internal fun <T> DispatchedTask<T>.dispatch(mode: Int) {
143
141
// dispatch directly using this instance's Runnable implementation
144
142
val dispatcher = delegate.dispatcher
145
143
val context = delegate.context
146
- if (dispatcher.isDispatchNeeded (context)) {
147
- dispatcher.dispatch (context, this )
144
+ if (dispatcher.safeIsDispatchNeeded (context)) {
145
+ dispatcher.safeDispatch (context, this )
148
146
} else {
149
147
resumeUnconfined()
150
148
}
@@ -205,3 +203,17 @@ internal inline fun DispatchedTask<*>.runUnconfinedEventLoop(
205
203
internal inline fun Continuation <* >.resumeWithStackTrace (exception : Throwable ) {
206
204
resumeWith(Result .failure(recoverStackTrace(exception, this )))
207
205
}
206
+
207
+ /* *
208
+ * This exception holds an exception raised in [CoroutineDispatcher.dispatch] method.
209
+ * When dispatcher methods fail unexpectedly, it is likely a user-induced programmatic bug,
210
+ * such as calling `executor.close()` prematurely. To avoid reporting such exceptions as fatal errors,
211
+ * we handle them with a separate code path. See also #4091.
212
+ *
213
+ * @see safeDispatch
214
+ */
215
+ internal class DispatchException (
216
+ override val cause : Throwable ,
217
+ dispatcher : CoroutineDispatcher ,
218
+ context : CoroutineContext ,
219
+ ) : Exception(" Coroutine dispatcher $dispatcher threw an exception, context = $context " , cause)
0 commit comments