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

tech.harmonysoft.oss.common.execution.ExecutionContextManager.kt Maven / Gradle / Ivy

package tech.harmonysoft.oss.common.execution

import kotlin.coroutines.CoroutineContext

/**
 * Holds and exposes information about current execution context in a thread-local storage
 */
interface ExecutionContextManager {

    /**
     * Exposes current context which is configured via previous [pushToContext] calls.
     *
     * Essentially it returns aggregation of [getFromCurrentContext] results for all registered context keys.
     */
    val currentContext: Map

    /**
     * Exposes all current context information as [CoroutineContext].
     *
     * Currently execution context is based on thread and [ThreadLocal] but different coroutines can be executed
     * on different threads.
     *
     * If we want to keep using [currentContext] data in coroutines, we should include
     * [currentCoroutineContextElements] as part of coroutine context, for example, as below:
     *
     * ```
     * runBlocking {
     *     launch(Dispatchers.IO + executionContextManager.currentCoroutineContextElements) {
     *         // coroutine logic
     *     }
     * }
     * ```
     */
    val currentCoroutineContextElements: CoroutineContext

    fun  getFromCurrentContext(key: String): T?

    /**
     * This method and [popFromContext] do the same thing as [withContext], it's highly recommended to use them
     * as below:
     *
     * ```
     * pushToContext(myKey, myValue)
     * try {
     *     // action
     * } finally {
     *     popFromContext(myKey)
     * }
     * ```
     *
     * These methods are introduced in order to avoid new lambda object construction during frequent calls
     * to [withContext].
     *
     * **Note:** it's recommended to use [withContext] whenever possible as it guarantees that target data
     * is popped out from active context once target action is done. This explicit push/pop operations are only
     * for situations when we fine-tune application to avoid memory consumption as much as possible.
     */
    fun pushToContext(key: String, value: Any?)

    /**
     * @see pushToContext
     */
    fun popFromContext(key: String)
}

/**
 * Executes given action in a way that [ExecutionContextManager.getFromCurrentContext] for the given key
 * returns given value until the action is done.
 */
inline fun  ExecutionContextManager.withContext(key: String, value: Any?, action: () -> T): T {
    pushToContext(key, value)
    return try {
        action()
    } finally {
        popFromContext(key)
    }
}

/**
 * Similar to `withContext(String, Any?)` but applies the bulk parameters.
 */
inline fun  ExecutionContextManager.withContext(context: Map, action: () -> T): T {
    for ((key, value) in context) {
        pushToContext(key, value)
    }
    return try {
        action()
    } finally {
        for (key in context.keys) {
            popFromContext(key)
        }
    }
}

/**
 * We might start processing in one thread and then continue in another thread, e.g. we might receive an event
 * to process in one thread, queue and process it in a worker thread.
 *
 * That way a [context][withContext] set in the initial thread is lost in the second thread and we need
 * to re-configure it explicitly.
 *
 * This method allows to decorate given action in a way that it's executed in the context which was active
 * during this method's call time.
 *
 * Sample:
 *
 * ```
 * executionContextManager.withContext("some-key", "some-value") {
 *     threadPool.submit(executionContextManager.withCurrentContext {
 *         println(executionContextManager.getFromCurrentContext("some-key")) // is guaranteed to print 'some-value'
 *     })
 * }
 * ```
 */
inline fun  ExecutionContextManager.withCurrentContext(crossinline action: () -> T): () -> T {
    val contextToPreserve = currentContext
    return {
        withContext(contextToPreserve) {
            action()
        }
    }
}

inline fun  ExecutionContextManager.withCurrentContext(crossinline action: (T) -> R): (T) -> R {
    val contextToPreserve = currentContext
    return { param ->
        withContext(contextToPreserve) {
            action(param)
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy