Expose isNewCoroutine from CopyableThreadContextElement
Use case
Hi, all. Our tracing system is based on ThreadLocal. To not lose changes to our ThreadLocal, we store the object in CopyableThreadContextElement.
ThreadLocal.set(trace)
withContext(context) { // copyForChild()
// updateThreadContext()
assertThat(ThreadLocal.get()).isEqualTo(trace)
...
}
class TraceThreadContextElement(val trace: Trace) {
fun copyForChild() {
return TraceThreadContextElement(ThreadLocal.get())
}
fun updateThreadContext() {
ThreadLocal.set(trace)
}
}
The above solution works fine for us, but we only want to propagate our object when used with withContext, we don't want to propagate the state into launch, because we as a tracing system, only consider withContext as part of the execution of the enclosing code block, since it is blocking, while launch is not.
Therefore, we hope there is a way to tell whether the CopyableThreadContextElement is created for withContext or launch, and I noticed coroutine implementation already has a boolean isNewCoroutine indicating the difference, and I wonder if is possible to expose this flag from copyForChild, thanks!
The Shape of the API
interface CopyableThreadContextElement {
fun copyForChild(isNewCoroutine: Boolean)
fun mergeForChild(isNewCoroutine: Boolean)
}
Related #3414
Do you have an approach in mind to retain backwards compatibility?
Deprecate the old API, and make the new API delegate to the old API by default. Then users has to at least override one of them. If both are overriden, then only the override for the new API will be used.
interface CopyableThreadContextElement<T> {
@Deprecated
fun copyForChild(): CopyableThreadContextElement<T> {
throw NotImplementedError(
"copyForChild(isNewCoroutine) not implemented")
}
fun copyForChild(
isNewCoroutine: Boolean): CopyableThreadContextElement<T> {
return copyForChild()
}
@Deprecated
fun mergeForChild(
element: CoroutineContext.Element): CoroutineContext {
throw NotImplementedError(
"mergeForChild(element, isNewCoroutine) not implemented")
}
fun mergeForChild(
element: CoroutineContext.Element, isNewCoroutine: Boolean) {
return mergeForChild(element)
}
}
Hi, any thoughts on this proposal? An alternative way with regard to compatibility issue:
interface CopyableThreadContextElement<T> {
fun copyForChild(): CopyableThreadContextElement<T>
fun copyForChildOnNewCoroutine(): CopyableThreadContextElement<T> = copyForChild()
}
Make copyForChild being used when isNewCoroutine is false, and copyForChildOnNewCoroutine for the other case, by default copyForChildOnNewCoroutine uses implementation of copyForChild. This might be easier than my earlier proposal. Thanks!
Hi, any updates for the issue? having distinction between new coroutine and switching context will make our tracing clients life easier, so that they don't need to manually set ThreadLocal everywhere, only for launches. Thanks!