Skip to content

Ability to obtain the current coroutine context in arbitrary code #3522

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
dovchinnikov opened this issue Nov 8, 2022 · 6 comments
Open

Comments

@dovchinnikov
Copy link
Contributor

In IJ we have huge amount of old Java code. Sometimes we want to track certain behaviour or turn on assertions depending on whether the code is running under a coroutine or not, or we'd like to access some value which is put into the coroutine context by the platform. With coroutines started by the platform we can achieve this by putting a simple ThreadContextElement on the platform side:

val ourCoroutineContext = ThreadLocal<CoroutineContext>()

object ThreadCoroutineContextElement :
  ThreadContextElement<Unit>,
  CoroutineContext.Key<ThreadCoroutineContextElement> {

  override val key: CoroutineContext.Key<*>
    get() = ThreadCoroutineContextElement

  override fun updateThreadContext(context: CoroutineContext) {
    ourCoroutineContext.set(context)
  }

  override fun restoreThreadContext(context: CoroutineContext, oldState: Unit) {
    ourCoroutineContext.remove()
  }
}

Cons of this approach:

  • we cannot do this for all other coroutines which are launched in GlobalScope and CoroutineScope(EmptyCoroutineContext) outside of the platform, or runBlocking.
  • existence of ThreadContextElement in the context may trigger the slow path in kotlinx.coroutines.internal.ThreadContextKt#updateThreadContext, it won't be necessary if this behaviour could be natively supported by the library.

Please provide an API for obtaining the current coroutine context inside arbitrary code. I think this behaviour might be turned on or off by a system property. Alternatively, a ServiceLoader-based API for contributing to the default coroutine context would be also helpful.

@dovchinnikov
Copy link
Contributor Author

a ServiceLoader-based API for contributing to the default coroutine context

I've just discovered #2932

@dovchinnikov
Copy link
Contributor Author

JetBrains@0103dd5

@dkhalanskyjb
Copy link
Collaborator

I can't imagine how it could be implemented other than by always keeping a thread-local value with the current context that would need to be updated on every dispatch, and it seems like that's the approach you took in JetBrains@0103dd5.

I think the ServiceLoader-based API is the better way: with a global flag, when some parts of the program don't need this behavior, you can't do anything. If, on the other hand, you have a coroutine context element, you can remove it from the context where you don't need it. Other than this added flexibility, I don't see meaningful differences between the two approaches if we sufficiently limit the coroutine context elements suitable for the ServiceLoader API.

@dovchinnikov
Copy link
Contributor Author

dovchinnikov commented Jul 11, 2024

you can remove it from the context where you don't need it

How? Override with an empty element with the same key? I'd like to have a proper way of actually removing an element from the context, but I understand that it might be dangerous in general.

@dkhalanskyjb
Copy link
Collaborator

Override with an empty element with the same key?

Yep. Also, not in this case, but often, something like CoroutineContext(oldContext.minusKey(unnecessaryKey)) can help.

@dovchinnikov
Copy link
Contributor Author

It would be great to have withoutContext(Key) {}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants