Michael Friend
03/19/2025, 6:47 PMlaunch
then at the end of my function cancelling the heartbeat job so the function doesnt sit forever for the heartbeat to finish. Is there a way to launch a job in a way that autocancels itself if its the only thing a scope is waiting for?kevin.cianfarini
03/19/2025, 6:53 PMmain
function then you can do something like this:
fun main() = runBlocking {
val heartbeatJob = launch { heartbeat() }
programEntryPoint()
heartbeatJob.cancelAndJoin()
}
suspend fun programEntryPoint() { ... }
suspend fun heartbeat() { ... }
kevin.cianfarini
03/19/2025, 6:54 PMGlobalScope
exists, but should mostly be avoided.PHondogo
03/19/2025, 7:09 PMMichael Friend
03/19/2025, 7:09 PMCLOVIS
03/20/2025, 8:37 AMval mainJob = Job()
GlobalScope.launch {
// Heartbeat code
while (true) …
}.also { mainJob.invokeOnCompletion { it.cancel("Main job finished, no need for heartbeat anymore") } }
CLOVIS
03/20/2025, 8:39 AMkevin.cianfarini
03/20/2025, 2:09 PMMichael Friend
03/20/2025, 2:39 PMkevin.cianfarini
03/20/2025, 2:44 PMMichael Friend
03/20/2025, 2:46 PMPHondogo
03/20/2025, 2:57 PMsuspend fun<T> runWithDaemons(
daemons: suspend CoroutineScope.() -> Unit,
main: suspend CoroutineScope.() -> T
) : T {
return coroutineScope {
val daemonsJob = launch {
coroutineScope(block = daemons)
}
val result = coroutineScope(block = main)
daemonsJob.cancel()
result
}
}
PHondogo
03/20/2025, 3:02 PMrunWithDaemons(
daemons = {
launch { // launching new daemon job
// ...
}
delay(50000L)
}
) {
launch { // launching new main job
// ...
}
delay(1000)
}
kevin.cianfarini
03/21/2025, 3:12 PMsuspend fun <T> withDaemonScope(block: suspend CoroutineScope.(CoroutineScope) -> T): T {
return coroutineScope {
val daemonScope = CoroutineScope(Job(coroutineContext[Job]!!))
val ret = coroutineScope { block(daemonScope) }
daemonScope.cancel()
ret
}
}
Usage would look like the following:
suspend fun foo() = withDaemonScope { daemonScope ->
daemonScope.launch { heartbeat() }
doOtherWork()
}