⚠️ 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 Cancellation Cooperative 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.

Backlinks