Is there a particular reason one should prefer `su...
# coroutines
a
Is there a particular reason one should prefer
suspend 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?
y
runBlocking
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 dispatcher
k
Cases against run blocking: • It's pretty delicate to use and you don't want to imply to others working in your codebase to use it everywhere. • It isn't available on js. Cases for runBloxking: • It spins up an event loop in the current thread. • That event loop implements
Delay
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.
👍 1
I generally prefer runBlocking over suspend fun main.
y
• Doesn't work on Kotlin native.
I thought it was? I saw an implementation of it at least, but maybe I'm wrong
k
Hmmmm I filed a bug on you track a while ago that suspending entrypoints were not currently supported on native. I don't think it's been resolved yet.
That might be outdated info.
z
I think
suspend fun main
supports JavaScript better too, since it doesn’t have the concept of threads
👍 1
y
Btw, `suspend fun main`'s implementation is really simple. It basically calls some
runSuspend
function that's defined like this for JVM:
Copy code
/*
 * 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:
Copy code
/*
 * 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)
}
k
Yup. One thing I'm curious about is how it handles a cancelation exception which bubbles up all the way to the top of the stack and if that differs from run blocking. I tested this before but I don't remember what the outcome was.
a
Thank you for the response both!
I vaguely remember running into some issue in the past when using
suspend 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