https://kotlinlang.org logo
Title
j

juliocbcotta

05/03/2023, 8:34 AM
Hey, I am trying to create a in memory cache with
Mutex
... I am using the mutex in multiple methods... but I am not sure about the behaviour...
private val cache = MutableStateFlow(emptySet<String>())
    private val mutex = Mutex()
    suspend fun add(brand: Favorite) {
        mutex.withLock {
            val favorites = cache.value.toMutableSet()
            favorites.add(brand.brandId)
            cache.emit(favorites.toSet())
        }
    }

    suspend fun remove(brand: Favorite) {
        mutex.withLock {
            val favorites = cache.value.toMutableSet()
            favorites.remove(brand.brandId)
            cache.emit(favorites.toSet())
        }
    }
    
    suspend fun set(favorites: Collection<Favorite>) {
        mutex.withLock {
            cache.emit(favorites.map { it.brandId }.toSet())
        }
    }
Can I say that this code is secure from concurrency point of view ? I mean, I am locking the access in different
suspended
methods using the same
mutex
.
t

Tijl

05/03/2023, 8:55 AM
everything inside
mutex.withLock
will only run 1 at the same time yes, it’s governed by the instance of
Mutex
, which suspend method etc you use does not matter. you might want to check https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/get-and-update.html though, would not need a Mutex in that case
e

ephemient

05/03/2023, 8:57 AM
you don't even need the "and get" part,
cache.update { it + brand.brandId }
cache.update { it - brand.brandId }
cache.value = buildSet { favorites.mapTo(this) { it.brandId } }
is sufficient to match behavior
j

juliocbcotta

05/03/2023, 8:57 AM
that is amazing! Thanks
t

Tijl

05/03/2023, 8:59 AM
yeah or just
update
depending on your exact usecase
e

ephemient

05/03/2023, 9:05 AM
a different implementation using mutex could be
private val cache = mutableSetOf<String>()
private val _flow = MutableStateFlow(cache.toSet())
val flow: StateFlow<String>
    get() = _flow.asStateFlow()

suspend fun add(brand: Favorite) {
    mutex.withLock {
        cache.add(brand.brandId)
        _flow.value = cache.toSet()
    }
}
etc. but for this use case that pattern is not necessary
j

juliocbcotta

05/03/2023, 9:10 AM
thanks everyone! My cache is way simpler now