1

I'm trying to learn about how coroutines work in Kotlin. I've read several articles online, including this one - https://proandroiddev.com/demystifying-coroutinecontext-1ce5b68407ad - but I still find myself a bit confused about how coroutine elements in the current context can be accessed. Here's the part that's confusing me.

The elements in the current context can be obtained by using the top-level suspending coroutineContext read-only property.

println("Running in ${coroutineContext[CoroutineName]}")

In this case, CoroutineName is a reference to the key that maps to the CoroutineName element. I understand that the get method on the coroutineContext looks at the generic type of the key provided to it - in this case, CoroutineName - to get the appropriate element. What I'm trying to understand is how CoroutineName, or for that matter Job, CoroutineExceptionHandler or CoroutineDispatcher are even available to refer to in the current scope, when they are not properties of the CoroutineScope receiver.

Community
  • 1
  • 1
Pastafarian
  • 2,080
  • 4
  • 18
  • 22
  • I'm not sure what exactly you're asking about, but here's a guess: when you dereference the global `coroutineContext` property, you have touched a Kotlin intrinsic that "magically", in a way not explainable by inspecting the source code, provides you the coroutine context currently in effect. Source code [here](https://github.com/JetBrains/kotlin/blob/master/libraries/stdlib/src/kotlin/coroutines/Continuation.kt#L154). – Marko Topolnik Mar 16 '20 at 07:19
  • When you, say, `launch` a new coroutine, the `coroutineContext` is available to you as a member of the `CoroutineScope` which is the receiver on the block that you pass to `launch`. However, what I want to know is how we are able to access `coroutineContext[CoroutineName]`. How is `CoroutineName` available in the current scope for us to use as an index into the map provided by `coroutineContext`? – Pastafarian Mar 16 '20 at 08:51

3 Answers3

0

Those all coroutine context elements have a property key used to address the elements in a coroutine context (which is kind of an adressable set). CoroutineName, like other context elements, define a key as a object:

companion object Key : Key<CoroutineName>

In Kotlin, you can access class's companion object using the class name:

The companion object is a singleton, and its members can be accessed directly via the name of the containing class (although you can also insert the name of the companion object if you want to be explicit about accessing the companion object)

So, I think these two expressions will be the same:

coroutineContext[CoroutineName]
coroutineContext[CoroutineName.Key]

Another great question about companion objects on SO: What is the point of naming a companion object in kotlin

BTW, you can just ctrl + click on the CoroutineName in coroutineContext[CoroutineName] in IDEA and see the declaration yourself.

madhead
  • 31,729
  • 16
  • 153
  • 201
0

The implementation of the global coroutineContext property is intrinsic, meaning that no promises are made about how it's actually implemented.

But on the JVM, the property is essentially a thread-local variable. When a coroutine is suspended, the current coroutine context is remembered in the Continuation object that is created. When the coroutine is resumed, the context is copied back from the resuming continuation to the thread-local variable that the coroutineContext property refers to.

Matt Timmermans
  • 53,709
  • 3
  • 46
  • 87
0

Here's some simple code you can run to clarify your understanding:

fun main() {
    GlobalScope.launch {
        println(this)
        println(GlobalScope)
    }
    Thread.sleep(100)
}

It will print

StandaloneCoroutine{Active}@b4642b3
kotlinx.coroutines.GlobalScope@72445c73

which should make it clear to you that the scope you launch a coroutine in is not the scope that is the receiver of your coroutine builder block. The scope you get was created by the launch function, which populated it with the name and everything else you see.

The more magical part of this mechanism is that coroutineContext is not just available inside launch, but in any suspend fun, as a global property. It evaluates to the same context you see within the launch block.

Marko Topolnik
  • 195,646
  • 29
  • 319
  • 436