Есть ли способ понять, что делает сопрограмма, когда она была отменена? - PullRequest
5 голосов
/ 21 октября 2019

Предположим, у нас есть тривиальный случай "отменить с тайм-аутом" (Kotlin 1.3.50, kotlinx-coroutines-core 1.3.2):

import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.delay
import kotlinx.coroutines.withTimeout

suspend fun test() {
    println("test.begin")
    delay(5000)
    println("test.end")
}

fun main() {
    runBlocking {
        withTimeout(100) {
            test()
        }
    }
}

Я ожидаю, что исключение будет содержать имена функций, такие как main и test. Исключение, вероятно, должно содержать трассировку стека сопрограммы.

Однако на практике результат выглядит следующим образом:

Exception in thread "main" kotlinx.coroutines.TimeoutCancellationException: Timed out waiting for 100 ms
 at kotlinx.coroutines.TimeoutKt.TimeoutCancellationException (Timeout.kt:126) 
 at kotlinx.coroutines.TimeoutCoroutine.run (Timeout.kt:92) 
 at kotlinx.coroutines.EventLoopImplBase$DelayedRunnableTask.run (EventLoop.common.kt:491) 
 at kotlinx.coroutines.EventLoopImplBase.processNextEvent (EventLoop.common.kt:270) 
 at kotlinx.coroutines.DefaultExecutor.run (DefaultExecutor.kt:68) 
 at java.lang.Thread.run (Thread.java:745) 

Есть ли способ сделать исключение отмены, включающее как "отмену, так иследы стека сопрограмм "?

Обновление: CancellationException можно обнаружить с помощью try-catch, однако я не хочу try-catch каждый suspendable fun вызов:

import kotlinx.coroutines.*

suspend fun test() {
    println("test.begin")
    try {
        delay(5000)
    } catch (e: TimeoutCancellationException) {
        // It is important to throw an exception different from CancellationException
        throw RuntimeException("delay was cancelled", e) // <-- line 9
    }
    println("test.end")
}

fun main() {
    runBlocking { // <-- line 15
        withTimeout(100) {
            test()
        }
    }
}
Exception in thread "main" java.lang.RuntimeException: delay was cancelled
 at FileKt.test (File.kt:9) // throw RuntimeException
 at FileKt$test$1.invokeSuspend (File.kt:-1) 
 at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith (ContinuationImpl.kt:33) 
 at kotlinx.coroutines.DispatchedTask.run (Dispatched.kt:334) 
 at kotlinx.coroutines.EventLoopImplBase.processNextEvent (EventLoop.common.kt:270) 
 at kotlinx.coroutines.BlockingCoroutine.joinBlocking (Builders.kt:79) 
 at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking (Builders.kt:54) 
 at kotlinx.coroutines.BuildersKt.runBlocking (:1) 
 at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default (Builders.kt:36) 
 at kotlinx.coroutines.BuildersKt.runBlocking$default (:1) 
 at FileKt.main (File.kt:15) // runBlocking
 at FileKt.main (File.kt:-1) 
Caused by: java.lang.RuntimeException: delay was cancelled
 at FileKt.test(File.kt:9) // throw RuntimeException
 at FileKt$test$1.invokeSuspend(File.kt:-1)
 at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
 at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:334)
 at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:270)
 at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:79)
 at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:54)
 at kotlinx.coroutines.BuildersKt.runBlocking(:1)
 at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:36)
 at kotlinx.coroutines.BuildersKt.runBlocking$default(:1)
 at FileKt.main(File.kt:15) // runBlocking
 at FileKt.main(File.kt:-1)
...