⚠️ does-not-cancel-parent-or-siblings-on-timeout ⚠️

When withTimeout fails it will throw TimeoutCancellationException which is a subtype of CancellationException, so it does NOT cancel parent or its siblings (even when we are running with Regular Job)

Example that illustrates that sibling is able to finish, AFTER a co-routine times out

Code

package com.glassthought.sandbox

import com.glassthought.sandbox.util.out.impl.out
import gt.sandbox.util.output.Out
import kotlinx.coroutines.*
import kotlin.system.exitProcess
import kotlin.time.Duration.Companion.seconds

private suspend fun mainImpl(out: Out) {
  coroutineScope {
    foo()
  }
}

private suspend fun foo() {
  val result = out.actionWithMsg("fooImpl", { fooImpl() })

  out.info("Got result from fooImpl: [$result]")
}

private suspend fun fooImpl(): String {
  return coroutineScope {
    launch(CoroutineName("IWillTimeOut")) {
      out.info("I am going to timeout!")
      withTimeout(1.seconds) {
        out.delayNamed(5.seconds, "delay(5.seconds) longer than surrounding withTimeout(1.seconds)")
      }
    }

    val deferredResult = async(CoroutineName("IWillReturnResult")) {
      out.delayNamed(3.seconds, "result")
      "async-result"
    }

    deferredResult.await()
  }
}


fun main(): Unit = runBlocking(CoroutineName("RunBlocking-At-Main")) {
  out.info("START - ON MAIN")

  try {
    mainImpl(out)
  } catch (e: Exception) {
    out.error("back at MAIN got an exception! of type=[${e::class.simpleName}] with msg=[${e.message}] cause=[${e.cause}]. Exiting with error code 1")

    exitProcess(1)
  }

  out.info("DONE - WITHOUT errors on MAIN")
}

Command to reproduce:

gt.sandbox.checkout.commit 547cdde62132766fd2fa \
&& cd "${GT_SANDBOX_REPO}" \
&& cmd.run.announce "./gradlew run --quiet"

Recorded output of command:

Picked up JAVA_TOOL_OPTIONS: -Dkotlinx.coroutines.debug
Picked up JAVA_TOOL_OPTIONS: -Dkotlinx.coroutines.debug
[INFO][elapsed:   14ms][🥇][①][coroutname:@RunBlocking-At-Main#1][tname:main/tid:1] START - ON MAIN
[INFO][elapsed:   31ms][🥇][①][coroutname:@RunBlocking-At-Main#1][tname:main/tid:1] [>] Starting action=[fooImpl] 
[INFO][elapsed:   35ms][🥇][⓶][coroutname:@IWillTimeOut#2][tname:main/tid:1] I am going to timeout!
[INFO][elapsed:   39ms][🥇][⓶][coroutname:@IWillTimeOut#2][tname:main/tid:1] Delaying for 5s what_for=[delay(5.seconds) longer than surrounding withTimeout(1.seconds)]
[INFO][elapsed:   39ms][🥇][⓷][coroutname:@IWillReturnResult#3][tname:main/tid:1] Delaying for 3s what_for=[result]
[WARN][elapsed: 1086ms][🥇][⓶][coroutname:@IWillTimeOut#2][tname:main/tid:1] 🫡 I have caught [TimeoutCancellationException/Timed out waiting for 1000 ms], and rethrowing it 🫡
[INFO][elapsed: 3040ms][🥇][⓷][coroutname:@IWillReturnResult#3][tname:main/tid:1] Done delaying for 3s what_for=[result]
[INFO][elapsed: 3041ms][🥇][①][coroutname:@RunBlocking-At-Main#1][tname:main/tid:1] [<] Finished action=[fooImpl].
[INFO][elapsed: 3041ms][🥇][①][coroutname:@RunBlocking-At-Main#1][tname:main/tid:1] Got result from fooImpl: [async-result]
[INFO][elapsed: 3041ms][🥇][①][coroutname:@RunBlocking-At-Main#1][tname:main/tid:1] DONE - WITHOUT errors on MAIN

Backlinks