I want to ask about thread safety of state flow im...
# coroutines
u
I want to ask about thread safety of state flow imperative reading/writing
Copy code
private val state = MutableStateFlow(IDLE)

fun doSomething() {
    if (state.value == LOADING) return
    state.value = LOADING
    scope.launch {
        try {
            actuallyDoSomething()
            state.value = SUCCESS
        } catch(ex: Exception) {
            state.value = ERROR
        }
    }
}
i.e. this is not guaranteed to be only single execution, right? I need to make reading & writing of LOADING synchronized, correct?
g
single execution of what?
it’s just shared mutable state, if state would be not Flow<State>, but just State
so all usual thread-safety techniques are applied here
so your current doSomething is not thread safe
and Flow would be able to make your code thread safe even in theroy, because you have 2 operations: value reading and value writing, they cannot be atomic
but there is atomic version of set for MutableStateFlow, which allows set value atomically with comparation
u
right, that kind of atomicity is what I need I see this is similar to AtomicInteger api, but I never really used that other than simple counters, so a stupid question, how do I use the
compareAndSet
if I need the first param to be a negation (since its a enum)?
Copy code
i.e. state.compareAndSet(expected = NOT Loading, new = Loading)
basically to emulate this
Copy code
synchonized(state) {
   if (state.value != LOADING) {
      state.value = LOADING
      ...
   }
}
Do I need to write my own primitive with the usual neverending loop etc?
g
yeah, you can use pattern with infinite loop for this
when you get current value and set it as “expected”, retry if set failed (or return if it already Loading
u
If I may revive this
Copy code
fun <T> MutableStateFlow<T>.compareNotAndSet(compareNot: T, new: T) {
    do {
        val value = value
        if (value == compareNot) break
    } while (!compareAndSet(value, new))
}
came up with this, but im not super sure if its correct I was searching for AtomicReference, but they also only have compareAndSet, which sets if the value is the same -- which I have no idea how is that useful, but nevermind
Btw looking at the source of MutableStateFlow.compareAndSet ..they use synchrinzed block as well, I dont really see the CAS, is it just for compiler to optimize magically? If so, I might have just wrote
Copy code
fun <T> MutableStateFlow<T>.setIfNotEqual(newValue: T): Boolean {
    synchronized(this) {
        if (value != newValue) {
            value = newValue
            return true
        } else {
            return false
        }
    }
}
Which my non-CAS brain reads more easily