https://kotlinlang.org logo
#coroutines
Title
# coroutines
u

ursus

10/17/2020, 5:07 AM
Is low level synchronized block still cool in coroutines? The usual copy load+save use case... Or maybe updating StateFlow instance via reducing of the current value + change
m

Marc Knaup

10/17/2020, 5:15 AM
Depends on what you are trying to achieve.
l

louiscad

10/17/2020, 5:33 AM
synchronized
over suspension points is not cool (expect issues), and should be replaced by
Mutex
, but otherwise, it's fine in the JVM.
e

ephemient

10/17/2020, 6:50 AM
@Synchronized
just sets a flag on the method in the classfile, so it probably doesn't cause anything to break, but it won't work either; as far as the JVM is concerned, the function is being called and is returning at every suspension point
synchronized(lock)
translates to monitorenter and monitorexit bytecode instructions. that will be problematic for sure as the object will stay locked on the original thread, but the coroutine may not be resumed there
u

ursus

10/17/2020, 12:15 PM
Yes I should have been clearer. Its only blocking code within the synchronized block
Copy code
synchronized {
   stateFlow.value = stateFlow.value.copy(foo = bar)
}
(stateflow syntax might be incorrect, im new to it)
z

Zach Klippenstein (he/him) [MOD]

10/17/2020, 2:42 PM
You don't need a lock for that,
MutableStateFlow
has a
compareAndSet
method you can use for lock-free atomic updates: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-mutable-state-flow/compare-and-set.html
👍 1
e

ephemient

10/17/2020, 11:03 PM
also, if you're trying to handle updates coming from multiple sources, maybe you could try to process them in a single coroutine, effectively serializing them
u

ursus

10/18/2020, 12:28 AM
@Zach Klippenstein (he/him) [MOD] ehm I might be confused but how would I use that? Id expect something like this
Copy code
val mutableStateFlow = MutableStateFlow(State())
mutableStateFlow.set {
    copy(counter + 1)
}

data class State(counter: Int = 0)
and have the set function be thread safe
z

Zach Klippenstein (he/him) [MOD]

10/18/2020, 12:33 AM
Not hard to write that function yourself:
Copy code
lateinit var old: YourState
lateinit var new: YourState
do {
  old = mutableStateFlow.value
  new = old.copy(foo = bar)
} while (!mutableStateFlow.compareAndSet(old, new))
u

ursus

10/18/2020, 12:36 AM
yea I had this so far
Copy code
fun <T> MutableStateFlow<T>.set(reduce: T.() -> T) {
    synchronized(this) {
        value = value.reduce()
    }
}
so the difference is the classic lock vs lockfree concurrency thing? nothing coroutine about this right?
z

Zach Klippenstein (he/him) [MOD]

10/18/2020, 12:40 AM
But using a mutex is probably simpler
u

ursus

10/18/2020, 12:41 AM
thread mutex or coroutine Mutex?
z

Zach Klippenstein (he/him) [MOD]

10/18/2020, 12:53 AM
Coroutine mutex
u

ursus

10/18/2020, 12:56 AM
hmm why, does it matter? is it to make it multiplatform friendly in future?
l

louiscad

10/18/2020, 2:41 AM
@ursus Threads take up space in memory (RAM for their heap and stack), so blocking them just to wait is wasting them.
u

ursus

10/18/2020, 2:42 AM
im aware thanks, this is however just a simple assigment just need to make sure everyone sees the latest value before updating it, bit of a overkill to use some highlevel coroutine object, no?
l

louiscad

10/18/2020, 3:00 AM
@Zach Klippenstein (he/him) [MOD] Other reasons to pick coroutines mutex over java.util.concurrent mutex?
e

ephemient

10/18/2020, 4:16 AM
do not use java.util.concurrent locks in a coroutine. it'll block instead of suspending
and if you're talking about coroutines versus traditional concurrency, I think the overall benefits of structured concurrency are clear
u

ursus

10/18/2020, 5:17 AM
Im almost sure all sqlite libraries will still block transactions blocks on thread level regardless, I dont this its useless
e

ephemient

10/18/2020, 7:05 AM
assuming you're talking about java.util.concurrent.locks.ReentrantLock (there's no standard type specifically called Mutex), you also can't use it because it must be unlocked on the same thread that it was locked on
the coroutine dispatcher will not necessarily resume your coroutine on the same (java) thread. same problem as monitorenter/monitorexit
u

ursus

10/18/2020, 3:24 PM
No im talking about synchronized block. Underlying dispatcher thread will get blocked and wait as usual. Theoretically less performant but will work correctly, no?
e

ephemient

10/20/2020, 8:05 AM
no, even with a synchronized block, as soon as you hit a suspension point, the actual JVM function returns
(even though it doesn't look like it looking at the kotlin suspend fun)
actually playing around with it now, it looks like the compiler actually prevents you from doing that altogether
Copy code
suspend fun foo(lock: Any, block: suspend () -> Unit) {
    synchronized(lock) {
        block()
    }
}
Copy code
foo.kt:3:9: error: the 'invoke' suspension point is inside a critical section
        block()
        ^
u

ursus

10/25/2020, 2:12 PM
I mean if sychnronization block doesnt work with coroutines, then how can all the java concurent data structures work? im sure its not just lockless threadsafety in all of them
e

ephemient

10/25/2020, 2:31 PM
atomic references work
and it shouldn't be a problem in pure java code that doesn't have suspension points in it
u

ursus

10/25/2020, 2:36 PM
even the basic example in the official coroutine samples uses AtomicInteger to count emits of parallel coroutines and says to use this if possible
e

ephemient

10/25/2020, 2:37 PM
atomic doesn't use synchronization blocks
u

ursus

10/25/2020, 2:42 PM
sure but ConcurrentHashMap does
e

ephemient

10/25/2020, 4:13 PM
ConcurrentHashMap can't possibly have any suspend points while in the middle of a synchronized block
9 Views