diff options
Diffstat (limited to 'ui/kotlinx-coroutines-android/src/HandlerDispatcher.kt')
-rw-r--r-- | ui/kotlinx-coroutines-android/src/HandlerDispatcher.kt | 40 |
1 files changed, 28 insertions, 12 deletions
diff --git a/ui/kotlinx-coroutines-android/src/HandlerDispatcher.kt b/ui/kotlinx-coroutines-android/src/HandlerDispatcher.kt index d693e2bc..ca8dd0d0 100644 --- a/ui/kotlinx-coroutines-android/src/HandlerDispatcher.kt +++ b/ui/kotlinx-coroutines-android/src/HandlerDispatcher.kt @@ -7,8 +7,8 @@ package kotlinx.coroutines.android import android.os.* -import androidx.annotation.* import android.view.* +import androidx.annotation.* import kotlinx.coroutines.* import kotlinx.coroutines.internal.* import java.lang.reflect.* @@ -54,15 +54,22 @@ internal class AndroidDispatcherFactory : MainDispatcherFactory { override fun createDispatcher(allFactories: List<MainDispatcherFactory>) = HandlerContext(Looper.getMainLooper().asHandler(async = true)) - override fun hintOnError(): String? = "For tests Dispatchers.setMain from kotlinx-coroutines-test module can be used" + override fun hintOnError(): String = "For tests Dispatchers.setMain from kotlinx-coroutines-test module can be used" override val loadPriority: Int get() = Int.MAX_VALUE / 2 } /** - * Represents an arbitrary [Handler] as a implementation of [CoroutineDispatcher] + * Represents an arbitrary [Handler] as an implementation of [CoroutineDispatcher] * with an optional [name] for nicer debugging + * + * ## Rejected execution + * + * If the underlying handler is closed and its message-scheduling methods start to return `false` on + * an attempt to submit a continuation task to the resulting dispatcher, + * then the [Job] of the affected task is [cancelled][Job.cancel] and the task is submitted to the + * [Dispatchers.IO], so that the affected coroutine can cleanup its resources and promptly complete. */ @JvmName("from") // this is for a nice Java API, see issue #255 @JvmOverloads @@ -113,7 +120,7 @@ internal class HandlerContext private constructor( * @param handler a handler. * @param name an optional name for debugging. */ - public constructor( + constructor( handler: Handler, name: String? = null ) : this(handler, name, false) @@ -129,24 +136,33 @@ internal class HandlerContext private constructor( } override fun dispatch(context: CoroutineContext, block: Runnable) { - handler.post(block) + if (!handler.post(block)) { + cancelOnRejection(context, block) + } } override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation<Unit>) { val block = Runnable { with(continuation) { resumeUndispatched(Unit) } } - handler.postDelayed(block, timeMillis.coerceAtMost(MAX_DELAY)) - continuation.invokeOnCancellation { handler.removeCallbacks(block) } + if (handler.postDelayed(block, timeMillis.coerceAtMost(MAX_DELAY))) { + continuation.invokeOnCancellation { handler.removeCallbacks(block) } + } else { + cancelOnRejection(continuation.context, block) + } } override fun invokeOnTimeout(timeMillis: Long, block: Runnable, context: CoroutineContext): DisposableHandle { - handler.postDelayed(block, timeMillis.coerceAtMost(MAX_DELAY)) - return object : DisposableHandle { - override fun dispose() { - handler.removeCallbacks(block) - } + if (handler.postDelayed(block, timeMillis.coerceAtMost(MAX_DELAY))) { + return DisposableHandle { handler.removeCallbacks(block) } } + cancelOnRejection(context, block) + return NonDisposableHandle + } + + private fun cancelOnRejection(context: CoroutineContext, block: Runnable) { + context.cancel(CancellationException("The task was rejected, the handler underlying the dispatcher '${toString()}' was closed")) + Dispatchers.IO.dispatch(context, block) } override fun toString(): String = toStringInternalImpl() ?: run { |