ursus
02/23/2026, 1:15 PMclass SerializedGate {
private val mutex = Mutex()
suspend fun run(block: suspend () -> Unit) {
mutex.withLock {
block()
}
}
}
class ClassA(private val gate: SerializedGate) {
suspend fun publicFunctionA() {
gate.run {
// A work
}
}
}
class ClassB(private val gate: SerializedGate) {
suspend fun publicFunctionB() {
gate.run {
// B work
}
}
}
This is what I have and seems to work but I feel odd about it (as I've only used mutex as a internal detail effectively within a single class)
Is this idiomatic?Youssef Shoaib [MOD]
02/23/2026, 1:20 PMrun to something else just because there's the stdlib .run function, and so it might be confusing.
What you've basically done is abstracted over a mutex into a less-powerful construct, which is aboslutely fine!ursus
02/23/2026, 1:23 PMYoussef Shoaib [MOD]
02/23/2026, 1:24 PMursus
02/23/2026, 1:25 PMursus
02/23/2026, 1:25 PMYoussef Shoaib [MOD]
02/23/2026, 1:26 PMMutex is not necessarily thread-related. It actually uses coroutines, while a normal Java-style Lock would not even function properly.ursus
02/23/2026, 1:27 PMclass SerializedGate {
private val mutex = Mutex()
suspend fun run(block: suspend () -> Unit) {
mutex.withLock {
block()
}
}
}
class ClassA(private val gate: SerializedGate) {
suspend fun publicFunctionA() {
gate.run {
val foo = db.loadFoo()
val isSelected = apiCall(foo) <--- suspend between synchornous db read and write
db.updateFoo(foo.id, isSelected)
}
}
}
class ClassB(private val gate: SerializedGate) {
suspend fun publicFunctionB() {
gate.run {
val foo = db.loadFoo()
val isSelected = apiCall(foo) <--- suspend between synchornous db read and write
db.updateFoo(foo.id, isSelected)
}
}
}
+- this is what I'm trying to do
the issue is foo changing between loadFoo and updateFoo
usually i'd stick this in a db transaction - but api call is suspend function which breaks it (db tx are synchronous)ursus
02/23/2026, 1:30 PM