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

taer

06/02/2020, 10:58 PM
Quick launch question. I have a method I have to implement. It's called externally, and isn't expected to return till the spawned coroutine exits. I used
runBlocking
with great results. But, now I want to use a scope for the class. I have this:
Copy code
private val scope = CoroutineScope(serverCoroutineDispatcher )

    fun doWork() {
        runBlocking(scope.coroutineContext) {
            //.....
        }
    }
The runBlocking with the scope's context feels wrong. I could
scope.launch
but then it would return instantly. What am I missing?
o

octylFractal

06/02/2020, 11:16 PM
I'd say the most correct thing to do is:
Copy code
runBlocking(ctx.newCoroutineContext(Job(ctx.coroutineContext[Job])))
basically, create a "child" of the context
hmm, actually, you may not need to do that
it seems like
runBlocking
already creates a child coroutine, at least from static analysis. I'll test it out
h

henrikhorbovyi

06/02/2020, 11:21 PM
When you do:
Copy code
scope.launch { 
    // your stuff
}
inside your
doWork
function it will return instantly (as you noticed) because your function is not suspend or it is not waiting your launch to be completed
But when you do
Copy code
runBlocking(scope.coroutineContext) {
  //.....
}
It causes an illusion that doWork is waiting for your process to be completed cause runBlocking blocks your current thread
o

octylFractal

06/02/2020, 11:28 PM
here is my proof for
runBlocking
spawning a child job:
Copy code
val ctx = CoroutineScope(Dispatchers.Default + SupervisorJob())

fun main() {
    try {
        runBlocking(ctx.coroutineContext) {
            println("I am running...")
            println("But now I will cancel the job by throwing! If I'm not a child coroutine, this _will_ cancel the ctx")
            error("Test error")
        }
    } catch (e: Exception) {
        println("The other runBlocking bailed out: ${e.message}")
    }
    runBlocking(ctx.coroutineContext) {
        println("[A] I will never run if my Job is cancelled.")
    }
    // explicitly cancel now
    ctx.coroutineContext.cancel()
    runBlocking(ctx.coroutineContext) {
        println("[B] I will never run if my Job is cancelled.")
    }
}
results in
Copy code
I am running...
But now I will cancel the job by throwing! If I'm not a child coroutine, this _will_ cancel the ctx
The other runBlocking bailed out: Test error
[A] I will never run if my Job is cancelled.
Exception in thread "main" kotlinx.coroutines.JobCancellationException: Job was cancelled; job=SupervisorJobImpl{Cancelled}@3e57cd70
so I believe that what you are doing now is just fine, and will do what you want
l

Luis Munoz

06/03/2020, 2:48 AM
I have code that looks like this this:
Copy code
suspend fun disconnect(reason: DisconnectReason) {
        withContext(coroutineContext){
            agentConnection.send(Frame().setCommand(Frame.Command.DISCONNECT))
            agentConnection.close(reason)
        }
    }
using runBlocking I think is usually a mistake
when a method suspends you shouldn't hide that
t

taer

06/03/2020, 2:25 PM
In this case, the caller is an old legacy system. They're expecting the method to block until it's complete.
I could make the entry point a runBlocking and then turn around and call a suspend function that uses withContext.. That'd probably work(and expose the "desired" api upstream as well)
My last thing I'm trying to grock is scope vs context.
It just feels weird/wrong to go in and grab the context from the scope
@octylFractal So the throwing exception not canceling is actually what I want. This is inside a backend server where I want to control all jobs spawned for easy shutdown, but not necessarily want the kill server on exception
Actually, that testing brings up a question. When I create
CoroutineScope(serverCoroutineDispatcher)
it creates a Job as part of that if there isn't one already. Then when I runBlocking with that scope that then throws, shouldn't the Job be canceled in that case?
I guess I'm looking for the best way to manage scope/context for a backend server(think webapp server) where each request is independent. Should I have 2 scopes? One per request? And one for background tasks?
Or, I make a beautiful root scope that has a superVisor job, and then create an additional child scope w/ a new Job per request
5 Views