cancel-parent-cancel-children

Here we cancel the parent scope and that automatically cancels children.

Code

package com.glassthought.sandbox
 
import gt.sandbox.util.output.Out
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.async
import kotlinx.coroutines.cancel
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlin.system.exitProcess
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds
 
private val out = Out.standard()
private suspend fun mainImpl(out: Out) {
  coroutineScope {
    foo("msg-1")
  }
}
 
private suspend fun foo(msg: String) {
  out.actionWithMsg("fooImpl", { fooImpl(msg) })
}
 
private suspend fun fooImpl(msg: String) {
  coroutineScope {
    val deferred3 = async {
      out.delayNamed(3.seconds, "delayed([${msg}])")
 
      "res-3"
    }
    val deferred2 = async {
      out.delayNamed(2.seconds, "delayed([${msg}])")
 
      "res-2"
    }
    val deferred1 = async {
      out.delayNamed(1.seconds, "delayed([${msg}])")
 
      "res-1"
    }
 
 
    out.info("Just launched co-routines")
    out.delayNamed(500.milliseconds, "delay before scope cancel")
    this.cancel()
 
    out.info("deferred-1.result=" + deferred1.await())
    out.info("deferred-2.result=" + deferred2.await())
    out.info("deferred-3.result=" + deferred3.await())
  }
 
}
 
fun main(): Unit = runBlocking {
  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 a2df237045869c0a8e1b \
&& 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:   39ms][๐Ÿฅ‡][โ‘ ][coroutname:@coroutine#1][tname:main/tid:1] START - ON MAIN
[INFO][elapsed:   56ms][๐Ÿฅ‡][โ‘ ][coroutname:@coroutine#1][tname:main/tid:1] [>] Starting action=[fooImpl] 
[INFO][elapsed:   58ms][๐Ÿฅ‡][โ‘ ][coroutname:@coroutine#1][tname:main/tid:1]    Just launched co-routines
[INFO][elapsed:   60ms][๐Ÿฅ‡][โ‘ ][coroutname:@coroutine#1][tname:main/tid:1]    Delaying for 500ms what_for=[delay before scope cancel]
[INFO][elapsed:   63ms][๐Ÿฅ‡][โ“ถ][coroutname:@coroutine#2][tname:main/tid:1] Delaying for 3s what_for=[delayed([msg-1])]
[INFO][elapsed:   63ms][๐Ÿฅ‡][โ“ท][coroutname:@coroutine#3][tname:main/tid:1] Delaying for 2s what_for=[delayed([msg-1])]
[INFO][elapsed:   63ms][๐Ÿฅ‡][โ“ธ][coroutname:@coroutine#4][tname:main/tid:1] Delaying for 1s what_for=[delayed([msg-1])]
[INFO][elapsed:  562ms][๐Ÿฅ‡][โ‘ ][coroutname:@coroutine#1][tname:main/tid:1]    Done delaying for 500ms what_for=[delay before scope cancel]
[WARN][elapsed:  615ms][๐Ÿฅ‡][โ“ถ][coroutname:@coroutine#2][tname:main/tid:1] ๐Ÿซก I have caught [JobCancellationException/ScopeCoroutine was cancelled], and rethrowing it ๐Ÿซก
[WARN][elapsed:  615ms][๐Ÿฅ‡][โ“ท][coroutname:@coroutine#3][tname:main/tid:1] ๐Ÿซก I have caught [JobCancellationException/ScopeCoroutine was cancelled], and rethrowing it ๐Ÿซก
[WARN][elapsed:  616ms][๐Ÿฅ‡][โ“ธ][coroutname:@coroutine#4][tname:main/tid:1] ๐Ÿซก I have caught [JobCancellationException/ScopeCoroutine was cancelled], and rethrowing it ๐Ÿซก
[INFO][elapsed:  616ms][๐Ÿฅ‡][โ‘ ][coroutname:@coroutine#1][tname:main/tid:1] [>][๐Ÿซก] Cancellation Exception - rethrowing.
[ERROR][elapsed:  617ms][๐Ÿฅ‡][โ‘ ][coroutname:@coroutine#1][tname:main/tid:1] back at MAIN got an exception! of type=[JobCancellationException] with msg=[ScopeCoroutine was cancelled] cause=[kotlinx.coroutines.JobCancellationException: ScopeCoroutine was cancelled; job="coroutine#1":ScopeCoroutine{Cancelled}@768b970c]. Exiting with error code 1
 
FAILURE: Build failed with an exception.
 
* What went wrong:
Execution failed for task ':app:run'.
> Process 'command '/home/nickolaykondratyev/.jdks/corretto-21.0.7/bin/java'' finished with non-zero exit value 1
 
* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.
> Get more help at https://help.gradle.org.
 
BUILD FAILED in 1s
Link to original

Scope with Regular Job - throw CancellationException - stops co-routine that threw. Does NOT stop sibling co-routine, does not rethrow to parent.

Code

package com.glassthought.sandbox
 
import gt.sandbox.util.output.Emojis
import gt.sandbox.util.output.Out
import kotlinx.coroutines.*
import kotlin.system.exitProcess
 
suspend fun main(): kotlin.Unit {
  val out = Out.standard()
  out.info("START")
 
  try {
    runBlocking {
 
      launch(CoroutineName("WillThrowCancelExc")) {
        // Loop over a range from 1 to 5 (inclusive)
        val howMany = 5
        for (i in 1..howMany) {
          val timeMillis = 1000L
          out.info("I will call throw CancellationException in $timeMillis ms - processing value:[${i}/${howMany}]")
          delay(timeMillis)
          out.warn("I am throwing CancellationException at value - [${i}/${howMany}]")
 
          throw CancellationException("cancel-message")
        }
      }
 
      launch(CoroutineName("JustPrints")) {
        (0..10)
          .map { "a-${it}" }
          .forEach {
            out.info(it)
 
            try {
              delay(500)
            } catch (e: CancellationException) {
              val excMsg = e.message ?: e.toString()
              out.warn("${Emojis.OBIDIENT} I have caught [${e::class.simpleName}/$excMsg], and rethrowing it ${Emojis.OBIDIENT} ")
 
              throw e
            }
          }
 
        out.info("${Emojis.CHECK_MARK} I have FINISHED all of my messages.")
      }
    }
 
  } catch (e: Exception) {
    out.error("runBlocking threw an exception! of type=[${e::class.simpleName}] with msg=[${e.message}]")
 
    exitProcess(1)
  }
 
  out.info("DONE no errors at main.")
}
 
class MyExceptionWillThrowFromCoroutine(msg: String) : RuntimeException(msg)

Command to reproduce:

gt.sandbox.checkout.commit b7c3be031f6453aa7ce9 \
&& 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:   25ms][๐Ÿฅ‡][๐Ÿงต][tname:main/tid:1] START
[INFO][elapsed:   67ms][๐Ÿฅ‡][โ‘ ][coroutname:@WillThrowCancelExc#2][tname:main/tid:1] I will call throw CancellationException in 1000 ms - processing value:[1/5]
[INFO][elapsed:   74ms][๐Ÿฅ‡][โ“ถ][coroutname:@JustPrints#3][tname:main/tid:1] a-0
[INFO][elapsed:  575ms][๐Ÿฅ‡][โ“ถ][coroutname:@JustPrints#3][tname:main/tid:1] a-1
[WARN][elapsed: 1074ms][๐Ÿฅ‡][โ‘ ][coroutname:@WillThrowCancelExc#2][tname:main/tid:1] I am throwing CancellationException at value - [1/5]
[INFO][elapsed: 1075ms][๐Ÿฅ‡][โ“ถ][coroutname:@JustPrints#3][tname:main/tid:1] a-2
[INFO][elapsed: 1576ms][๐Ÿฅ‡][โ“ถ][coroutname:@JustPrints#3][tname:main/tid:1] a-3
[INFO][elapsed: 2076ms][๐Ÿฅ‡][โ“ถ][coroutname:@JustPrints#3][tname:main/tid:1] a-4
[INFO][elapsed: 2577ms][๐Ÿฅ‡][โ“ถ][coroutname:@JustPrints#3][tname:main/tid:1] a-5
[INFO][elapsed: 3077ms][๐Ÿฅ‡][โ“ถ][coroutname:@JustPrints#3][tname:main/tid:1] a-6
[INFO][elapsed: 3578ms][๐Ÿฅ‡][โ“ถ][coroutname:@JustPrints#3][tname:main/tid:1] a-7
[INFO][elapsed: 4079ms][๐Ÿฅ‡][โ“ถ][coroutname:@JustPrints#3][tname:main/tid:1] a-8
[INFO][elapsed: 4579ms][๐Ÿฅ‡][โ“ถ][coroutname:@JustPrints#3][tname:main/tid:1] a-9
[INFO][elapsed: 5080ms][๐Ÿฅ‡][โ“ถ][coroutname:@JustPrints#3][tname:main/tid:1] a-10
[INFO][elapsed: 5581ms][๐Ÿฅ‡][โ“ถ][coroutname:@JustPrints#3][tname:main/tid:1] โœ… I have FINISHED all of my messages.
[INFO][elapsed: 5581ms][๐Ÿฅ‡][๐Ÿงต][tname:main/tid:1] DONE no errors at main.

Calling this.cancel()

โš ๏ธ cancel() call stops on next suspension point, NOT right away โš ๏ธ

Scope with Regular Job - Call this.cancel() - Will stop co-routine on next cooperative cancelation function invocation. Does NOT stop sibling co-routine, does not rethrow to parent. โš ๏ธWill not stop right awayโš ๏ธ

Highlight

When co-routine calls this.cancel() it does NOT stop processing right away, it stops processing once it reaches functions-suspension-point.

Code

package com.glassthought.sandbox
 
import gt.sandbox.util.output.Emojis
import gt.sandbox.util.output.Out
import kotlinx.coroutines.*
import kotlin.system.exitProcess
 
suspend fun main(): kotlin.Unit {
  val out = Out.standard()
 
  out.info("START")
 
  try {
    runBlocking {
 
      launch(CoroutineName("WillCancelMyself")) {
        // Loop over a range from 1 to 5 (inclusive)
        val howMany = 5
        for (i in 1..howMany) {
 
          val timeMillis = 1000L
          out.info("I will call cancel in $timeMillis ms - processing value:[${i}/${howMany}] - going into delay()")
 
          try {
            delay(timeMillis)
          } catch (e: CancellationException) {
            val excMsg = e.message ?: e.toString()
            out.warn("${Emojis.OBIDIENT} I have caught [${e::class.simpleName}/$excMsg], and rethrowing it ${Emojis.OBIDIENT} ")
            throw e
          }
 
          out.warn("I am calling this.cancel() at value - [${i}/${howMany}]")
          this.cancel()
 
          out.warn("${Emojis.WARNING_SIGN} We continued work after this.cancel()${Emojis.WARNING_SIGN}")
 
        }
      }
 
      launch(CoroutineName("JustPrints")) {
        (0..10)
          .map { "a-${it}" }
          .forEach {
            out.info(it)
 
            try {
              delay(500)
            } catch (e: CancellationException) {
              val excMsg = e.message ?: e.toString()
              out.warn("${Emojis.OBIDIENT} I have caught [${e::class.simpleName}/$excMsg], and rethrowing it ${Emojis.OBIDIENT} ")
 
              throw e
            }
          }
 
        out.info("${Emojis.CHECK_MARK} I have FINISHED all of my messages.")
      }
    }
 
  } catch (e: Exception) {
    out.error("runBlocking threw an exception! of type=[${e::class.simpleName}] with msg=[${e.message}]")
 
    exitProcess(1)
  }
 
  out.info("DONE no errors at main.")
}
 
class MyExceptionWillThrowFromCoroutine(msg: String) : RuntimeException(msg)

Command to reproduce:

gt.sandbox.checkout.commit 8798f084905aee1fef60 \
&& 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:   17ms][๐Ÿฅ‡][๐Ÿงต][tname:main/tid:1] START
[INFO][elapsed:   51ms][๐Ÿฅ‡][โ‘ ][coroutname:@WillCancelMyself#2][tname:main/tid:1] I will call cancel in 1000 ms - processing value:[1/5] - going into delay()
[INFO][elapsed:   57ms][๐Ÿฅ‡][โ“ถ][coroutname:@JustPrints#3][tname:main/tid:1] a-0
[INFO][elapsed:  558ms][๐Ÿฅ‡][โ“ถ][coroutname:@JustPrints#3][tname:main/tid:1] a-1
[WARN][elapsed: 1057ms][๐Ÿฅ‡][โ‘ ][coroutname:@WillCancelMyself#2][tname:main/tid:1] I am calling this.cancel() at value - [1/5]
[WARN][elapsed: 1058ms][๐Ÿฅ‡][โ‘ ][coroutname:@WillCancelMyself#2][tname:main/tid:1] โš ๏ธ We continued work after this.cancel()โš ๏ธ
[INFO][elapsed: 1058ms][๐Ÿฅ‡][โ‘ ][coroutname:@WillCancelMyself#2][tname:main/tid:1] I will call cancel in 1000 ms - processing value:[2/5] - going into delay()
[WARN][elapsed: 1094ms][๐Ÿฅ‡][โ‘ ][coroutname:@WillCancelMyself#2][tname:main/tid:1] ๐Ÿซก I have caught [JobCancellationException/StandaloneCoroutine was cancelled], and rethrowing it ๐Ÿซก 
[INFO][elapsed: 1094ms][๐Ÿฅ‡][โ“ถ][coroutname:@JustPrints#3][tname:main/tid:1] a-2
[INFO][elapsed: 1594ms][๐Ÿฅ‡][โ“ถ][coroutname:@JustPrints#3][tname:main/tid:1] a-3
[INFO][elapsed: 2095ms][๐Ÿฅ‡][โ“ถ][coroutname:@JustPrints#3][tname:main/tid:1] a-4
[INFO][elapsed: 2595ms][๐Ÿฅ‡][โ“ถ][coroutname:@JustPrints#3][tname:main/tid:1] a-5
[INFO][elapsed: 3096ms][๐Ÿฅ‡][โ“ถ][coroutname:@JustPrints#3][tname:main/tid:1] a-6
[INFO][elapsed: 3596ms][๐Ÿฅ‡][โ“ถ][coroutname:@JustPrints#3][tname:main/tid:1] a-7
[INFO][elapsed: 4097ms][๐Ÿฅ‡][โ“ถ][coroutname:@JustPrints#3][tname:main/tid:1] a-8
[INFO][elapsed: 4598ms][๐Ÿฅ‡][โ“ถ][coroutname:@JustPrints#3][tname:main/tid:1] a-9
[INFO][elapsed: 5098ms][๐Ÿฅ‡][โ“ถ][coroutname:@JustPrints#3][tname:main/tid:1] a-10
[INFO][elapsed: 5600ms][๐Ÿฅ‡][โ“ถ][coroutname:@JustPrints#3][tname:main/tid:1] โœ… I have FINISHED all of my messages.
[INFO][elapsed: 5600ms][๐Ÿฅ‡][๐Ÿงต][tname:main/tid:1] DONE no errors at main.
Link to original