https://kotlinlang.org logo
#coroutines
Title
# coroutines
s

spierce7

12/20/2018, 5:19 AM
@elizarov Is it possible to use the coroutine context to hold other information, such as a database connection that follows a request through the server? I can think of several instances where it would be nice to to use a coroutine context to intrinsically hold certain pieces of data to follow an async request.
g

gildor

12/20/2018, 5:26 AM
Sure, coroutine context is created exactly for this, you can pass any information
s

spierce7

12/20/2018, 5:40 AM
How do I pass information into the coroutine context? Am I creating my own coroutine context class, and using
is
checks to pull data, or is there another way (I see there is a CoroutineContext.Key)? What about the same information being available in the child coroutine contexts as well?
g

gildor

12/20/2018, 6:46 AM
Yes, you creating own coroutine context
and using
is
checks to pull data
No, you can just use Key for that and when you get request by Key it will be already casted to your context class
What about the same information being available in the child coroutine contexts as well?
The same as now, you can pass parent context explicitly, most of primitives just getting parent context and merge with some own, like
CoroutineScope
context will be used for all child coroutines or
withContext
merges passed context with parent and so on
@spierce7 See CoroutineName context implementation as a simple example of custom context (just 1 additional field)
And to retrieve it on any coroutine just call:
Copy code
val coroutineName = coroutineContext[CoroutineName]?.name
As you can see context is always nullable but casted for you because Key is also type information of context
d

dave08

12/20/2018, 9:46 AM
What's the advantage over passing these things in from the outer scope? And also, what conceptual relationship would these things have to the coroutineContext, which as far as I understand is just for tracking coroutine state?
g

gildor

12/20/2018, 9:49 AM
which as far as I understand is just for tracking coroutine state
this is not true, coroutineContext is for any coroutine related metadata
it’s like ThreadLocal
d

dave08

12/20/2018, 9:50 AM
Practically speaking, what would be an example of a good use case?
g

gildor

12/20/2018, 10:03 AM
I don’t think that some every day usage cases are suitable, but for framework/library writers it can be very useful in some cases
like in case of kotlinx.coroutines
d

dave08

12/20/2018, 12:17 PM
Thanks for the ThreadLocal hint 😉, I started looking into it and found a few nice articles...
s

spierce7

12/20/2018, 2:28 PM
@gildor You end up helping me out a lot. Thanks for that. Do you work for JB?
I'm still not understanding. The following example fails. The actual type is a
CombinedContext
. Would I need to override some additional methods?
Copy code
@Test fun coroutineTest() = runBlocking {
    TestScope.launch {
        assertEquals(CoroutineName::class, coroutineContext::class)
    }.join()
}

object TestScope : CoroutineScope {
    override val coroutineContext: CoroutineContext
        get() = CoroutineName("Blah")
}

data class CoroutineName(
    val name: String
) : AbstractCoroutineContextElement(CoroutineName) {
    companion object Key : CoroutineContext.Key<CoroutineName>
    override fun toString(): String = "CoroutineName($name)"
}
g

gildor

12/20/2018, 2:59 PM
No, I'm not working for JB or Kotlin team :)
Nono, you shouldn’t just check class of context, instead get context by key, because coroutines add own context items
The reason why you test is fails, because
coroutineContext
is not only your item, it’s merged with other default contexts, just add
println(coroutineContext)
in
launch
to check what is there, most obvious thing is Job, because you use launch
s

spierce7

12/20/2018, 4:09 PM
Ok, that works.
If I remove
companion object Key : CoroutineContext.Key<CoroutineName>
, it stops working. Is reflection being used to retrieve the key for the context? How does that work?
g

gildor

12/20/2018, 4:24 PM
No, reflections are not used
Just something similar to map where Key is key of map, but also Key is parametrized by context type, so you getting context of correct type
s

spierce7

12/20/2018, 6:02 PM
But how does the coroutine context know which key to associate with a specific context?
If I remove
companion object Key : CoroutineContext.Key<CoroutineName>
from the
CoroutineName
class it no longer works
g

gildor

12/21/2018, 2:14 AM
If I remove
companion object Key : CoroutineContext.Key<CoroutineName>
from the
CoroutineName
class it no longer works
Of course, because companion object used as a Key
see:
Copy code
coroutineContext[CoroutineName]
It’s actually the same as
Copy code
coroutineContext[CoroutineName.Key]
Just implicit reference to companion object
It’s pretty simple Map-like datastrcture
s

spierce7

12/21/2018, 2:28 AM
Right, but how does it retrieve the key for a context if not through reflection?
g

gildor

12/21/2018, 2:28 AM
This is how kotlin companion object works
s

spierce7

12/21/2018, 2:28 AM
I understand that once the key is retrieved, it's stored in a map-like data structure
g

gildor

12/21/2018, 2:28 AM
coroutineContext[CoroutineName]
CoroutineName is companion object
s

spierce7

12/21/2018, 2:28 AM
Please explain? I think it's possible there is something for me to learn here
g

gildor

12/21/2018, 2:29 AM
see, imagine class:
class Foo
you cannot just use it as
Foo
, there is no such syntax in Kotlin, correct? Like
val foo = Foo
You will get compile error
s

spierce7

12/21/2018, 2:30 AM
right. You'd have to do Foo()
g

gildor

12/21/2018, 2:30 AM
but, let’s now add companion object to it:
Copy code
class Foo {
    companion object
}
empty, without interface, without members
now this code is completely valid:
Copy code
val foo = Foo
and
foo
contains instance of companion object of Foo
s

spierce7

12/21/2018, 2:31 AM
I see... I didn't realize the key was a companion object
I just thought it was an object inside of it. What's the point of naming the companion object as type Key?
g

gildor

12/21/2018, 2:32 AM
Actually name
Key
is redundand, it’s just a convention
s

spierce7

12/21/2018, 2:32 AM
ic
g

gildor

12/21/2018, 2:32 AM
but what is important is parent interface
companion object : CoroutineContext.Key<CoroutineName>
you see, companion object implements interaface CoroutineContext.Key and has generic type that equals to your context type
s

spierce7

12/21/2018, 2:33 AM
right
g

gildor

12/21/2018, 2:33 AM
so Key is key of this map-like context and has parameterized type so type check works on compile time, no reflections
s

spierce7

12/21/2018, 2:34 AM
So I should be able to do something like
CoroutineName is CoroutineContext.Key
and that should return true
g

gildor

12/21/2018, 2:35 AM
yes, because
CoroutineName
is not a type, but companion object instance
s

spierce7

12/21/2018, 2:35 AM
ic
one more piece of the puzzle is missing for me
so for a given
coroutineContext
, how do I access it's companion object?
g

gildor

12/21/2018, 2:36 AM
companion object of coroutineContext? what do you mean?
you just lookup required context by key
Copy code
coroutineContext[SomeCoroutineContextKey]
s

spierce7

12/21/2018, 2:37 AM
so there has to be a way to programmatically access the companion object for a class instance for this all to work without reflection
somehow the
SomeCoroutineContextKey
has to be stored inside of the
coroutineContext
right?
we never give it the key. We only give it the coroutine context, and so the coroutine library somehow grabs the Key
g

gildor

12/21/2018, 2:40 AM
I see what you mean
check plus operator implementation
Your context should extend
AbstractCoroutineContextElement
, or just implement Element
if you want to retrieve it by Key
s

spierce7

12/21/2018, 2:42 AM
lookinmg
g

gildor

12/21/2018, 2:42 AM
Element just has
key
property with key
so you can get key of context
s

spierce7

12/21/2018, 2:43 AM
ah, I see it now
public abstract class AbstractCoroutineContextElement(public override val key: Key<*>) : Element
the Key / companion object is fed into the constructor for
AbstractCoroutineContextElement
I have a full understanding of it now. Thanks 🙂
g

gildor

12/21/2018, 2:44 AM
Yes, otherwise you have to implement get/fold yourself
s

spierce7

12/21/2018, 2:44 AM
awesome 🙂
thanks 🙂
g

gildor

12/21/2018, 2:47 AM
You are welcome👍
2 Views