Is there any way to force a dispatch when using `D...
# coroutines
a
Is there any way to force a dispatch when using
Dispatchers.Main.immediate
?
yield()
will not do anything.
delay(1)
works but is kind of ugly
o
Seems counter-productive -- why not use
Dispatchers.Main
?
a
Because I don’t want other resumes to be deferred
Specifically I need to make something happen on the next frame on Android
s
Like a
post
of a Runnable, but putting in front of the queue?
o
maybe
withContext(Dispatchers.Main)
then?
a
Yeah, I suppose that would work. Now I implemented a general purpose version:
Copy code
suspend fun defer() {
    val dispatcher = coroutineContext[ContinuationInterceptor] as? CoroutineDispatcher
    if (dispatcher == null) {
        Timber.w("No coroutine dispatcher found, using delay instead")
        delay(1)
    } else {
        suspendCancellableCoroutine { cont ->
            dispatcher.dispatch(EmptyCoroutineContext, Runnable { cont.resume(Unit) })
        }
    }
}
s
This looks fragile to me. It requires hidden/private knowledge on how delay and dispatcher.dispatch work...
a
What do you mean, they are both public and well documented
s
What if 1 millisecond is too long? What if dispatcher.dispatch runs the provided Runnable immediately (in case
defer
is called on the main thread)? I'd write something myself to make the synchronization you need more explicit instead of relying on delay or dispatcher.dispatch.
a
dispatcher.dispatch
will always post it to the queue, even on the immediate dispatcher
1ms cannot be too long since a frame is 16ms
s
For now.... 😀 What are you trying to sequence/synchronize?
a
So my code looks like this essentially:
Copy code
viewCreatedScope.launch {
  view.awaitPredraw()
  // Setup animation
  defer()
  TransitionManager.beginDelayedTransition(...)
  // Set to final state
}
o
I suspect that you really don't need
immediate
-- any suspend point that actually suspends will still re-dispatch, since coroutine suspension removes the code from the thread, and any point that doesn't suspend will continue in the same thread regardless of
immediate
s
Also, https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-dispatcher/dispatch.html It doesn't promise to post the Runnable to a queue... It may choose a short path in case the calling thread is already on the dispatcher's thread....
o
immediate
only affects the initial coroutine launch
1
a
It wouldn’t work without
immediate
since it would actually draw the frame where i want to set up the animation
And it’s not true that it only affects the launch, it won’t dispatch if you resume while on the main thread
Here is the implementation of
resumeWith
for a coroutine dispatcher:
Copy code
override fun resumeWith(result: Result<T>) {
        val context = continuation.context
        val state = result.toState()
        if (dispatcher.isDispatchNeeded(context)) {
            _state = state
            resumeMode = MODE_ATOMIC_DEFAULT
            dispatcher.dispatch(context, this)
        } else {
            executeUnconfined(state, MODE_ATOMIC_DEFAULT) {
                withCoroutineContext(this.context, countOrElement) {
                    continuation.resumeWith(result)
                }
            }
        }
    }
s
I'd use the Handler's or View's
post
methods for sync, not relying on the Coroutine Dispatchers. You can the wrap that in a suspend function called defer()
a
And
isDispatchNeeded
would return
false
if called on the main thread when using the immediate dispatcher
Fair enough, but then
defer
could pretty much only be called on the main thread
But I suppose a better name would help
💯 1
s
You can call View's or Handler's
post
from any thread.
a
I know, but if you’re running on an IO thread it would be weird to call
defer
s
Yup, true... A different name would be better 😀
l
I think
yield()
should support the
immediate
dispatcher. You should open an issue on GitHub.
💯 1
d
Would CoroutineStart.UNDISPATCHED help you?