https://kotlinlang.org logo
#coroutines
Title
# coroutines
a

ansman

08/26/2019, 12:37 AM
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

octylFractal

08/26/2019, 12:42 AM
Seems counter-productive -- why not use
Dispatchers.Main
?
a

ansman

08/26/2019, 12:42 AM
Because I don’t want other resumes to be deferred
Specifically I need to make something happen on the next frame on Android
s

streetsofboston

08/26/2019, 12:43 AM
Like a
post
of a Runnable, but putting in front of the queue?
o

octylFractal

08/26/2019, 12:43 AM
maybe
withContext(Dispatchers.Main)
then?
a

ansman

08/26/2019, 12:44 AM
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

streetsofboston

08/26/2019, 12:50 AM
This looks fragile to me. It requires hidden/private knowledge on how delay and dispatcher.dispatch work...
a

ansman

08/26/2019, 12:51 AM
What do you mean, they are both public and well documented
s

streetsofboston

08/26/2019, 12:55 AM
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

ansman

08/26/2019, 12:55 AM
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

streetsofboston

08/26/2019, 12:57 AM
For now.... 😀 What are you trying to sequence/synchronize?
a

ansman

08/26/2019, 12:58 AM
So my code looks like this essentially:
Copy code
viewCreatedScope.launch {
  view.awaitPredraw()
  // Setup animation
  defer()
  TransitionManager.beginDelayedTransition(...)
  // Set to final state
}
o

octylFractal

08/26/2019, 1:00 AM
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

streetsofboston

08/26/2019, 1:00 AM
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

octylFractal

08/26/2019, 1:00 AM
immediate
only affects the initial coroutine launch
1
a

ansman

08/26/2019, 1:01 AM
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

streetsofboston

08/26/2019, 1:03 AM
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

ansman

08/26/2019, 1:03 AM
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

streetsofboston

08/26/2019, 1:05 AM
You can call View's or Handler's
post
from any thread.
a

ansman

08/26/2019, 1:05 AM
I know, but if you’re running on an IO thread it would be weird to call
defer
s

streetsofboston

08/26/2019, 1:06 AM
Yup, true... A different name would be better 😀
l

louiscad

08/26/2019, 6:55 AM
I think
yield()
should support the
immediate
dispatcher. You should open an issue on GitHub.
💯 1
d

David Glasser

08/26/2019, 7:04 PM
Would CoroutineStart.UNDISPATCHED help you?
3 Views