I have some doubts regarding context of coroutines...
# coroutines
t
I have some doubts regarding context of coroutines. In @elizarov article Coroutine Context and Scope there is an image showing how context and coroutines creation is handled. What I have understood from it is during launch{} inside a scope, 3 coroutine context are there. One coming from scope(called scope context), another intermediate Parent context(which will be lost since there won’t be any reference to it) and final child context(with a job inside it). Am I correct? I have read coroutines over and over still get confused. If anyone can give me an explanation, it will be huge help. Thanks.
e
CoroutineContext is made up of CoroutineContext.Element's. CoroutineScope is a wrapper around CoroutineContext that ensures conventions (such as the presence of a Job context element) to support structured concurrency.
in
scope.launch(context)
, the child's scope has elements contributed by three sources: the parent's scope's context elements, the given context's elements, and the Job created by launch
there are no concerns about "losing" the parent context, all of its elements (such as dispatcher) become part of the child's context
t
So child context will be the set union of scope context + additional context + job. That's fine. I want to know if Parent context(intermediate one) is stored somewhere. Like in child job, I can access coroutineContext and get Child Context. Same for Scope Context. But does Parent context is accessible somewhere?
e
no, why does it need to be? it is simply a set
setOf(a) + setOf(b) + setOf(c) -- what would you do with the intermediate "setOf(a) + setOf(b)"?
(well, the implementation does merge some of these things together - scope, job, continuation, all handled by the same class - but that's an implementation detail and doesn't change the conceptual model)
t
Ok. Thanks. It was just bugging me. I was thinking if child is cancelled/failed with exception, it is propagated to parent. But how do we propagate to parent without knowing about it. 😅I guess that's implementation detail and shouldn't concern me.
It's coming to me step by step now. If i have understood right, the parent-child relationship is drawn between jobs rather than contexts.
👌 1
t
you don't need a scope to run a coroutine. You don't need the coroutines library at all. Any `suspend {}`block as extra functions on it to start a new coroutine. You don't nee a launcher. You just need someplace to stash the context.
Copy code
suspend {
        println("from coroutine");
    }.startCoroutine(
        object : Continuation<Unit> {
            override val context: CoroutineContext = EmptyCoroutineContext
            // called when a coroutine ends. do nothing.
            override fun resumeWith(result: Result<Unit>) {
                result.onFailure { ex : Throwable -> throw ex }
            }
        }
    )
e
using stdlib's
kotlin.coroutines.*
, yes. you'lll note that you can't
launch
from inside there though, because CoroutineScope and the rest of
kotlinx.coroutines.*
work together to support and enforce certain patterns of structured concurrency
t
you don't need
launch
at all. that's all nice-to-have helper functions. but you don't need any of that if you want to create/run/suspend coroutines.
not that you wouldn't want to use all of that. The point is more that if your trying to figure out how the compiler and stdlib implement coroutines, I personaly found it much easier to figure out w/o using any of the coroutines library.
e
you don't need it, but if you want to have trees of jobs with cancellation... well, you might as well use kotlinx.coroutines or re-implement it
t
actually, your kind of forced to write you own in some cases. For example : You have a
epoll
based event loop. You can't have people called
dealy
that independ of the vent loop without havaing threading issues. I think rust has a better solution as it decoupled the progress of events from when/how/who wakes them up.
f
sorry for reviving an old thread but… why can’t the extension functions be defined in
CoroutineContext
and we get rid of
Scope
altoghether. It’s pretty confusing because it doesn’t even scope the coroutine (the
Job
does that)… I find
CoroutineScope
has a lot of overlap with other already defined structs in the library
e
That medium post of mine was designed to answer this specific question. We need to distinguish contexts that are used for different purposes. Hence the CoroutineScope.
f
Thanks Roman, I read the post and alongside this thread I think I finally understand the intended propose