taer
08/07/2024, 2:21 PMupdateThreadContext
and restoreThreadContext
don't seem to get invoked if there is no coroutineDispatcher in the context.
Ran into this in a small grpc client app. We mistakenly used suspend fun main
instead of runBlocking
, so the code was running with no dispatcher.
We noticed that our ThreadContextElement was present in the coroutineContext when resumed, but was not being restored to the ThreadLocal. Once we added a coroutineDispatcher to the context, everything worked.
Running with a Dispatcher is obviously something useful. But it was a surprise that the coroutineContext was restored without the ThreadLocals.
Is this a bug?
Reproduced here: https://github.com/taer/coroutine-dispatch-issueDmitry Khalanskiy [JB]
08/07/2024, 2:30 PMThreadContextElement
, like any context element except for ContinuationInterceptor
, is simply an object lying in a CoroutineContext
, and someone has to look at it and know to interpret it somehow. suspend fun main
is implemented on the level of the language, so it clearly has no knowledge of context elements introduced by the kotlinx.coroutines
library. I don't think it's possible to change this behavior, though it's probably worth explicitly highlighting in the documentation.taer
08/07/2024, 2:33 PMDmitry Khalanskiy [JB]
08/07/2024, 2:36 PMcoroutineContext
is a language-level feature, as is delivering it to the resumed code, and ThreadContextElement
is a library-level construct. The language doesn't know about our library.taer
08/07/2024, 2:38 PMtaer
08/07/2024, 2:38 PMtaer
08/07/2024, 2:41 PMwithContinuationContext
inside CoroutineContext, which sounds semi-generic. But I'm too deep now in code that would take a bit to figure out. 🙂taer
08/07/2024, 7:15 PMupdateThreadContext
Sam
08/08/2024, 7:09 AMContinuation
, which is part of the language/stdlib. The injection of library-level features happens when the language calls continuation.intercepted()
. That calls through to
coroutineContext[ContinuationInterceptor]?.interceptContinuation(this)
So by placing a ContinuationInterceptor
into the coroutine context, the coroutines library can modify (decorate) the continuation (coroutine) that's created by the language. In practice, all the continuation interceptors inherit from CoroutineDispatcher
. I think it mostly makes sense that the dispatcher deals with thread context, since the dispatcher is the thing providing the thread. I could see an argument for breaking up the ContinuationInterceptor
class hierarchy to separate out the different responsibility more, though.Sam
08/08/2024, 7:11 AMwithContext
function messes with this pattern to save time, but that's an implementation detail)taer
08/08/2024, 3:26 PM