Suspend Coroutine No Cancel
ID |
kotlin.suspend_coroutine_no_cancel |
Severity |
low |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Reliability |
Language |
Kotlin |
Tags |
concurrency, reliability |
Description
Reports usages of suspendCoroutine { … }, recommending
suspendCancellableCoroutine instead so cancellation can propagate to the
underlying callback.
Rationale
suspend fun fetch(): String = suspendCoroutine { cont -> // FLAW
api.fetch { result -> cont.resume(result) }
}
suspendCoroutine bridges a callback-based API to a coroutine, but the
returned continuation is not cancellable. When the surrounding coroutine is
cancelled the callback keeps running and the coroutine waits for it (or never
returns if the callback never fires). This breaks structured concurrency and
leaks resources held by the callback.
Remediation
suspend fun fetch(): String = suspendCancellableCoroutine { cont ->
val handle = api.fetch { result -> cont.resume(result) }
cont.invokeOnCancellation { handle.cancel() }
}
Use suspendCancellableCoroutine and wire invokeOnCancellation { } to the
underlying resource’s cancel/dispose hook.