All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.co.uzzu.kortex.HotInvocation.kt Maven / Gradle / Ivy

There is a newer version: 0.11.0
Show newest version
package co.uzzu.kortex

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.BroadcastChannel
import kotlinx.coroutines.channels.broadcast
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlin.coroutines.CoroutineContext

/**
 * Coroutine context element by using CoroutineScope#withHot
 */
@ExperimentalCoroutinesApi
interface HotInvocation : CoroutineContext.Element {
    override val key: CoroutineContext.Key<*> get() = Key

    companion object Key : CoroutineContext.Key

    val mutex: Mutex

    val map: MutableMap>
}

/**
 * Create a new HotInvocation object
 * @param mutex
 * @return A new HotInvocation object
 */
@ExperimentalCoroutinesApi
fun hotInvocation(
    mutex: Mutex = Mutex(),
    map: MutableMap> = mutableMapOf()
): HotInvocation =
    HotInvocationImpl(mutex, map)

/**
 * Hot-invoke specified suspending function by unique key
 * @param key unique key to use hot-invoke a specified suspending function
 * @param block suspending function to invoke
 * @return same value if specified suspend function was reused
 * @throws IllegalArgumentException if coroutineContext[HotInvocation] was not set.
 */
@ExperimentalCoroutinesApi
@Suppress("SuspendFunctionOnCoroutineScope")
suspend fun  CoroutineScope.withHot(key: String, block: suspend () -> T): T {
    val invocation = requireNotNull(coroutineContext[HotInvocation]) {
        "Requires HotInvocation to call this function. Please add into your coroutineContext."
    }
    val mutex = invocation.mutex
    val map = invocation.map
    return mutex.withLock {
        if (map.containsKey(key) && !requireNotNull(map[key]).isClosedForSend) {
            @Suppress("unchecked_cast")
            val cached = map[key] as BroadcastChannel
            return@withLock cached.openSubscription()
        }

        map.remove(key)
        @Suppress("unchecked_cast")
        val created = map.getOrPut(key) {
            broadcast {
                val result = block()
                mutex.withLock {
                    send(result)
                    map.remove(key)
                }
            }
        } as BroadcastChannel
        created.openSubscription()
    }.receive()
}

@ExperimentalCoroutinesApi
private class HotInvocationImpl(
    override val mutex: Mutex,
    override val map: MutableMap>
) : HotInvocation




© 2015 - 2024 Weber Informatics LLC | Privacy Policy