https://kotlinlang.org logo
#feed
Title
# feed
l

louiscad

11/03/2018, 6:48 PM
Funny, I thought about a road lanes analogy (which the picture makes me think of), actually wanted to use at my first public talk about coroutines at Mobilization (but didn't because I didn't have time to make a supporting visual)
o

oshai

11/03/2018, 8:17 PM
It's a great post. What I miss here is a discussion about throttling and backpressure which io threads brings to my mind.
v

ValV

11/03/2018, 8:47 PM
As for me -- it's subtle difference between suspend and non-blocking in general, 'cause suspend wraps blocking code, making it non-blocking (IO), or less-blocking (CPU)
m

Mark

11/04/2018, 2:42 AM
Minor point: NetworkOnMainThreadException link is not working (there’s a rogue “%5C” at the end)
“To make everybody’s life simpler we use the following convention: suspending functions do not block.” should this be “To make everybody’s life simpler we use the following convention: suspending functions do not block the thread they are called on.“?
e

elizarov

11/04/2018, 4:40 AM
@Mark Thanks. Corrected and clarified.
👍 1
j

jmfayard

11/04/2018, 5:59 AM
Great article Roman!
m

Mark

11/04/2018, 6:14 AM
Also bearing in mind that a suspend function shouldn’t leave anything behind, does that mean it would be common for such a function to have both a
coroutineScope{}
and
withContext(dispatcher) {}
surrounding the main body?
l

louiscad

11/04/2018, 6:15 AM
@Mark
withContext
already creates a scope, no need for an additional one.
m

Mark

11/04/2018, 6:16 AM
So
withContext()
will not return until all child jobs complete?
And if that’s true, when would you use
coroutineScope{}
within a suspend function?
l

louiscad

11/04/2018, 6:22 AM
@Mark
withContext
gives you a scope as a receiver for le block you pass to it, but no, it only folds the context you pass to it with the current context, so it doesn't wait for any child coroutine you start (the parent scope will). That said, I have never launched a coroutine from a
withContext
block.
e

elizarov

11/04/2018, 6:22 AM
It actually does wait
👍 1
All functions in
kotlinx.coroutines
library that accept a block with
CoroutineScope
receiver do consistently wait for all children.
l

louiscad

11/04/2018, 6:24 AM
@elizarov Try this snippet on play.kotl.in:
Copy code
suspend fun main() = coroutineScope {
    withContext(Dispatchers.Default) {
        println(1)
        launch {
            delay(1000)
            println(2)
        }
        println(3)
    }
    println(4)
}
I first thought the same as you, @elizarov, but remembering last time I digged into
withContext
source code, I wasn't so sure, so I tried, and saw it creates no job that waits for child coroutines.
e

elizarov

11/04/2018, 6:27 AM
Hm… Something is not right on play.kotl.in
l

louiscad

11/04/2018, 6:28 AM
For reference, the snippet above prints this:
Copy code
1
3
4
2
which shows that
withContext
call resumes before its child coroutine is completed.
e

elizarov

11/04/2018, 6:28 AM
It looks like some older version of the library — we’ve fixed that before release. With 1.0.0 on my machine it prints 1, 2, 3, 4
l

louiscad

11/04/2018, 6:30 AM
It'd be nice to be able to see which "third party" library versions are used on play.kotl.in and try.kotl.in (which has the same behavior)
e

elizarov

11/04/2018, 6:31 AM
We shall embed library version somewhere, so that you can query it….
l

louiscad

11/04/2018, 6:31 AM
Then there's a bug if it prints
1, 2, 3, 4
, it should print
1, 3, 2, 4
😉 I assume you mistyped it
e

elizarov

11/04/2018, 6:35 AM
Mistyped.
Should be 1, 3, 2, 4
m

Mark

11/04/2018, 6:36 AM
I’m curious when one would use
coroutineScope{}
(instead of
withContext()
) within suspend function? I guess when you don’t want to specify the
Dispatcher
, but that seems odd
e

elizarov

11/04/2018, 6:38 AM
It is not odd at all if you follow convention to encapsulate threading decisions. If your suspending function only calls other suspending functions, then it should not be using
withContext
but
coroutineScope
m

Mark

11/04/2018, 6:40 AM
maybe some lint checks for that would be useful?
l

louiscad

11/04/2018, 6:44 AM
@Mark I don't see how it could be done.
withContext
takes a parameter,
coroutineScope
does not. I don't see how a lint check would/should assume that you should not call
withContext
but use
coroutineScope
instead.
m

Mark

11/04/2018, 6:50 AM
Yes, I’m assuming this “If your suspending function only calls other suspending functions” can be implemented as a lint check. Maybe it can’t (and I really don’t know much at all about lint), though I’d be surprised from what I’ve seen other lint checks are capable of.
@elizarov The only reason I say “odd” is because
withContext
and
coroutineScope
seem to offer similar functionality. The key difference being the passed in dispatcher/context. In which case, one might think of those two functions being named the same with an optional dispatcher/context. In other words, being able to write
coroutineScope(<http://Dispatcher.IO|Dispatcher.IO>){}
instead of
withContext(<http://Disptacher.IO|Disptacher.IO>){}
Just cutting down the vocabulary.
So suppose I have something like
coroutineScope { <my suspending function calls> }
and I want to add a call to IO-blocking function (within that block), then I can do that and just add the
(<http://Dispatcher.IO|Dispatcher.IO>)
argument to
coroutineScope
l

louiscad

11/04/2018, 7:33 AM
I currently use this syntax: https://github.com/Kotlin/kotlinx.coroutines/issues/428#issuecomment-434381951 If this
operator fun invoke
thing never makes it into the library, then I think your
coroutineScope(context: CoroutineContext = EmptyCoroutineContext) { … }
proposal is a good idea. Maybe you could create on issue for that on GitHub, @Mark?
👍 1
m

Mark

11/04/2018, 7:43 AM
I really don’t know if the proposal makes sense (since I’m very new to coroutines) so I’d be happier if someone with more experience wants to pick it up instead.
e

elizarov

11/04/2018, 8:08 AM
Difference in vocabulary reflects difference in intent. You use withContext to wrap blocking code. In the other hand, coroutineScope is used when you decompose your code into multiple concurrent operations via launch or async.
👍 4
m

Mark

11/04/2018, 8:52 AM
Wouldn’t you also use
withContext
for non-blocking code (e.g.
withContext(Dispatchers.Main)
) when touching the UI? And couldn’t you also “decompose your code into multiple concurrent operations via launch or async” when using
withContext()
as in the earlier
1, 3, 2, 4
example?
l

louiscad

11/04/2018, 9:04 AM
It works for blocking code but also code that needs to run on a specific thread. It's called
withContext
, not
runBlocking
, so the use cases go beyond just running blocking code off the calling thread. Usually, when you use
Dispatcher.Main
, you use it as default dispatcher for the UI component root scope.
t

Tolriq

11/04/2018, 12:25 PM
@elizarov I'm having a conversation about withContext(IO) from suspend commands in a library. https://github.com/square/okio/pull/531. I personally think that a library suspending function should not enforce a dispatcher as it could generate unneeded context switch. Is there already a consensus about how libraries should well behave? And proper way to manage that?
e

elizarov

11/04/2018, 12:52 PM
The default dispatcher would not context switch unless needed (unless you are running in the main thread). So, IMO, libraries should encapsulate their blocking code inside
withContext
to make life easier for consumers.
t

Tolriq

11/04/2018, 12:56 PM
My concern is more about custom dispatchers for a context. Until https://github.com/Kotlin/kotlinx.coroutines/issues/261 is reality. If I have some kind of ressource control having the library suspend function switch to IO will generate switching + break the limits enforcing and not easy to debug issues in that case.
e

elizarov

11/04/2018, 1:24 PM
Yes. Custom dispatchers are still an open issue. We’ll design an API for them, though.
t

Tolriq

11/04/2018, 1:26 PM
So for the moment libraries should already do withContext(IO) and it's up to consumer in custom dispatchers to async.await or runblocking until custom dispatchers can share threads with them.
e

elizarov

11/04/2018, 1:30 PM
Neither async.await nor runblocking should be needed, or I don’t get your use-case
t

Tolriq

11/04/2018, 1:34 PM
I want to ensure that for a specific server I never have more than 3 active connections, so I have custom theadpool.asdispatcher and run the commands via launch { suspendfunction() }. The function is ran in my context so I know there will never be more than 3 blocking connection. If the suspend function is from the library and use withContext(IO) then the limits are not enforced and at any moment there can be 64 active connections no? Or there's something I missed in the suspend / non blocking and parallelism in the docs.
e

elizarov

11/04/2018, 3:35 PM
You approach would only work in a tightly coupled system with blocking IO and breaks when you switch to non blocking, for example. Better use coroutines worker pool. See my latest talk

https://youtu.be/a3agLJQ6vt8

t

Tolriq

11/04/2018, 4:39 PM
Really nice talk, but I'm still lost at how to build real implementation as for the moment you do not provide the abstraction for that and channels are still experimental 😞 The need is global to all access to 1 resources, and there's about 50 different needs, not just a single downloader to limit. The concurrency limit needs to apply to any of those 50 functions at any time. I do not even see how it goes with https://github.com/Kotlin/kotlinx.coroutines/issues/261. In that issue you give the example of limiting the DB access to X, but I assume it's because you assume all DB access are blocking? I suppose for this particular need, I should keep on blocking calls to ensure the limits then and see how things evolve in the future as I'm sure proper solutions will be provider later.
@elizarov started a new thread https://kotlinlang.slack.com/archives/C1CFAFJSK/p1541415543349000 with more details about the need.
6 Views