Migration to coroutines from 0.25.1 to 0.30.2

Hi,
I’m migrating my server-side code from coroutines 0.25.1 to 0.30.2. Excuse my frankness, but I’m absolutely baffled by the new behavior and unable to get the desired behavior.

Here’s the code:

	@Test
	fun testException() {
		runBlocking {
			try {
				val x = async(context = coroutineContext) {
					throw Exception()
					10
				}
				println("Awaiting...")
				x.await()
			} catch (e: Exception) {
				println("Here")
				delay(1000)
				println("What?!")
			}
		}
	}

In 0.25.1 this prints:

Awaiting...
Here
What?!

as I would intuitively expect.

Going to 0.30.2:

Awaiting...
Here
java.lang.Exception

It looks like the exception inside try is somehow preventing delay inside catch to start.

Some observations:

  • Using GlobalScope.async does not seem to help (same output).
  • Using GlobalScope.async without context parameter will yield the desired behavior. Why the difference?
  • Even if I don’t await, the exception will somehow popup.

What I’m trying to accomplish:

  • I want to start IO computation in parallel in the same thread (think Node.JS or Vert.x execution model).
  • I want to be able to catch this exception and handle it, which might include calling suspend functions and starting additional parallel work (async calls).
  • I want exception to propagate on await. I don’t want exception to popup if I don’t await at all.

How would I do this after the redesign in 0.30.2?

After studying the documentation, I feel I understand a little better what you’re trying to accomplish with the redesign.

I’m coming from Hack/PHP background where the execution model is not as flexible as in Kotlin. Hack model has these assumptions:

  • A coroutine is not started until it is awaited on.
  • If a coroutine is not awaited, it will never be started, and no one will wait for it.
  • There is no cancelability.

Under these assumptions, structural concurrency is implicit.

I believe my example can be fixed with either using coroutineScope or supervisorScope inside try, depending on desired behavior.

Just to flag an issue I encountered during migration, resume and resumeWithException are now an extension methods (used to be methods on Continuation?). I was pretty confused by the compiler error until I figured it out.

Please add this change to kotlinx.coroutines/CHANGES.md at master · Kotlin/kotlinx.coroutines · GitHub, thanks!

(it happened between 0.30.2-eap13 and 1.0.0)