Reputation: 854
I have the following code which has a race condition. I try to find an item in a list and set its loading property. But if onLoaded("A")
and onLoaded("B")
are called multiple times from different threads. I always lose the data of the first call if it doesn't complete before second starts.
How can I make this work? Is using Mutex should be the correct approach?
val list = MutableLiveData<List<Model>>() // assume this is initialized with ["Model(false, "A"), Model(false, "B")]
data class Model(
val loaded: Boolean,
val item: String,
)
fun onLoaded(item: String) = viewModelScope.launch {
val currList = list.value ?: return@launch
withContext(Dispatchers.Default) {
val updated = currList.find { it.item == item }?.copy(loaded = true)
val mutable = currList.toMutableList()
updated?.let {
val index = mutable.indexOf(it)
mutable[index] = it
}
list.postValue(mutable.toList())
}
}
onLoaded("A")
onLoaded("B")
expected: ["Model(true, "A"), Model(true, "B")]
actual: ["Model(false, "A"), Model(true, "B")]
Upvotes: 0
Views: 1456
Reputation: 30605
In onLoaded()
a new coroutine is launched using viewModelScope
. viewModelScope
has Dispatchers.Main.immediate
context, so the code inside it will be executed on the Main Thread, e.g. execution is limited to only one thread. The reason you have a Race Condition because calling the onLoaded()
function consecutively doesn't guarantee the order of coroutines execution.
If you call onLoaded()
consecutively from one thread I suggest to remove launching a coroutine viewModelScope.launch
in it. Then the order of calling will be preserved. Use list.postValue()
in this case.
If you call onLoaded()
from different threads and still want to launch a coroutine you can refer to answers to this question.
Try to use @Synchronized
anotation without launching a coroutine:
@Synchronized
fun onLoaded(item: String) { ... }
Method will be protected from concurrent execution by multiple threads by the monitor of the instance on which the method is defined. Use list.postValue()
in this case.
Upvotes: 1