Skip to content

Unintuitive dispatching behavior in some rare cases with Android's Dispatchers.Main.immediate #3963

Closed
@dkhalanskyjb

Description

See #3924 (comment)

This code produces 1, 2, 3:

@Test
fun foo() {
    GlobalScope.launch(Dispatchers.IO) {
        withContext(Dispatchers.Main.immediate) {
            println("1")
            launch(Dispatchers.Main) {
                println("2")
            }
            withContext(Dispatchers.Main) {
                println("3")
            }
        }
    }
}

This code, however, produces 1, 3, 2:

@Test
fun foo() {
    GlobalScope.launch(Dispatchers.Main) { // <-- note the different dispatcher
        withContext(Dispatchers.Main.immediate) {
            println("1")
            launch(Dispatchers.Main) {
                println("2")
            }
            withContext(Dispatchers.Main) {
                println("3")
            }
        }
    }
}

This behavior may be surprising. For example, consider this pseudo-code:

fun updateUi() = scope.launch(Dispatchers.Main) { }

suspend fun readUiElement(): Int = withContext(Dispatchers.Main) { 1 }

fun update() = viewModelScope.launch {
  updateUi()
  cache = readUiElement()
}

Depending on whether update is called from the main thread, the block in updateUi will happen either earlier or later than readUiElement, even though visually, it seems like updateUi should happen earlier.

Notably, all the other implementations of Dispatchers.Main that we provide don't suffer from the same problem but instead consistently output 1, 2, 3 in both scenarios.

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions