Anurag Soni
05/01/2025, 4:31 PMsuspend fun main()
over fun main() { runBlocking ...
as the entry point of an application that uses coroutines? I've always defaulted to runBlocking
as that was what I remember initially from the coroutine docs. I believe one difference might be that runBlocking picks a dispatcher from kotlinx-coroutines and suspend main starts off on the Main thread?Youssef Shoaib [MOD]
05/01/2025, 4:40 PMrunBlocking
is actually mildly better. suspend fun main
has no Dispatcher at all in the context, so some functions like startCoroutine
end up running in-place. Otherwise, both are pretty similar, especially if you're using kotlinx-coroutines
anyways because launch
et al will pick a dispatcher when called with no dispatcherkevin.cianfarini
05/01/2025, 4:41 PMDelay
so you don't need extra threads simply to delay.
• It's got knowledge of kotlinx-coroutines so you aren't operating in weird middle cases of a coroutine entry point that doesn't know about all of kotlinx-coroutine's machinery.
Cases against suspend fun main:
• It operates in a bit of a weird place that all libraries might not support properly.
• Need to spin up Displatchers.default
to delay.
• Doesn't work on Kotlin native.kevin.cianfarini
05/01/2025, 4:41 PMYoussef Shoaib [MOD]
05/01/2025, 4:42 PM• Doesn't work on Kotlin native.I thought it was? I saw an implementation of it at least, but maybe I'm wrong
kevin.cianfarini
05/01/2025, 4:42 PMkevin.cianfarini
05/01/2025, 4:42 PMZach Klippenstein (he/him) [MOD]
05/01/2025, 4:43 PMsuspend fun main
supports JavaScript better too, since it doesn’t have the concept of threadsYoussef Shoaib [MOD]
05/01/2025, 4:46 PMrunSuspend
function that's defined like this for JVM:
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
import kotlin.coroutines.Continuation
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
import kotlin.coroutines.startCoroutine
/**
* Wrapper for `suspend fun main` and `@Test suspend fun testXXX` functions.
*/
fun runSuspend(block: suspend () -> Unit) {
val run = RunSuspend()
block.startCoroutine(run)
run.await()
}
private class RunSuspend : Continuation<Unit> {
override val context: CoroutineContext
get() = EmptyCoroutineContext
var result: Result<Unit>? = null
override fun resumeWith(result: Result<Unit>) = synchronized(this) {
this.result = result
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") (this as Object).notifyAll()
}
fun await() = synchronized(this) {
while (true) {
when (val result = this.result) {
null -> @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") (this as Object).wait()
else -> {
result.getOrThrow() // throw up failure
return
}
}
}
}
}
And like this for other platforms:
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
import kotlin.coroutines.Continuation
import kotlin.coroutines.EmptyCoroutineContext
import kotlin.coroutines.startCoroutine
val EmptyContinuation: Continuation<Any?> = Continuation(EmptyCoroutineContext) { result ->
result.getOrThrow()
}
fun runSuspend(block: suspend () -> Unit) {
block.startCoroutine(EmptyContinuation)
}
kevin.cianfarini
05/01/2025, 4:49 PMAnurag Soni
05/01/2025, 8:01 PMAnurag Soni
05/01/2025, 8:18 PMsuspend fun main
(on JVM) that eventually led me to https://github.com/Kotlin/kotlinx.coroutines/issues/2930#issuecomment-1332225246 which is back when I remember moving things at work to runBlocking
as we too leverage some things that still rely on the thread local contexts