CopyableThreadContextElement

A ThreadContextElement copied whenever a child coroutine inherits a context containing it.

When an API uses a mutableThreadLocal for consistency, a CopyableThreadContextElement can give coroutines "coroutine-safe" write access to that ThreadLocal.

A write made to a ThreadLocal with a matching CopyableThreadContextElement by a coroutine will be visible to itself and any child coroutine launched after that write.

Writes will not be visible to the parent coroutine, peer coroutines, or coroutines that happen to use the same thread. Writes made to the ThreadLocal by the parent coroutine after launching a child coroutine will not be visible to that child coroutine.

This can be used to allow a coroutine to use a mutable ThreadLocal API transparently and correctly, regardless of the coroutine's structured concurrency.

This example adapts a ThreadLocal method trace to be "coroutine local" while the method trace is in a coroutine:

class TraceContextElement(private val traceData: TraceData?) : CopyableThreadContextElement<TraceData?> {
companion object Key : CoroutineContext.Key<TraceContextElement>

override val key: CoroutineContext.Key<TraceContextElement> = Key

override fun updateThreadContext(context: CoroutineContext): TraceData? {
val oldState = traceThreadLocal.get()
traceThreadLocal.set(traceData)
return oldState
}

override fun restoreThreadContext(context: CoroutineContext, oldState: TraceData?) {
traceThreadLocal.set(oldState)
}

override fun copyForChild(): TraceContextElement {
// Copy from the ThreadLocal source of truth at child coroutine launch time. This makes
// ThreadLocal writes between resumption of the parent coroutine and the launch of the
// child coroutine visible to the child.
return TraceContextElement(traceThreadLocal.get()?.copy())
}

override fun mergeForChild(overwritingElement: CoroutineContext.Element): CoroutineContext {
// Merge operation defines how to handle situations when both
// the parent coroutine has an element in the context and
// an element with the same key was also
// explicitly passed to the child coroutine.
// If merging does not require special behavior,
// the copy of the element can be returned.
return TraceContextElement(traceThreadLocal.get()?.copy())
}
}

A coroutine using this mechanism can safely call Java code that assumes the corresponding thread local element's value is installed into the target thread local.

Functions

Link copied to clipboard
abstract fun copyForChild(): CopyableThreadContextElement<S>

Returns a CopyableThreadContextElement to replace thisCopyableThreadContextElement in the child coroutine's context that is under construction if the added context does not contain an element with the same key.

Link copied to clipboard
abstract fun mergeForChild(overwritingElement: CoroutineContext.Element): CoroutineContext

Returns a CopyableThreadContextElement to replace thisCopyableThreadContextElement in the child coroutine's context that is under construction if the added context does contain an element with the same key.

Sources

Link copied to clipboard