Michael Ferguson
2 min readSep 26, 2022

--

Your example is exactly what I had in my article just with using the main threading that doesn’t really do anything to protect the value. You’re doing a read and write on the main thread. That offers no more protection than my examples on IO or Default. Perhaps your observer is on the main thread. Perhaps it will first do a bunch of mapping off the main thread before finally observing the value on the main thread. Who’s to know?

(Medium replies suck at formatting, sorry in advance.)

This example:

withContext(Dispatchers.Default) {
val calculatedValue = calculate()
withContext(Main) {
uiState.value = uiState.value.copy(calculatedValue = calculatedValue)
}
}

Is really this:

withContext(Dispatchers.Default) {
val calculatedValue = calculate()
withContext(Main) {
val currentValue = uiState.value

// Here is the spot where you can have a concurrency error.

uiState.value = currentValue.copy(calculatedValue = calculatedValue)
}
}

If we elaborate on the contents of the ui state value:

withContext(Dispatchers.Default) {
val calculatedValue = calculate() // lets say calculated value is “foo”
withContext(Main) {
val currentValue = uiState.value
// say the currentValue is the following
// currentValue.propertyA = “Something”
// currentValue.calculatedValue = “Old Value”

// Maybe someone updates the ui state on a different thread.
// Say someone sets another property of the UI state to
// something different like this:
// newUiState.propertyA = “Something else” <— CHANGE!
// newUIState.calculatedValue = “Old Value”
// uiState.value = newUIState
// Now you’re going to save:
// currentValue.propertyA = “Something”
// currentValue.calculatedValue = “foo”
uiState.value = currentValue.copy(calculatedValue = calculatedValue)
// So you’ve just stomped on the change of newUiState.propertyA = “Something else”
}
}

In order for what you proposed to work, you’d have to ensure that 100% of all observers all the way down the chain are on the main. No mapping or other flow intermediates on another thread. You’d also have to ensure that 100% of all setting of the UI state value is on the main thread. That’s a lot of baggage to carry forward forever.

Or you can just be safe and use either a mutex or the update function and allow the flexibility of both observing, even part of the flow, off the main thread and setting the UI state off the main thread.

Edit: formatting

--

--

Michael Ferguson
Michael Ferguson

Written by Michael Ferguson

Android software developer. Views and opinions expressed are my own and not that of my employer.

No responses yet