Hi everyone, I recently saw an article arguing tha...
# coroutines
g
Hi everyone, I recently saw an article arguing that updating the state flow in this particular case is a race condition because we read and write the state in the flow but not atomically. Given that the viewmodel scope is always using the main thread and there is no suspension point after the list is retrieved can there be a race condition ?
Copy code
private fun editListA() {
        viewModelScope.launch {
            val newList = Api.getList() //suspending
            mutableState.value = mutableState.value.copy(
                listA = newList
            )
        }
    }

private fun editListB() {
        viewModelScope.launch {
            val newList = Api.getList() //suspending
            mutableState.value = mutableState.value.copy(
                listB = newList
            )
        }
    }
Also claude code and chatgpt is trying to convince me that there is a race condition but I still can't understand why it would be. Article link
s
It's not a race condition in the sense that a variable/structure is updated at the same time, in parallel. This is a data synchronization issue, very similar to what can happen to a database when two writers are updating it concurrently.
g
I would understand it being a data race if we have two threads or some suspension point in between the read and write, but in the particular case after calling the suspending function getList(), either the editListA() or the editListB() will resume and then run completely the read and write to the mutableState. They can't resume at the same time, at least base on my understanding of coroutines.
s
They can resume at the same time. Say editListA and, by chance, its getList() is called first at T and it suspends there (calling some remote server and waiting for a response) The editListB is called and then getList() is called at T+1 and it suspends there as well. Then when the getList() calls resume, the last one to resume wins... If you want synchronization, so that they are in sequence and not concurrent, use a
Mutex
.
g
Sure they can both be suspending at the same time, but how can both resume at the same time if we have a single thread, where do they run to resume concurrently 😕 ?
s
True, they won't resume at the same time in parallel, but the last one to resume (and complete the coroutine) wins. And the Mutex would only be needed for Dispatchers with pool of multiple threads.
g
Indeed if we have a pool of thread a mutex, or an atomic operation would be necessary. Here though if both are suspending, then editListA resumes reads the mutableState and updated it, then editListB resumes reads the state(Its already updated by the editListA) and then updates it. So no information can be lost.
s
In your example it's not that bad. The last one to resume wins, the first one is just "skipped" But if the mutableState.value needed to be used as input to the getList call, the synchronization could possibly become a bit more complicated.
g
Maybe I can try to write a test to see if it's triggered somehow 😅 True this would be a different case, and there it would be a race condition.
s
But in short, with a one thread pool Dispatcher, a true race condition is not possible. 😀 But only if you are sure that your suspend code is always called from such a dispatcher.
👍 1
g
Yeap at least in our case it's a single thread dispatcher, thanks for the chat 🙂