Description
There is a section in coroutines guide "Shared mutable state and concurrency". Inside there is a statement, that accessing shared mutable state from multiple coroutines executed in a multithreaded Dispatcher is not safe. That is entirely correct. However accessing shared mutable state by multiple coroutines confined to a single thread (like Dispatchers.Main) is not really safe too.
Consider this example:
class SomeClass {
var lastUsedUserId
suspend fun loadUser(): User {
if(lastUsedUserId != getCurrentUserId() ) {
val newUser = callSuspendFun()
lastUserId = newUser.id
return newUser
}
else doSomethingElse()
}
}
If fun loadUser()
was a normal function && it was always called on same thread - this would be an ok code. We check whether user has changed, fetch its data and cache new user id for further invocations.
Now e.g. all default coroutineScopes on Android (viewmodelScope, livedataScope, etc) operate on Dispatchers.Main. So everything in this suspend fun should run on single, main thread, and should work, as if it were normal function, right?
No.
suspend fun loadUser()
may... suspend. And in the meantime another, MainThread-bound coroutine may invoke this function again. And possibly finish faster than first invocation, messing up our userId caching policy. Or any other mutable state, shared between coroutines - whether they are bound to multiple threads or only single-threaded.
I would mention in a guide, that accessing mutable state shared between corotuines / suspend functions is generally dangerous, no matter whether we operate on multithreaded Dispatcher, or single-threaded.