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

vngantk

02/12/2019, 3:16 AM
I am still struggling with some basic concepts of coroutines. What is the purpose of having CoroutineScope when there is already CoroutineContext?
g

gildor

02/12/2019, 3:20 AM
Read this article from Roman about this concept https://medium.com/@elizarov/structured-concurrency-722d765aa952
s

streetsofboston

02/12/2019, 3:28 AM
As for the design of the CoroutineScope interface, having only one property which is a CoroutineContext: It allows for calling coroutine builders (launch, async, etc) using extension functions with a CoroutineScope receiver, which allows those functions to easily obtain a CoroutineContext that could've been configured (eg added Job or Dispatcher). It also allows child-CoroutineScopes to easily 'inherit' and modify it's parent's CoroutineContext. At least, that is how I understand it :)
d

Dico

02/12/2019, 4:46 AM
Yes, and if they were the same interface, it would have 2 different meanings/uses. The implementation uses the scope in receiver position of coroutine builders, and it would be strange to have all members of
CoroutineContext
there. Additionally, by implementing the interface with the coroutine object, there is no perdormance loss by treating the two meanings with their own interface.
v

vngantk

02/12/2019, 5:04 AM
Supposing, I have a framework providing function calls doing something that need the support of coroutines. What is the best way to pass down the ‘context’, using CoroutineScope or explicitly passing the CoroutineContext?
g

gildor

02/12/2019, 5:17 AM
I have a framework providing function calls doing something that need the support of coroutines
This is quite abstract, do you have any particular example
explicitly passing the CoroutineContext?
This is usually not required for coroutines You must use CoroutineScope if you want start coroutine, this is force you to handle lifecycle See article that I send above, it explains this use case and rationale
v

vngantk

02/12/2019, 6:04 AM
Somewhere down in the framework I need to make asynchronous calls, e.g. launch(), or async(). Because they need CoroutineScope. I do not think that just giving them GlobalSope is a good idea.
g

gildor

02/12/2019, 6:22 AM
Somewhere down in the framework I need to make asynchronous calls, e.g. launch(), or async().
There are official recommendation about this in documentation. Recommended way to use only suspend functions everywhere where it’s possible and use launch/async on top most level in some CoroutineScope. This is why i’m asking about some real use case that you have to suggest how better handle it
v

vngantk

02/12/2019, 8:13 AM
My use case is this: I am creating a Kotlin framework to wrap around an existing messaging framework written in Java. This framework will provide functions to register coroutine-based callbacks for processing messages received by the framework. So, the framework must be able to start coroutines in order to fire the coroutine-based callbacks. How should the framework decide what CoroutineScope to use for firing the callbacks? In what way we should initialise the framework to have the right CoroutineScope to do this job?
g

gildor

02/12/2019, 8:19 AM
Maybe you can show some example, because usually it’s clear from context. You need CoroutineScope only if some class want to run some background job and this job lifecycle managed by class. You said that you need wrap callback to coroutines, if it’s just one-time event you don;t need any kind of CoroutineScope or even context, you just wrap callback using continuation and you will just get suspend function, so no need to have any kind of scope on framework side If you want to wrap sequence of events you need channel and coroutine that listen events and send events to channel, in this case you need CoroutineScope and depending on your case it may be scope of class that provides this API (so class has own lifecycle) or it may be caller’s scope, so this coroutine will have lifecycle of caller
1
v

vngantk

02/12/2019, 10:09 AM
The Kotlin callback is a suspending lambda. It has to be launched from inside a non-suspending callback needed by the wrapped Java framework. How can I wrap this Kotlin callback as coroutine. I only know that I can wrap a suspending function using launch() or async() which require a CoroutineScope.
g

gildor

02/12/2019, 10:11 AM
Do you want to call suspend function from Kotlin?
How can I wrap this Kotlin callback as coroutine
What do you mean? You have any example?
I only know that I can wrap a suspending function using launch() or async() which require a CoroutineScope.
Exactly, you need coroutine scope. And recommended way to implement CoroutineScope on your class, otherwise you can use GlobalScope, but than you don’t use Structured concurrency and have to handle coroutine lifecycel yourself
v

vngantk

02/12/2019, 10:16 AM
This is the code fragment done using GlobalScope.launch():
Copy code
val kotlinCallback: suspend (Message) -> Unit = ...

    javaMessageFramework.registerListener(object : JavaMessageListener() {
        override fun receive(message: Message) {
            GlobalScope.launch {
                kotlinCallback.invoke(message)
            }
        }
    }
I have to call launch() in order to call the suspending kotlinCallback. To call launch(), I have to give it a CoroutineScope. GlobalScope can be a choice but is there anything better?
g

gildor

02/12/2019, 10:19 AM
Yes, in general it’s fine. But I would instead convert
registerListener
to suspend function or channel, it will be much more idiomatic API. Problem of this particular approach, that you do not handle lifecycle of
GlobalScope.launch
coroutine, so if kotlinCallback suspend forever or just very long, it will leak itself and all resources of javaMessageFramework
Yes, proper CoroutineScope implemented by class where you call launch
did you check Roman’s article and documentation of structured concurrency?
also as I said in this particular case I believe that
registerListener
should be conerted to coroutine itself
v

vngantk

02/12/2019, 10:21 AM
How can I convert registerListener to a suspend function because it is part of the contract provided by an existing Java framework which of course has no concept of coroutine. It just allows you register a Java interface as callback.
g

gildor

02/12/2019, 10:22 AM
Okay, is this callback will be called only once or many times?
if one, just write coroutine adapter as suspend function, no need for CoroutineContext
v

vngantk

02/12/2019, 10:22 AM
This callback is called only once when the framework is set up.
Can you show me how to write a coroutine adapter as suspend function?
g

gildor

02/12/2019, 10:24 AM
Somethingg like this
Copy code
suspend fun JavaMessageFramework.awaitMessage() = kotlin.coroutines.suspendCoroutine<Message> { cont ->
    registerListener(object : JavaMessageListener() {
        override fun receive(message: Message) {
            cont.resume(message)
        }
    }
}
this is partial implementation, you probably also should add support of
uregisterListener
when coroutine is cancelleds
Also if you will do cancellation support check
kotlinx.coroutines.suspendCancellableCoroutine
builder which provides API to get notification about coroutine cancellation
v

vngantk

02/12/2019, 10:33 AM
Thanks for your help. I need to study the reference you showed me to understand it clearly.
g

gildor

02/12/2019, 10:35 AM
Yes, check coroutine guide, KEEP also has a lot of information. In general what I show above it’s just a way how you convert any one-time callback to suspend function
4 Views