Where to `supervisorScope { }` and `coroutineScope...
# coroutines
d
Where to
supervisorScope { }
and
coroutineScope { }
get their
CoroutineScope
from?
m
They create one. That's their purpose.
d
In the kdocs it says
The provided scope inherits its [coroutineContext][CoroutineScope.coroutineContext] from the outer scope, but overrides context's [Job] with [SupervisorJob].
...?
s
It inherits the context, not the scope. You can get the context from any suspend function
👍🏼 1
d
So
from the outer scope
in the kdocs, is really just an assumption that we are running from one, otherwise, it could be a
runBlocking
or
GlobalScope
... so it gets cancelled along with wherever the suspend function was called from, right?
s
I'm not sure I can follow.
coroutineScope
is a
suspend fun
, therefore it can only be called from a suspending function. Every suspending functions has an implicit Continuation and therefore a
CoroutineContext
. This
CoroutineContext
is used. So when you are using
coroutineScope {}
within a coroutine that was launched on
GlobalScope
, the new scope will inherit `GlobalScope`'s context.
👍🏼 1
d
Oh, sorry, now I got it! I guess I thought only the Dispatcher was getting passed along... but you're right, the whole context, including the current Job + Dispatcher (which is a child of any outer
CoroutineScope
the
suspend fun
is being called from). Thanks a lot, this is now much clearer!
🙂 1
v
As it was correctly mentioned, these builders inherit
coroutineContext
, not the scope.
CoroutineScope
is a wrapper around
coroutineContext
, which is created by builders. It does not matter whether you have outer scope or not, because
coroutineContext
is always present in suspend functions.
Also does it have any default Dispatcher?
No. Which dispatcher is used depends on particular primitive. E.g. for most of our builders (
async
,
launch
,
produce
etc.)
Dispatchers.Default
will be used if context does not have one.
d
That could be a bit confusing... the same
launch
called from a suspend function could be
Dispatchers.Default
or inherit a parent's dispatcher far back in the call stack... so if one would call it from
runBlocking
or
GlobalScope
it would work on
Dispatchers.Default
but from a presenter running on
Dispatchers.Main
inheriting from the Presenter's
CoroutineScope
..., shouldn't this be a bit more explicit? @Vsevolod Tolstopyatov [JB]
v
or inherit a parent’s dispatcher far back in the call stack...
It cannot happen “out of thin air”. You have precise control over what happens: 1) You can use
coroutineScope
. Then you are using convention “here is my isolated scope with dispatcher inherited from the caller”. 2) You can make your method extension on coroutine scope. Then again you are opt-in for “caller is responsible” 3) You can use
withContext(dispatcher)
, then you are forcing dispatcher you want 4) Your API requires dispatcher in parameters and/or you are always pass it explicitly such as
fun CoroutineScope.launchInIO = async(IO) { ... }
shouldn’t this be a bit more explicit
We are always open for a better proposals 🙂 But explicitly requiring dispatcher in every builder is too verbose. The idea of structured concurrency is that once you have a scope, all coroutines inherit it and optionally override some parts of context.
d
Thanks for the explanation, #1 is the one I'm concerned about, say for an api that handles some computation in parallel, the api provider expects the default
Dispatchers.Default
to be used like it used to be before structured concurrency.. or someone migrating to structured concurrency might not notice that instead of defaulting to something similar to
CommonPool
it gets inherited from the scope ... which could be completely wrong... maybe some kind of migration guide or stress in the docs along with a link to it from the release notes (that I assume is usually what most people look at...).
Or maybe even better... to allow to override the Dispatcher in the
coroutineScope
and
supervisorScope
builders as if one used
withContext
?
That way, the entry point doesn't make any assumptions, but has the option to be explicit without having to specify it for each
launch
or
async
...
That's really what I was expecting when I first approached this new api...
v
to allow to override the Dispatcher in the
coroutineScope
withContext(dispatcher)
🙂
the api provider expects the default
Dispatchers.Default
That way, the entry point doesn’t make any assumptions
if you expect it — specify it explicitly. Nothing in API prevents you from that. You can use
withContext(Dispatcher)
or create
CoroutineScope(coroutineContext + dispatcher)
and use it as receiver etc.
I don’t understand what you are trying to achieve or what pieces you miss. API is rich and allows you to enforce almost any behaviour you like. If you are itself an API provider you have a various options to enforce context you want. If you are writing concrete business logic you may have an implicit contract “everything executes in UI thread by default” and use it. But what we can’t do is to protect users from every possible API misuse 🙂
Though I understand that the library is quite large and most of the primitives are new. I’m thinking about making a F.A.Q. with short answers for most common questions like “What’s the difference between coroutineContext and CoroutineScope?“. The trick is not to make another
coroutine-guide.md
🙂 I’d happy to have your questions in this F.A.Q if you’ll make them short and concrete
d
My use case is a component that downloads multiple files in parallel, and each file is downloaded in parts in parallel (saved to different numbered files). The code is spread out in a few classes to make it pluggable and to offer different options. I need to
launch
(parts) and
async
(file received) and
produce
(for download progress) spread out in all those classes. It comes out that I have a few `coroutineScope`s spread out all over, those are internal, so they can inherit a dispatcher. But the entrypoints need to be explicit, so if there would be
coroutineScope(<http://Dispatchers.IO|Dispatchers.IO>) { }
for those endpoints, anyone using this library even from the main thread would be fine, and to the implementor it's clearer and more explicit than
coroutineScope { withContext(<http://Dispatchers.IO|Dispatchers.IO>) { } }
. Both forms are needed, no?
I once thought it would be nice to have a best practices guide with examples on recommended common use patterns, I think that would be even more useful than a faq. When it comes to using coroutines in practise, there's the do's and the don'ts...
The guide is for explaining basic concepts, the best practises are more like short guidelines in a point form with short examples or links to samples...