Repository navigation
Conversation
When a ThreadLocal is finalised its slot is reused by the next ThreadLocal, but threads still held the values stored for the old one, so a new ThreadLocal could return a value of an unrelated type. Slots now carry a generation that is bumped on reuse, and threads store the generation along with each value. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
The example and the description make a lot of sense to me, but I'm not sure if this is really the best way to address it. @Aidan63 Could you take a look? |
|
Hmm, yeah, I remember seeing this generation trick somewhere else and I'm not a massive fan. There's also the unrelated unideal situation where even after the TLS is collected the thread will retain the last object stored. I originally wanted the thread object to not have to worry / store info about TLS objects, but maybe that's not really possible after all. Maybe the thread object could store weak references to TLS objects and that could a basis for eagerly collecting / clearing stale values? I'll have a think. |
|
What I think an ideal solution would be is... Each thread TLS objects ( There are two problems with implementing this though, there is a Given that the weak ref approach needs a bit more work I'm fine with merging this is as is if you need it for a failing test. |
|
I'm not in a hurry, this was found via HaxeFoundation/hxcoro#164 and I'm fine with holding back that merge until everything has been sorted out properly. And the more relevant PR for that is #1435 anyway. |
When a
ThreadLocalis finalised, its slot id goes back on a free list and is handed to the nextThreadLocal. The values that threads stored in that slot are never cleared, though, so a newThreadLocalcan return a value that an old one stored on the same thread. That value can even be of an unrelated type:This made one of hxcoro's tests fail intermittently. A new
TrampolineDispatchergot aTlsslot from a collected thread pool, read that pool'sWorkStealingQueueas itsTrampoline, and failed with anhxSehException.The fix gives each slot a generation that's incremented when the slot is reused. Each
ThreadLocalremembers the generation it was created with, threads store the generation together with each value, andgetreturns null when the two don't match. Slot reuse stays as it is, and nothing has to be cleared across threads when aThreadLocalis finalised. The legacy__hxcpp_tls_get/__hxcpp_tls_setuse generation 0, which is what a slot has before its first reuse, so their behaviour doesn't change.getSlotnow reads with__get, so reading an unset slot no longer grows the arrays.With this change, the example above prints 0 of 100, and the hxcoro test passed in 20 of 20 runs instead of failing about one time in four.
🤖 Generated with Claude Code