Coroutine analog to ThreadLocal CoroutineContext .
To interact with ThreadLocal using libraries, you need to implement a custom ContinuationInterceptor that supports infrastructure-related stream locators.
Here is an example. Suppose we use some framework that relies on a specific ThreadLocal to store some specific application data ( MyData in this example):
val myThreadLocal = ThreadLocal<MyData>()
To use it with coroutines, you need to implement a context that stores the current value of MyData and puts it in the corresponding ThreadLocal every time the coroutine resumes in the thread. The code should look like this:
class MyContext( private var myData: MyData, private val dispatcher: ContinuationInterceptor ) : AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor { override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> = dispatcher.interceptContinuation(Wrapper(continuation)) inner class Wrapper<T>(private val continuation: Continuation<T>): Continuation<T> { private inline fun wrap(block: () -> Unit) { try { myThreadLocal.set(myData) block() } finally { myData = myThreadLocal.get() } } override val context: CoroutineContext get() = continuation.context override fun resume(value: T) = wrap { continuation.resume(value) } override fun resumeWithException(exception: Throwable) = wrap { continuation.resumeWithException(exception) } } }
To use it in your coroutines, you end the dispatcher that you want to use with MyContext and give it the initial value of your data. This value will be placed in the stream-local in the stream where the coroutine is escorted.
launch(MyContext(MyData(), CommonPool)) { // do something... }
The above implementation will also track any changes in the local stream that have been made and save them in this context, so that a multiple call can share the "local-stream" data through the context.
Roman elizarov
source share