Hi folks, I was trying to reproduce a case where c...
# coroutines
a
Hi folks, I was trying to reproduce a case where coroutines and thread local don’t play well together. I wanted to try out using the threadlocal in a coroutine without using the extension function asContextElement() to see it breaking :) Now, as you can see from the snippet I launch a bunch of coroutines in half the threads I spawn and I put the other half to sleep. I wanted to prove that a suspending coroutine could wake up in a thread that was sleeping, leaking the thread local value. What happens instead is that the coroutines run all in the thread 2 (which was surprising to me ) and all of them prints null when reading the thread local value. Now I guess that happens because somehow threadlocal gets cleared, is that correct? What can I change to make sure the coroutines read the wrong value? (yeah I know, but I’m just experimenting)
I actually see why the run all on thread 2, that’s because that guy is the first to get free and the coroutine code is too fast so it just execute all in the same thread that has been finished. Indeed if I change
yield()
with
delay(input.toLong() * 1000)
then I can see them running on complete different threads. Now I also understand that the null is given by the fact that the coroutine is running on a thread after it has completed, so probably the thread local gets emptied. Maybe Thread.sleep() is taking the thread and not allowing a coroutine to be dispatched on it?
t
Thread.sleep indeed keeps (as you expect) your thread blocked.
a
anything you can suggest to reproduce such scenario?
t
not sure what your aim is, but Kotlin will not do any magic where it steals a thread if you are using for something else. Only at a
suspend
point (like
yield
can execution switch to another part of your code (which also has to be
suspend
).
Thread.sleep
is just a JRE method which is unaware of any of this.
a
my aim is to reproduce when threadlocal used in coroutines can cause data leak across threads if you don't use asContextElement in a launch()
t
your only suspend point after setting your thread local is
joinAll
but since this is for coroutines that do practically nothing that is a very small window, further more since you use
DEFAULT
scheduling in your launch, the coroutines you launched in that context are already scheduled. there is a small chance for coroutines launched by other context (which you yourself schedule in other threads) to steal some time during
joinAll
but the chance of that is very minimal, probably they just spawn too late and you’re already at
print
the reason you get
null
in thread 2 is because nothing is ever set there.
rather than demonstrating this by randomly launching threads all over the place you’re better of using a
Executors.newSingleThreadExecutor().asCoroutineDispatcher()
, launching something on it, set your thread local and
delay
, then launch something else a little later (but less than the delay) on the same dispatcher, and set the thread local to something else. then after the delay the value will have changed.
also, a lot less code  😅
so little, I made it myself: https://pl.kotl.in/eo0q99k_q
I think you can build on that to demonstrate whatever you want exactly
👍 1
a
thank you @Tijl