zak.taccardi
05/06/2020, 5:55 PMAtomicRef
on numberOfTimesUserHasChanged
because it’s guaranteed to be executed in the same coroutine?
My concern is that currentUserIdFlow
could switch threads/coroutines internally, but I think that would not be able to affect the coroutine in which numberOfTimesUserHasChanged
is read and written from?fatih
05/06/2020, 6:03 PMnumberOfTimesUserHasChanged
always going to be changed through Flow onEach?Zach Klippenstein (he/him) [MOD]
05/06/2020, 6:13 PMflowOn
) in between onEach
and collect
that could introduce parallelism and thus a race. Since ++
is an expression that returns a value, you could turn this into:
currentUserIdFlow.map { number++ }
.collect(::updateUi)
Although that’s still a little gross, because it’s using side effects. A more pure implementation would be:
currentUserIdFlow
.scan(0) { count, _ -> count + 1 }
.drop(1)
.collect(::updateUi)
zak.taccardi
05/06/2020, 6:16 PMThis is a weird way to write this logic thoughyeah it’s just an example, not something I’m actually using.
you could technically introduce an operator (e.g. flowOn) in between onEach and collect that could introduce parallelism and thus a race.definitely
zak.taccardi
05/06/2020, 6:16 PMIs numberOfTimesUserHasChanged always going to be changed through Flow onEach?yeah, it would always be changed/accessed from the same coroutine
zak.taccardi
05/06/2020, 6:17 PMzak.taccardi
05/06/2020, 6:17 PMZach Klippenstein (he/him) [MOD]
05/06/2020, 6:37 PM@Volatile
, but I can’t recall if that’s a thing for local vars vs fields.zak.taccardi
05/06/2020, 6:38 PM@Volatile
doesn’t need to be used for properties that are written/read from the same coroutinefatih
05/06/2020, 6:39 PMflowOn
is used, there will be no race since the value is only changed in intermediate operators (onEach) but not terminal operatorsfatih
05/06/2020, 6:39 PMzak.taccardi
05/06/2020, 6:40 PMflowOn
would change the dispatcher not the coroutinefatih
05/06/2020, 6:40 PMzak.taccardi
05/06/2020, 6:42 PM.collect()
onefatih
05/06/2020, 6:44 PMfatih
05/06/2020, 6:45 PMzak.taccardi
05/06/2020, 6:45 PMzak.taccardi
05/06/2020, 6:46 PMzak.taccardi
05/06/2020, 6:46 PM.flatMap { .. }
do I break it?fatih
05/06/2020, 6:48 PMzak.taccardi
05/06/2020, 6:48 PMzak.taccardi
05/06/2020, 6:48 PMfatih
05/06/2020, 6:50 PMzak.taccardi
05/06/2020, 6:50 PMzak.taccardi
05/06/2020, 6:50 PMflowOn
because I don’t see the pointzak.taccardi
05/06/2020, 6:51 PMfatih
05/06/2020, 6:51 PMzak.taccardi
05/06/2020, 6:52 PMI don’t ever use flowOn because I don’t see the pointI execute in the default dispatcher by default, otherwise I use
withContext(..)
in any suspending functions that require the non-default dispatcherzak.taccardi
05/06/2020, 6:53 PMThen it is safe since all the work will be sequentially executedeven if you are sequential across different coroutines, the code isn’t thread safe. For example if you have two suspending functions that utilize a mutex and update that shared state, I think you still need
@Volatile
or a lock object on that shared state IIRCfatih
05/06/2020, 6:55 PMzak.taccardi
05/06/2020, 6:55 PMzak.taccardi
05/06/2020, 6:56 PMfatih
05/06/2020, 6:58 PMzak.taccardi
05/06/2020, 6:59 PMFlow<T>
runs in a single coroutine by default?fatih
05/06/2020, 7:01 PMLuis Munoz
05/06/2020, 9:16 PMzak.taccardi
05/06/2020, 9:17 PMzak.taccardi
05/06/2020, 9:17 PMzak.taccardi
05/06/2020, 9:17 PMLuis Munoz
05/06/2020, 9:29 PMfun main() {
val latch = CountDownLatch(1)
GlobalScope.launch(<http://Dispatchers.IO|Dispatchers.IO>) {
val threadId = Thread.currentThread().id
println("${Thread.currentThread()}")
repeat(1000) {
if(Thread.currentThread().id != threadId) {
println("WRONG THREAD")
throw RuntimeException("wrong thread")
}
delay(1)
}
latch.countDown()
}
latch.await(2, TimeUnit.SECONDS)
}
Luis Munoz
05/06/2020, 9:29 PMLuis Munoz
05/06/2020, 9:30 PMzak.taccardi
05/06/2020, 9:30 PMLuis Munoz
05/06/2020, 9:31 PMzak.taccardi
05/06/2020, 9:31 PMzak.taccardi
05/06/2020, 9:31 PMLuis Munoz
05/06/2020, 9:35 PMLuis Munoz
05/06/2020, 9:36 PMLuis Munoz
05/06/2020, 9:44 PMLuis Munoz
05/06/2020, 9:45 PMLuis Munoz
05/06/2020, 9:45 PMLuis Munoz
05/06/2020, 9:46 PMLuis Munoz
05/06/2020, 9:46 PMfun main() {
val latch = CountDownLatch(2)
runBlocking {
var myInt = 1
GlobalScope.launch(<http://Dispatchers.IO|Dispatchers.IO>) {
repeat(1000) {
myInt += 1
delay(1)
myInt -= 1
if (myInt != 1) {
println("ERROR")
throw RuntimeException("wrong count")
}
}
latch.countDown()
}
GlobalScope.launch(<http://Dispatchers.IO|Dispatchers.IO>) {
repeat(100000) {
myInt += 1
delay(1)
myInt -= 1
if (myInt != 1) {
println("ERROR")
throw RuntimeException("wrong count")
}
}
latch.countDown()
}
}
latch.await(2, TimeUnit.SECONDS)
}
zak.taccardi
05/06/2020, 9:55 PMmyInt
between the twozak.taccardi
05/06/2020, 9:55 PMzak.taccardi
05/06/2020, 9:56 PMelizarov
05/07/2020, 7:13 AM