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

groostav

11/16/2018, 7:12 AM
Hey guys, I've got a question about parent-child coroutines: what do I do if I want to start a coroutine that might not complete: should I set its parent to the caller or not?
Copy code
//producer function following conventional (?) coroutine parent-child-ing as per Romans Coroutines part II at kotlinconf 2018
fun CoroutineScope.longRunningProducer(): RecieveChannel<Info> { ... }

//then a user
fun someUser(){
  println("started!")
  runBlocking {

    val selfScope: CoroutineScope = this@runBlocking
    val producer: RecieveChannel<Info> = selfScope.longRunningProducer()

    for(item in producer){
      if(businessLogic(item)) {
        //bail!
        break;
      }
      else //...
    } 
    println("almost done!")
  }
  println("done!")
}
if
longRunningProducer
never returns never stops producing values on its returned channel --which is reasonable-- and dispatches a child coroutine that runs forever, then your output will be
Copy code
started!
almost done!
because the
nestedEventLoop
wont close until its children are done. What I'd like to have happen is for an abandoned
longRunningProducer
coroutine to simply get garbage collected, but I guess that means I gotta be careful with
weakReferences
and such, yeah?
g

gildor

11/16/2018, 7:38 AM
What is definition of “abandoned producer”? You can just add some time out logic or something similar. In general your coroutine cannot be garbage collected until you subscribed, so you can of course use some weakReferences, but better to handle lifecycle of such coroutine properly and cancel not required coroutines to let them be garbage collected
g

groostav

11/17/2018, 7:45 AM
@gildor "abandoned producer" as in a producer that is still open but will never
yield
another element. I could enforce some kind of timeout policy, but in my case its not unreasonable to have a producer go for hours without producing anything. Think of a cron-job or scheduled task kinda thing.
the "requirement of completion" is that
Copy code
runBlocking {
  launch {
    delay(999_999_999)
  }
}
println("done!")
means
done
will never get printed because by attaching a child-coroutine that is effectively abandoned to a parent scope,
runBlocking
wont exit until the child job is finished. I dont want that. But I do want the fact that if you cancel the parent, the child is also cancelled
that also begs the question: how does one listen for cancellation...
I could use a kind've
launchDaemon
to get what I want...
Copy code
@Test fun `one can create daemon coroutines with onjoin`() = runBlocking<Unit> {

        runBlocking {

            launchDaemon {
                println("I'm a daemon!")
                doLongRunningPossiblyAbandonedProcess()
                println("daemon is done!")
            }

            launch {
                println("I'm a prime job!")
                delay(10)
                println("prime job is done!")
            }

            println("last line in outer job")
        }

        println("outer job returned")
    }

    private suspend fun CoroutineScope.launchDaemon(job: suspend CoroutineScope.() -> Unit){
        val runningJob = GlobalScope.launch(block = job)
        val parentJob = this.coroutineContext[Job]!!

        GlobalScope.launch {
            val cancel = select<Boolean>{
                runningJob.onJoin { false }
                parentJob.onJoin { true }
            }

            if(cancel) runningJob.cancel()
        }
    }
but I think its also pretty clear I'm not understanding something. more reading tomorrow.
g

gildor

11/21/2018, 8:15 AM
Not sure now that understand your goal
and code of launchDaemon looks strange
that also begs the question: how does one listen for cancellation...
Job.invokeOnCompletion()
means
done
will never get printed
Yes, as I said, what is your target strategy in this case? I suggested to use withTimeout, because if some process is abandoned it means something went wrong, or if it’s fine you need some strategy how detach from it, by timeout or by some event that clean ups coroutines, for both cases it can be easily achieved, but not clear what is your case
runBlocking
wont exit until the child job is finished.
Of course, but what do you want? Also probably runBlocking is probably bad choice, because not clear here, is it just an example or you really want to use it
I do want the fact that if you cancel the parent, the child is also cancelled
This is how Job and nested coroutines work
5 Views