kotlinx.coroutines: Thread local is not cleaned up sometimes
I’m using a ThreadContextElement that sets value of a ThreadLocal. After resolving of #985 it worked perfectly.
But after upgrade to 1.5.0 I’ve got a similar problem: sometimes the last value of the thread local stucks in a worker thread.
Equivalent code:
while(true) {
someCode {
// here the thread local may already have a value from previous iteration
withContext(threadLocal.asContextElement("foo")) {
someOtherCode()
}
}
}
Actual code of the ThreadContextElement implementation is here.
It is hard to reproduce the issue, but I’m facing it periodically in production (it may take hours or days to arise).
Tested 1.5.0 and 1.5.2, both behaves the same. Running it with -ea.
About this issue
- Original URL
- State: open
- Created 3 years ago
- Reactions: 2
- Comments: 29 (13 by maintainers)
Commits related to this issue
- Confine context-specific state to the thread in UndispatchedCoroutine in order to avoid state interference when the coroutine is updated concurrently. Concurrency is inevitable in this scenario: when... — committed to Kotlin/kotlinx.coroutines by qwwdfsad 2 years ago
- Confine context-specific state to the thread in UndispatchedCoroutine… (#3155) * Confine context-specific state to the thread in UndispatchedCoroutine in order to avoid state interference when the co... — committed to Kotlin/kotlinx.coroutines by qwwdfsad 2 years ago
- Duct-tape fix for #2930 — committed to Kotlin/kotlinx.coroutines by qwwdfsad 2 years ago
- Properly preserve thread local values for coroutines that are not intercepted with DispatchedContinuation Fixes #2930 — committed to Kotlin/kotlinx.coroutines by qwwdfsad 2 years ago
- Properly preserve thread local values for coroutines that are not intercepted with DispatchedContinuation (#3252) * Properly preserve thread local values for coroutines that are not intercepted with ... — committed to Kotlin/kotlinx.coroutines by qwwdfsad 2 years ago
- Confine context-specific state to the thread in UndispatchedCoroutine… (#3155) * Confine context-specific state to the thread in UndispatchedCoroutine in order to avoid state interference when the co... — committed to dee-tree/kotlinx.coroutines by qwwdfsad 2 years ago
- Confine context-specific state to the thread in UndispatchedCoroutine… (#3155) * Confine context-specific state to the thread in UndispatchedCoroutine in order to avoid state interference when the co... — committed to pablobaxter/kotlinx.coroutines by qwwdfsad 2 years ago
- Properly preserve thread local values for coroutines that are not intercepted with DispatchedContinuation (#3252) * Properly preserve thread local values for coroutines that are not intercepted with ... — committed to pablobaxter/kotlinx.coroutines by qwwdfsad 2 years ago
Hi! Here I am again. The following code fails on 1.6.4:
playground
Thanks for both Ktor and regular reproducer!
The source of the issue is indeed non-
kotlinx.coroutinesrelated entry point that Ktor leverages in order to optimize its internal machinery (SuspendFunGun). #3155 fixed completely different bug that happened to be reproducible with the very same snippet 😃I have a potential solution in mind (#3252) and also future-proof plan to avoid similar problems (#3253), I believe this issue itself is enough to release 1.6.2 with a fix, though I cannot give you a strict timeline here
Great job with a reproducer! Verified it reproduces, we’ll fix it in 1.6.1
@qwwdfsad I have updated repo with new versions and new test based on Aleksei Tirman provided.
https://github.com/michail-nikolaev/kotlin-coroutines-thread-local/blob/master/test/SuspendFunctionGunTest.kt (and copy past of KTOR in https://github.com/michail-nikolaev/kotlin-coroutines-thread-local/blob/master/test/CopyPast.kt)
Is there a planned release date for 1.6.1?
Finally I’ve managed to write a small reproducer:
It completes almost instantly on my machine and takes some time on play.kotlinlang.org.
Hello.
We are getting something like this after few days in production…
We have a loop like this:
Also, all builders are pretty standard. Maybe some tricks with cancellation\exceptions\etc…
1.5.1 version.
And of course, we have a lot of code like this: