I have caches that are built (and rebuilt) in full...
# codereview
d
I have caches that are built (and rebuilt) in full, but may take a bit of time during generation. I need all coroutines that access the cache to suspend until the cache is built. My current solution looks like this:
Copy code
class DelayUntilReady<T>(private val builder: () -> T) {
    private companion object {
        enum class State { READY, WAIT }
    }

    private val channel: Channel<Unit> = Channel(0)
    private var state: State = State.WAIT

    fun setReady() {
        state = State.READY
        channel.offer(Unit)
    }

    fun setWait() {
        state = State.WAIT
    }

    suspend fun get(): T {
        while (state == State.WAIT) channel.receive()
        return builder.invoke()
    }
}


object Cache {
    private var elements: Map<String, String> = emptyMap()
    private val elementAwait = DelayUntilReady { elements }
    
    @Suppress("FunctionName")
    suspend fun Elements(): Map<String, String> = elementAwait.get()

    fun setupElements(builder: () -> Map<String, String>) {
        elementAwait.setWait()
        // parse
        elements = builder.invoke()

        elementAwait.setReady()
    }
}
Did I miss something, or is there a better way to write this? Please note that i will have around 4-5 of these caches in the
Cache
object, not just one as in the example.
z
d
I had problems with a mutex implementation in the past due to having to leave a mutex locked when leaving scope and unlocking in a different scope and coroutine, but I have tried to mitigate this now with two mutexes as well as making use of owners. How does this look?
Copy code
class Container<K, V> {
    private var container: Map<K, V> = emptyMap()
    private val init: Mutex = Mutex()
    private val access: Mutex = Mutex().apply { tryLock(init) }

    suspend fun initialize(builder: () -> Map<K, V>): Unit = init.withLock {
        if (!access.holdsLock(init)) access.lock(init)
        container = builder.invoke()
        access.unlock(init)
    }

    suspend fun invalidate(): Unit = init.withLock {
        if (!access.holdsLock(init)) access.lock(init)
        container = emptyMap()
    }

    suspend fun retrieve(key: K): V? = access.withLock(container) { container[key] }
}
z