Skip to content

ThreadLocal.asContextElement may not be cleaned up when used with Dispatchers.Main.immediate #4121

Open
@denis-bezrukov

Description

I am not sure if it is covered by #2930, but to me steps look different

Having the following code

        val threadLocal = ThreadLocal<Int>()
        val stateFlow = MutableStateFlow<Int>(0)

        viewLifecycleOwner.lifecycleScope.launch {
            stateFlow.collect {
                Log.d("testing", "coroutine A: ${threadLocal.get()}")
            }
        }
        viewLifecycleOwner.lifecycleScope.launch(threadLocal.asContextElement(5)) {
            delay(1000)
            Log.d("testing", "before emit")
            stateFlow.emit(1)
            Log.d("testing", "after emit")
        }

It logs

coroutine A: null
before emit
coroutine A: 5
after emit

So the coroutine A, that doesn't have a threadLocal as a ContextElement, has an access to it just because of Dispatchers.Main.immediate optimization (the code was invoked directly without dispatching, and somehow context element wasn't cleaned up).
If the first coroutine is launched with Dispatchers.Main (not immediate) the issue disappears.

The reproducer is for android project (can be placed in onViewCreated in an empty fragment) just because JVM has no direct dispatcher.

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions