https://kotlinlang.org logo
Title
d

Dico

10/11/2019, 4:32 PM
I worked out how to keep application alive until all tasks are completed. Is there a better way to do this with coroutines though?
private val termination = CountDownLatch(1)
private val scope = dispatcher + Job().apply {
    thread {
        // use non-dameon thread to keep application alive until all tasks completed.
        termination.await()
        while (children.any { it.isActive }) Thread.sleep(50)
    }
}

fun shutdown() {
    termination.countDown()
}
🙀 1
s

streetsofboston

10/11/2019, 4:40 PM
Why not just?
val scope : CoroutineScope = ...
fun main() = {
  val job = scope.launch { ... do all my tasks within this coroutine ... }
  runBlocking { job.join() }
}
d

Dico

10/11/2019, 4:41 PM
This scope is used for event processing and it has no set moment when it stops
but that could work
But I can't call
join()
on a forever active parent Job no?
There might be something like joinChildren() actually
s

streetsofboston

10/11/2019, 4:45 PM
Why not do a
runBlocking { job.join() }
instead of
while (children....) { ... }
? (the
job
is the
Job()
of your scope)
d

Dico

10/11/2019, 4:45 PM
But the
Job()
of my scope is never cancelled
s

streetsofboston

10/11/2019, 4:46 PM
RIght, I see… your scope is never cancelled, but the spawned child-coroutines (jobs) have ended….
d

Dico

10/11/2019, 4:49 PM
runBlocking {
 scope.coroutineContext[Job]!!.children.toList().joinAll()
}
💯 1
^^ that doesn't work
s

streetsofboston

10/11/2019, 4:50 PM
Hmmmm…
d

Dico

10/11/2019, 4:50 PM
and calling
join()
on the parent job causes the thread to hang indefinitely
s

streetsofboston

10/11/2019, 4:54 PM
This code works for me:
fun main() {
    val parentJob = Job()
    val scope = CoroutineScope(<http://Dispatchers.IO|Dispatchers.IO> + parentJob)

    println("Start")

    val job = scope.launch {
        launch { delay(1000) }
        launch { delay(2000) }
        launch { delay(2000) }
        launch { delay(2000) }
        launch { delay(2000) }
        launch { delay(2000) }
        launch { delay(2000) }
        launch { delay(2000) }
        launch { delay(2000) }
    }
    runBlocking { parentJob.children.toList().joinAll() }

    println("Done")
}
(it also works when replacing
parentJob
with
job
inside the
runBlocking { ... }
)
(
parentJob
has only one child,
job
has 9 children)
d

Dico

10/11/2019, 4:59 PM
I think there are some complications in my code that cause this to happen
testing something now
Yep ok, it's working
bit of a complex setup that I simplified too much I guess
Thanks for the help!
s

streetsofboston

10/11/2019, 5:04 PM
You’re welcome! 🙂 Great you got it working
g

gildor

10/12/2019, 5:10 AM
Maybe I didn't get the use case, but why not just replace scope.launch with runBlocking
Getting children jobs looks like a hack for me
d

Dico

10/13/2019, 7:20 AM
You didn't get the use case indeed
Perhaps it isn't the most clear but I used a solution with runBlocking, which works if called with a non daemon thread
u

uli

10/14/2019, 3:14 AM
How about making all scopes in your app children of a root scope and await this root scope in your thread?
d

Dico

10/14/2019, 5:50 PM
I would need to cancel the root scope right?
u

uli

10/14/2019, 6:01 PM
Right
d

Dico

10/17/2019, 6:13 AM
Not an option, I wouldn't be able to start cleanup jobs
u

uli

10/17/2019, 6:43 AM
Can't you start the cleanup job ina different context? It could be even non cancelable
d

Dico

10/17/2019, 7:17 AM
What I would practically need is for a channel to be closed when the parent job completes or gets cancelled, with a non cancellable coroutine that runs and processes all remaining items from the channel before dieing. They would have to be processed on a different scope which would need to be a child of the original parent, because the application must not close before those tasks are completed. The child scope would need to prevent its cancellation before receiving the cleanup tasks and then cancel itself when the cleanup tasks have all completed, allowing the original parent to finally complete and for the application to shut down.
There may be no items left in the channel, so there may also be no cleanup tasks. I think the child scope could be a child of the coroutine that processes items from the channel, starting only non cancellable tasks. That is probably a solution.
g

gildor

10/17/2019, 7:32 AM
channel to be closed when the parent job completes or gets cancelled
Sounds like you need Flow
d

Dico

10/17/2019, 11:55 AM
I'm sending items for processing to the channel from other threads