Skip to content

Commit 5befa1d

Browse files
committed
Protect isDispatchNeeded calls
1 parent 7bd5fbe commit 5befa1d

File tree

7 files changed

+30
-22
lines changed

7 files changed

+30
-22
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ public abstract class CoroutineDispatcher :
225225
* @suppress **This an internal API and should not be used from general code.**
226226
*/
227227
@InternalCoroutinesApi
228-
public open fun dispatchYield(context: CoroutineContext, block: Runnable): Unit = dispatch(context, block)
228+
public open fun dispatchYield(context: CoroutineContext, block: Runnable): Unit = safeDispatch(context, block)
229229

230230
/**
231231
* Returns a continuation that wraps the provided [continuation], thus intercepting all resumptions.

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public suspend fun yield(): Unit = suspendCoroutineUninterceptedOrReturn sc@ { u
2626
val context = uCont.context
2727
context.ensureActive()
2828
val cont = uCont.intercepted() as? DispatchedContinuation<Unit> ?: return@sc Unit
29-
if (cont.dispatcher.isDispatchNeeded(context)) {
29+
if (cont.dispatcher.safeIsDispatchNeeded(context)) {
3030
// this is a regular dispatcher -- do simple dispatchYield
3131
cont.dispatchYield(context, Unit)
3232
} else {

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

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -187,10 +187,10 @@ internal class DispatchedContinuation<in T>(
187187

188188
override fun resumeWith(result: Result<T>) {
189189
val state = result.toState()
190-
if (dispatcher.isDispatchNeeded(context)) {
190+
if (dispatcher.safeIsDispatchNeeded(context)) {
191191
_state = state
192192
resumeMode = MODE_ATOMIC
193-
dispatchWithExceptionHandling(context)
193+
dispatcher.safeDispatch(context, this)
194194
} else {
195195
executeUnconfined(state, MODE_ATOMIC) {
196196
withCoroutineContext(context, countOrElement) {
@@ -205,10 +205,10 @@ internal class DispatchedContinuation<in T>(
205205
@Suppress("NOTHING_TO_INLINE")
206206
internal inline fun resumeCancellableWith(result: Result<T>) {
207207
val state = result.toState()
208-
if (dispatcher.isDispatchNeeded(context)) {
208+
if (dispatcher.safeIsDispatchNeeded(context)) {
209209
_state = state
210210
resumeMode = MODE_CANCELLABLE
211-
dispatchWithExceptionHandling(context)
211+
dispatcher.safeDispatch(context, this)
212212
} else {
213213
executeUnconfined(state, MODE_CANCELLABLE) {
214214
if (!resumeCancelled(state)) {
@@ -247,13 +247,21 @@ internal class DispatchedContinuation<in T>(
247247

248248
override fun toString(): String =
249249
"DispatchedContinuation[$dispatcher, ${continuation.toDebugString()}]"
250+
}
250251

251-
private fun dispatchWithExceptionHandling(context: CoroutineContext) {
252-
try {
253-
dispatcher.dispatch(context, this)
254-
} catch (e: Throwable) {
255-
throw DispatchException(e, dispatcher, context)
256-
}
252+
internal fun CoroutineDispatcher.safeDispatch(context: CoroutineContext, runnable: Runnable) {
253+
try {
254+
dispatch(context, runnable)
255+
} catch (e: Throwable) {
256+
throw DispatchException(e, this, context)
257+
}
258+
}
259+
260+
internal fun CoroutineDispatcher.safeIsDispatchNeeded(context: CoroutineContext): Boolean {
261+
try {
262+
return isDispatchNeeded(context)
263+
} catch (e: Throwable) {
264+
throw DispatchException(e, this, context)
257265
}
258266
}
259267

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,8 @@ internal fun <T> DispatchedTask<T>.dispatch(mode: Int) {
147147
// dispatch directly using this instance's Runnable implementation
148148
val dispatcher = delegate.dispatcher
149149
val context = delegate.context
150-
if (dispatcher.isDispatchNeeded(context)) {
151-
dispatcher.dispatch(context, this)
150+
if (dispatcher.safeIsDispatchNeeded(context)) {
151+
dispatcher.safeDispatch(context, this)
152152
} else {
153153
resumeUnconfined()
154154
}
@@ -213,7 +213,7 @@ internal inline fun Continuation<*>.resumeWithStackTrace(exception: Throwable) {
213213
/**
214214
* This exception holds an exception raised in [CoroutineDispatcher.dispatch] method
215215
*
216-
* @see DispatchedContinuation.dispatchWithExceptionHandling
216+
* @see safeDispatch
217217
*/
218218
internal class DispatchException(
219219
override val cause: Throwable,

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ internal class LimitedDispatcher(
4242

4343
override fun dispatch(context: CoroutineContext, block: Runnable) {
4444
dispatchInternal(block) { worker ->
45-
dispatcher.dispatch(this, worker)
45+
dispatcher.safeDispatch(this, worker)
4646
}
4747
}
4848

@@ -116,10 +116,10 @@ internal class LimitedDispatcher(
116116
}
117117
currentTask = obtainTaskOrDeallocateWorker() ?: return
118118
// 16 is our out-of-thin-air constant to emulate fairness. Used in JS dispatchers as well
119-
if (++fairnessCounter >= 16 && dispatcher.isDispatchNeeded(this@LimitedDispatcher)) {
119+
if (++fairnessCounter >= 16 && dispatcher.safeIsDispatchNeeded(this@LimitedDispatcher)) {
120120
// Do "yield" to let other views execute their runnable as well
121121
// Note that we do not decrement 'runningWorkers' as we are still committed to our part of work
122-
dispatcher.dispatch(this@LimitedDispatcher, this)
122+
dispatcher.safeDispatch(this@LimitedDispatcher, this)
123123
return
124124
}
125125
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ internal class NamedDispatcher(
1212
private val name: String
1313
) : CoroutineDispatcher(), Delay by (dispatcher as? Delay ?: DefaultDelay) {
1414

15-
override fun isDispatchNeeded(context: CoroutineContext): Boolean = dispatcher.isDispatchNeeded(context)
15+
override fun isDispatchNeeded(context: CoroutineContext): Boolean = dispatcher.safeIsDispatchNeeded(context)
1616

17-
override fun dispatch(context: CoroutineContext, block: Runnable) = dispatcher.dispatch(context, block)
17+
override fun dispatch(context: CoroutineContext, block: Runnable) = dispatcher.safeDispatch(context, block)
1818

1919
@InternalCoroutinesApi
2020
override fun dispatchYield(context: CoroutineContext, block: Runnable) = dispatcher.dispatchYield(context, block)

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,8 @@ public fun CoroutineDispatcher.asExecutor(): Executor =
105105

106106
private class DispatcherExecutor(@JvmField val dispatcher: CoroutineDispatcher) : Executor {
107107
override fun execute(block: Runnable) {
108-
if (dispatcher.isDispatchNeeded(EmptyCoroutineContext)) {
109-
dispatcher.dispatch(EmptyCoroutineContext, block)
108+
if (dispatcher.safeIsDispatchNeeded(EmptyCoroutineContext)) {
109+
dispatcher.safeDispatch(EmptyCoroutineContext, block)
110110
} else {
111111
block.run()
112112
}

0 commit comments

Comments
 (0)