question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Android: Main.immediate causes a dispatch using Unconfined inside

See original GitHub issue

Dispatchers.Main.immediate forces child coroutine with Dispatchers.Unconfined interceptor to dispatch.

Example code

GlobalScope.launch(Dispatchers.Main.immediate) {
    GlobalScope.launch(Dispatchers.Unconfined) {
        println("$coroutineContext: before")
    }
    println("$coroutineContext: after")
}

This code prints

[CoroutineId(1), "coroutine#1":StandaloneCoroutine{Active}@229841e, Main [immediate]]: after
[CoroutineId(2), "coroutine#2":StandaloneCoroutine{Active}@f2294ff, Unconfined]: before

But expected would be backwards.

I can force not to dispatch if I use start = CoroutineStart.UNDISPATCHED. Then I get the expected result

[CoroutineId(4), "coroutine#4":StandaloneCoroutine{Active}@229841e, Unconfined]: before
[CoroutineId(3), "coroutine#3":StandaloneCoroutine{Active}@f2294ff, Main [immediate]]: after

But shouldn’t GlobalScope.launch(Dispatchers.Unconfined) and GlobalScope.launch(start = CoroutineStart.UNDISPATCHED, context = Dispatchers.Unconfined) be interchangable?

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:6 (3 by maintainers)

github_iconTop GitHub Comments

2reactions
qwwdfsadcommented, Nov 21, 2019

It is a mix of “immediate” interaction with channel unfairness (#111). Two solutions here:

  1. Yielding after send to make sending to the channel fair
  2. Launching sender on main dispatcher and receiver on immediate one:
GlobalScope.launch(Dispatchers.Main) {
    val c = Channel<Int>()
    launch(Dispatchers.Main.immediate) {
        c.consumeAsFlow()
                .collect {
                    println(it)
                }
    }
    launch {
        c.send(1)
        c.send(2)
   }
}

No way to force this behaviour on the level of a dispatcher, because it’s easy to make a mistake here e.g. during code evolution, not at the moment of writing, thus we don’t endorse this. In theory, a sophisticated user can write its own dispatcher around Dispatchers.Main that behaves in a way you want, but we neither advocate it nor have an extensive test suite for such dispatchers (thus it may break in weird ways)

0reactions
vganincommented, Nov 21, 2019

Btw is there a way to force a non-dispatch in cases where it is known that no stack overflow can occur (I mean within reasonable limits). For example, if I have two coroutines but the data flows only one way. In that case dispatch is superfluous (and can create data races). Look at the following example:

GlobalScope.launch(Dispatchers.Main.immediate) {
    val c = Channel<Int>()
    launch {
        c.consumeAsFlow()
                .collect {
                    println(it)
                }
    }
    launch {
        c.send(1)
        c.send(2)
    }
}

I would like to force such statement execution order:

c.send(1)
println(1)
c.send(2)
println(2)

Instead I always get

c.send(1)
c.send(2)
println(1)
println(2)
Read more comments on GitHub >

github_iconTop Results From Across the Web

Kotlin coroutine unit test fails with "Module with the Main ...
A coroutine dispatcher that is not confined to any specific thread. It executes the initial continuation of a coroutine in the current call- ......
Read more >
Testing Kotlin coroutines on Android
In local unit tests, the Main dispatcher that wraps the Android UI thread will be unavailable, as these tests are executed on a...
Read more >
Kotlin Coroutines dispatchers - Kt. Academy
Dispatchers in Kotlin Coroutines are a similar concept to RxJava Schedulers. ... On Android, we typically use the Main dispatcher as the default...
Read more >
Demystifying The Kotlin Coroutine Dispatchers - Medium
This means that a coroutine launched with the Dispatchers.Unconfined dispatcher may run on any thread. This can be useful in particular ...
Read more >
Coroutines Dispatchers.Default and Dispatchers.IO ...
Therefore, by arguing that standard background dispatchers in ... currentTimeMillis() // use unconfined dispatcher to make sure that all ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found