Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mention swallowing CancellationExceptions in the docs #3302

Merged
merged 1 commit into from May 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
50 changes: 43 additions & 7 deletions docs/topics/cancellation-and-timeouts.md
Expand Up @@ -103,6 +103,42 @@ job: I'm sleeping 4 ...
main: Now I can quit.
-->

The same problem can be observed by catching a [CancellationException] and not rethrowing it:

```kotlin
import kotlinx.coroutines.*

fun main() = runBlocking {
//sampleStart
val job = launch(Dispatchers.Default) {
repeat(5) { i ->
try {
// print a message twice a second
println("job: I'm sleeping $i ...")
delay(500)
} catch (e: Exception) {
// log the exception
println(e)
}
}
}
delay(1300L) // delay a bit
println("main: I'm tired of waiting!")
job.cancelAndJoin() // cancels the job and waits for its completion
println("main: Now I can quit.")
//sampleEnd
}
```
{kotlin-runnable="true" kotlin-min-compiler-version="1.3"}

> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-cancel-03.kt).
>
{type="note"}

While catching `Exception` is an anti-pattern, this issue may surface in more subtle ways, like when using the
[`runCatching`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/run-catching.html) function,
which does not know rethrow [CancellationException].

## Making computation code cancellable

There are two approaches to making computation code cancellable. The first one is to periodically
Expand Down Expand Up @@ -137,7 +173,7 @@ fun main() = runBlocking {
```
{kotlin-runnable="true" kotlin-min-compiler-version="1.3"}

> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-cancel-03.kt).
> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-cancel-04.kt).
>
{type="note"}

Expand Down Expand Up @@ -182,7 +218,7 @@ fun main() = runBlocking {
```
{kotlin-runnable="true" kotlin-min-compiler-version="1.3"}

> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-cancel-04.kt).
> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-cancel-05.kt).
>
{type="note"}

Expand Down Expand Up @@ -237,7 +273,7 @@ fun main() = runBlocking {
```
{kotlin-runnable="true" kotlin-min-compiler-version="1.3"}

> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-cancel-05.kt).
> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-cancel-06.kt).
>
{type="note"}

Expand Down Expand Up @@ -275,7 +311,7 @@ fun main() = runBlocking {
```
{kotlin-runnable="true" kotlin-min-compiler-version="1.3"}

> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-cancel-06.kt).
> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-cancel-07.kt).
>
{type="note"}

Expand Down Expand Up @@ -318,7 +354,7 @@ fun main() = runBlocking {
```
{kotlin-runnable="true" kotlin-min-compiler-version="1.3"}

> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-cancel-07.kt).
> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-cancel-08.kt).
>
{type="note"}

Expand Down Expand Up @@ -378,7 +414,7 @@ fun main() {
```
{kotlin-runnable="true" kotlin-min-compiler-version="1.3"}

> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-cancel-08.kt).
> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-cancel-09.kt).
>
{type="note"}

Expand Down Expand Up @@ -431,7 +467,7 @@ fun main() {
```
{kotlin-runnable="true" kotlin-min-compiler-version="1.3"}

> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-cancel-09.kt).
> You can get the full code [here](../../kotlinx-coroutines-core/jvm/test/guide/example-cancel-10.kt).
>
{type="note"}

Expand Down
16 changes: 8 additions & 8 deletions kotlinx-coroutines-core/jvm/test/guide/example-cancel-03.kt
Expand Up @@ -8,15 +8,15 @@ package kotlinx.coroutines.guide.exampleCancel03
import kotlinx.coroutines.*

fun main() = runBlocking {
val startTime = currentTimeMillis()
val job = launch(Dispatchers.Default) {
var nextPrintTime = startTime
var i = 0
while (isActive) { // cancellable computation loop
// print a message twice a second
if (currentTimeMillis() >= nextPrintTime) {
println("job: I'm sleeping ${i++} ...")
nextPrintTime += 500L
repeat(5) { i ->
try {
// print a message twice a second
println("job: I'm sleeping $i ...")
delay(500)
} catch (e: Exception) {
// log the exception
println(e)
}
}
}
Expand Down
16 changes: 9 additions & 7 deletions kotlinx-coroutines-core/jvm/test/guide/example-cancel-04.kt
Expand Up @@ -8,14 +8,16 @@ package kotlinx.coroutines.guide.exampleCancel04
import kotlinx.coroutines.*

fun main() = runBlocking {
val job = launch {
try {
repeat(1000) { i ->
println("job: I'm sleeping $i ...")
delay(500L)
val startTime = currentTimeMillis()
val job = launch(Dispatchers.Default) {
var nextPrintTime = startTime
var i = 0
while (isActive) { // cancellable computation loop
// print a message twice a second
if (currentTimeMillis() >= nextPrintTime) {
println("job: I'm sleeping ${i++} ...")
nextPrintTime += 500L
}
} finally {
println("job: I'm running finally")
}
}
delay(1300L) // delay a bit
Expand Down
6 changes: 1 addition & 5 deletions kotlinx-coroutines-core/jvm/test/guide/example-cancel-05.kt
Expand Up @@ -15,11 +15,7 @@ fun main() = runBlocking {
delay(500L)
}
} finally {
withContext(NonCancellable) {
println("job: I'm running finally")
delay(1000L)
println("job: And I've just delayed for 1 sec because I'm non-cancellable")
}
println("job: I'm running finally")
}
}
delay(1300L) // delay a bit
Expand Down
20 changes: 16 additions & 4 deletions kotlinx-coroutines-core/jvm/test/guide/example-cancel-06.kt
Expand Up @@ -8,10 +8,22 @@ package kotlinx.coroutines.guide.exampleCancel06
import kotlinx.coroutines.*

fun main() = runBlocking {
withTimeout(1300L) {
repeat(1000) { i ->
println("I'm sleeping $i ...")
delay(500L)
val job = launch {
try {
repeat(1000) { i ->
println("job: I'm sleeping $i ...")
delay(500L)
}
} finally {
withContext(NonCancellable) {
println("job: I'm running finally")
delay(1000L)
println("job: And I've just delayed for 1 sec because I'm non-cancellable")
}
}
}
delay(1300L) // delay a bit
println("main: I'm tired of waiting!")
job.cancelAndJoin() // cancels the job and waits for its completion
println("main: Now I can quit.")
}
4 changes: 1 addition & 3 deletions kotlinx-coroutines-core/jvm/test/guide/example-cancel-07.kt
Expand Up @@ -8,12 +8,10 @@ package kotlinx.coroutines.guide.exampleCancel07
import kotlinx.coroutines.*

fun main() = runBlocking {
val result = withTimeoutOrNull(1300L) {
withTimeout(1300L) {
repeat(1000) { i ->
println("I'm sleeping $i ...")
delay(500L)
}
"Done" // will get cancelled before it produces this result
}
println("Result is $result")
}
26 changes: 7 additions & 19 deletions kotlinx-coroutines-core/jvm/test/guide/example-cancel-08.kt
Expand Up @@ -7,25 +7,13 @@ package kotlinx.coroutines.guide.exampleCancel08

import kotlinx.coroutines.*

var acquired = 0

class Resource {
init { acquired++ } // Acquire the resource
fun close() { acquired-- } // Release the resource
}

fun main() {
runBlocking {
repeat(100_000) { // Launch 100K coroutines
launch {
val resource = withTimeout(60) { // Timeout of 60 ms
delay(50) // Delay for 50 ms
Resource() // Acquire a resource and return it from withTimeout block
}
resource.close() // Release the resource
}
fun main() = runBlocking {
val result = withTimeoutOrNull(1300L) {
repeat(1000) { i ->
println("I'm sleeping $i ...")
delay(500L)
}
"Done" // will get cancelled before it produces this result
}
// Outside of runBlocking all coroutines have completed
println(acquired) // Print the number of resources still acquired
println("Result is $result")
}
13 changes: 4 additions & 9 deletions kotlinx-coroutines-core/jvm/test/guide/example-cancel-09.kt
Expand Up @@ -18,16 +18,11 @@ fun main() {
runBlocking {
repeat(100_000) { // Launch 100K coroutines
launch {
var resource: Resource? = null // Not acquired yet
try {
withTimeout(60) { // Timeout of 60 ms
delay(50) // Delay for 50 ms
resource = Resource() // Store a resource to the variable if acquired
}
// We can do something else with the resource here
} finally {
resource?.close() // Release the resource if it was acquired
val resource = withTimeout(60) { // Timeout of 60 ms
delay(50) // Delay for 50 ms
Resource() // Acquire a resource and return it from withTimeout block
}
resource.close() // Release the resource
}
}
}
Expand Down
36 changes: 36 additions & 0 deletions kotlinx-coroutines-core/jvm/test/guide/example-cancel-10.kt
@@ -0,0 +1,36 @@
/*
* Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/

// This file was automatically generated from cancellation-and-timeouts.md by Knit tool. Do not edit.
package kotlinx.coroutines.guide.exampleCancel10

import kotlinx.coroutines.*

var acquired = 0

class Resource {
init { acquired++ } // Acquire the resource
fun close() { acquired-- } // Release the resource
}

fun main() {
runBlocking {
repeat(100_000) { // Launch 100K coroutines
launch {
var resource: Resource? = null // Not acquired yet
try {
withTimeout(60) { // Timeout of 60 ms
delay(50) // Delay for 50 ms
resource = Resource() // Store a resource to the variable if acquired
}
// We can do something else with the resource here
} finally {
resource?.close() // Release the resource if it was acquired
}
}
}
}
// Outside of runBlocking all coroutines have completed
println(acquired) // Print the number of resources still acquired
}
Expand Up @@ -34,8 +34,8 @@ class CancellationGuideTest {
}

@Test
fun testExampleCancel03() {
test("ExampleCancel03") { kotlinx.coroutines.guide.exampleCancel03.main() }.verifyLines(
fun testExampleCancel04() {
test("ExampleCancel04") { kotlinx.coroutines.guide.exampleCancel04.main() }.verifyLines(
"job: I'm sleeping 0 ...",
"job: I'm sleeping 1 ...",
"job: I'm sleeping 2 ...",
Expand All @@ -45,8 +45,8 @@ class CancellationGuideTest {
}

@Test
fun testExampleCancel04() {
test("ExampleCancel04") { kotlinx.coroutines.guide.exampleCancel04.main() }.verifyLines(
fun testExampleCancel05() {
test("ExampleCancel05") { kotlinx.coroutines.guide.exampleCancel05.main() }.verifyLines(
"job: I'm sleeping 0 ...",
"job: I'm sleeping 1 ...",
"job: I'm sleeping 2 ...",
Expand All @@ -57,8 +57,8 @@ class CancellationGuideTest {
}

@Test
fun testExampleCancel05() {
test("ExampleCancel05") { kotlinx.coroutines.guide.exampleCancel05.main() }.verifyLines(
fun testExampleCancel06() {
test("ExampleCancel06") { kotlinx.coroutines.guide.exampleCancel06.main() }.verifyLines(
"job: I'm sleeping 0 ...",
"job: I'm sleeping 1 ...",
"job: I'm sleeping 2 ...",
Expand All @@ -70,8 +70,8 @@ class CancellationGuideTest {
}

@Test
fun testExampleCancel06() {
test("ExampleCancel06") { kotlinx.coroutines.guide.exampleCancel06.main() }.verifyLinesStartWith(
fun testExampleCancel07() {
test("ExampleCancel07") { kotlinx.coroutines.guide.exampleCancel07.main() }.verifyLinesStartWith(
"I'm sleeping 0 ...",
"I'm sleeping 1 ...",
"I'm sleeping 2 ...",
Expand All @@ -80,8 +80,8 @@ class CancellationGuideTest {
}

@Test
fun testExampleCancel07() {
test("ExampleCancel07") { kotlinx.coroutines.guide.exampleCancel07.main() }.verifyLines(
fun testExampleCancel08() {
test("ExampleCancel08") { kotlinx.coroutines.guide.exampleCancel08.main() }.verifyLines(
"I'm sleeping 0 ...",
"I'm sleeping 1 ...",
"I'm sleeping 2 ...",
Expand All @@ -90,8 +90,8 @@ class CancellationGuideTest {
}

@Test
fun testExampleCancel09() {
test("ExampleCancel09") { kotlinx.coroutines.guide.exampleCancel09.main() }.verifyLines(
fun testExampleCancel10() {
test("ExampleCancel10") { kotlinx.coroutines.guide.exampleCancel10.main() }.verifyLines(
"0"
)
}
Expand Down