Skip to content

Files

Latest commit

 

History

History
74 lines (57 loc) · 2.02 KB

SuspendFunSwallowedCancellation.md

File metadata and controls

74 lines (57 loc) · 2.02 KB

Pattern: suspend function swallowed cancellation

Issue: -

Description

suspend functions should not be called inside runCatching's lambda block, because runCatching catches all the Exceptions. For Coroutines to work in all cases, developers should make sure to propagate CancellationException exceptions. This means CancellationException should never be:

  • caught and swallowed (even if logged)
  • caught and propagated to external systems
  • caught and shown to the user

they must always be rethrown in the same context.

Using runCatching increases this risk of mis-handling cancellation. If you catch and don't rethrow all the CancellationException, your coroutines are not cancelled even if you cancel their CoroutineScope.

This can very easily lead to:

  • unexpected crashes
  • extremely hard to diagnose bugs
  • memory leaks
  • performance issues
  • battery drain

See reference, Kotlin doc.

If your project wants to use runCatching and Result objects, it is recommended to write a coRunCatching utility function which immediately re-throws CancellationException; and forbid runCatching and suspend combinations by activating this rule.

Example of incorrect code:

@@Throws(IllegalStateException::class)
suspend fun bar(delay: Long) {
    check(delay <= 1_000L)
    delay(delay)
}

suspend fun foo() {
    runCatching {
        bar(1_000L)
    }
}

Example of correct code:

@@Throws(IllegalStateException::class)
suspend fun bar(delay: Long) {
    check(delay <= 1_000L)
    delay(delay)
}

suspend fun foo() {
    try {
        bar(1_000L)
    } catch (e: IllegalStateException) {
        // handle error
    }
}

// Alternate
@@Throws(IllegalStateException::class)
suspend fun foo() {
    bar(1_000L)
}

Further Reading