Change stacktrace recovery contract
* Stacktraces are no longer recovered in DispatchedTask.run
* Callers of uCont.resumeWithException are responsible for recovering the stacktrace
* CancellableContinuation.resumeWithException recovers stacktrace if necessary
* Properly recover stacktrace in Channel.receive fast-path
* Properly recover stactraces on Channel.send fast-path
* Restructure stacktrace recovery tests
diff --git a/kotlinx-coroutines-core/common/src/CancellableContinuationImpl.kt b/kotlinx-coroutines-core/common/src/CancellableContinuationImpl.kt
index 559afb8..31cf415 100644
--- a/kotlinx-coroutines-core/common/src/CancellableContinuationImpl.kt
+++ b/kotlinx-coroutines-core/common/src/CancellableContinuationImpl.kt
@@ -244,7 +244,7 @@
}
override fun resumeWith(result: Result<T>) {
- resumeImpl(result.toState(), resumeMode)
+ resumeImpl(result.toState(this), resumeMode)
}
override fun resume(value: T, onCancellation: (cause: Throwable) -> Unit) {
diff --git a/kotlinx-coroutines-core/common/src/CompletedExceptionally.kt b/kotlinx-coroutines-core/common/src/CompletedExceptionally.kt
index 785041d..b75d430 100644
--- a/kotlinx-coroutines-core/common/src/CompletedExceptionally.kt
+++ b/kotlinx-coroutines-core/common/src/CompletedExceptionally.kt
@@ -9,8 +9,10 @@
import kotlin.coroutines.*
import kotlin.jvm.*
-internal fun <T> Result<T>.toState(): Any? =
- if (isSuccess) getOrThrow() else CompletedExceptionally(exceptionOrNull()!!) // todo: need to do it better
+internal fun <T> Result<T>.toState(): Any? = fold({ it }, { CompletedExceptionally(it) })
+
+internal fun <T> Result<T>.toState(caller: CancellableContinuation<*>): Any? = fold({ it },
+ { CompletedExceptionally(recoverStackTrace(it, caller)) })
@Suppress("RESULT_CLASS_IN_RETURN_TYPE", "UNCHECKED_CAST")
internal fun <T> recoverResult(state: Any?, uCont: Continuation<T>): Result<T> =
diff --git a/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt b/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt
index 8aadd26..4639bb8 100644
--- a/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt
+++ b/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt
@@ -144,13 +144,13 @@
public final override suspend fun send(element: E) {
// fast path -- try offer non-blocking
- if (offer(element)) return
- // slow-path does suspend
+ if (offerInternal(element) === OFFER_SUCCESS) return
+ // slow-path does suspend or throws exception
return sendSuspend(element)
}
internal suspend fun sendFair(element: E) {
- if (offer(element)) {
+ if (offerInternal(element) === OFFER_SUCCESS) {
yield() // Works only on fast path to properly work in sequential use-cases
return
}
@@ -547,7 +547,11 @@
public final override suspend fun receive(): E {
// fast path -- try poll non-blocking
val result = pollInternal()
- if (result !== POLL_FAILED) return receiveResult(result)
+ /*
+ * If result is Closed -- go to tail-call slow-path that will allow us to
+ * properly recover stacktrace without paying a performance cost on fast path.
+ */
+ if (result !== POLL_FAILED && result !is Closed<*>) return receiveResult(result)
// slow-path does suspend
return receiveSuspend(RECEIVE_THROWS_ON_CLOSE)
}
diff --git a/kotlinx-coroutines-core/common/src/internal/DispatchedTask.kt b/kotlinx-coroutines-core/common/src/internal/DispatchedTask.kt
index 6c33aba..98b45d5 100644
--- a/kotlinx-coroutines-core/common/src/internal/DispatchedTask.kt
+++ b/kotlinx-coroutines-core/common/src/internal/DispatchedTask.kt
@@ -52,7 +52,7 @@
cancelResult(state, cause)
continuation.resumeWithStackTrace(cause)
} else {
- if (exception != null) continuation.resumeWithStackTrace(exception)
+ if (exception != null) continuation.resumeWithException(exception)
else continuation.resume(getSuccessfulResult(state))
}
}
@@ -116,19 +116,8 @@
internal fun <T> DispatchedTask<T>.resume(delegate: Continuation<T>, useMode: Int) {
// slow-path - use delegate
val state = takeState()
- val exception = getExceptionalResult(state)?.let {
- /*
- * Recover stacktrace for non-dispatched tasks.
- * We usually do not recover stacktrace in a `resume` as all resumes go through `DispatchedTask.run`
- * and we recover stacktraces there, but this is not the case for a `suspend fun main()` that knows nothing about
- * kotlinx.coroutines and DispatchedTask
- */
- if (delegate is DispatchedTask<*>) it else recoverStackTrace(it, delegate)
- }
- val result = if (exception != null)
- Result.failure(exception)
- else
- Result.success(state as T)
+ val exception = getExceptionalResult(state)?.let { recoverStackTrace(it, delegate) }
+ val result = if (exception != null) Result.failure(exception) else Result.success(state as T)
when (useMode) {
MODE_ATOMIC_DEFAULT -> delegate.resumeWith(result)
MODE_CANCELLABLE -> delegate.resumeCancellableWith(result)
diff --git a/kotlinx-coroutines-core/common/src/internal/Scopes.kt b/kotlinx-coroutines-core/common/src/internal/Scopes.kt
index 49770c2..c6cb187 100644
--- a/kotlinx-coroutines-core/common/src/internal/Scopes.kt
+++ b/kotlinx-coroutines-core/common/src/internal/Scopes.kt
@@ -33,11 +33,6 @@
}
}
-internal fun AbstractCoroutine<*>.tryRecover(exception: Throwable): Throwable {
- val cont = (this as? ScopeCoroutine<*>)?.uCont ?: return exception
- return recoverStackTrace(exception, cont)
-}
-
internal class ContextScope(context: CoroutineContext) : CoroutineScope {
override val coroutineContext: CoroutineContext = context
}
diff --git a/kotlinx-coroutines-core/common/src/intrinsics/Undispatched.kt b/kotlinx-coroutines-core/common/src/intrinsics/Undispatched.kt
index cfef935..0aa9cd7 100644
--- a/kotlinx-coroutines-core/common/src/intrinsics/Undispatched.kt
+++ b/kotlinx-coroutines-core/common/src/intrinsics/Undispatched.kt
@@ -85,7 +85,7 @@
* First, this function initializes the parent job from the `parentContext` of this coroutine that was passed to it
* during construction. Second, it starts the coroutine using [startCoroutineUninterceptedOrReturn].
*/
-internal fun <T, R> AbstractCoroutine<T>.startUndispatchedOrReturn(receiver: R, block: suspend R.() -> T): Any? {
+internal fun <T, R> ScopeCoroutine<T>.startUndispatchedOrReturn(receiver: R, block: suspend R.() -> T): Any? {
initParentJob()
return undispatchedResult({ true }) {
block.startCoroutineUninterceptedOrReturn(receiver, this)
@@ -95,7 +95,7 @@
/**
* Same as [startUndispatchedOrReturn], but ignores [TimeoutCancellationException] on fast-path.
*/
-internal fun <T, R> AbstractCoroutine<T>.startUndispatchedOrReturnIgnoreTimeout(
+internal fun <T, R> ScopeCoroutine<T>.startUndispatchedOrReturnIgnoreTimeout(
receiver: R, block: suspend R.() -> T): Any? {
initParentJob()
return undispatchedResult({ e -> !(e is TimeoutCancellationException && e.coroutine === this) }) {
@@ -103,7 +103,7 @@
}
}
-private inline fun <T> AbstractCoroutine<T>.undispatchedResult(
+private inline fun <T> ScopeCoroutine<T>.undispatchedResult(
shouldThrow: (Throwable) -> Boolean,
startBlock: () -> Any?
): Any? {
@@ -129,8 +129,8 @@
if (state === COMPLETING_WAITING_CHILDREN) return COROUTINE_SUSPENDED // (2)
return if (state is CompletedExceptionally) { // (3)
when {
- shouldThrow(state.cause) -> throw tryRecover(state.cause)
- result is CompletedExceptionally -> throw tryRecover(result.cause)
+ shouldThrow(state.cause) -> throw recoverStackTrace(state.cause, uCont)
+ result is CompletedExceptionally -> throw recoverStackTrace(result.cause, uCont)
else -> result
}
} else {
diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testCancelledOffer.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testCancelledOffer.txt
new file mode 100644
index 0000000..095be1e
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testCancelledOffer.txt
@@ -0,0 +1,15 @@
+kotlinx.coroutines.JobCancellationException: Job was cancelled; job=JobImpl{Cancelling}@2a06d350
+ (Coroutine boundary)
+ at kotlinx.coroutines.channels.AbstractSendChannel.offer(AbstractChannel.kt:170)
+ at kotlinx.coroutines.channels.ChannelCoroutine.offer(ChannelCoroutine.kt)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testCancelledOffer$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:153)
+Caused by: kotlinx.coroutines.JobCancellationException: Job was cancelled; job=JobImpl{Cancelling}@2a06d350
+ at kotlinx.coroutines.JobSupport.createJobCancellationException(JobSupport.kt:680)
+ at kotlinx.coroutines.JobSupport.createCauseException(JobSupport.kt:696)
+ at kotlinx.coroutines.JobSupport.cancelMakeCompleting(JobSupport.kt:673)
+ at kotlinx.coroutines.JobSupport.cancelImpl$kotlinx_coroutines_core(JobSupport.kt:645)
+ at kotlinx.coroutines.JobSupport.cancelInternal(JobSupport.kt:611)
+ at kotlinx.coroutines.JobSupport.cancel(JobSupport.kt:599)
+ at kotlinx.coroutines.Job$DefaultImpls.cancel$default(Job.kt:164)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testCancelledOffer$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:151)
+ at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testOfferFromScope.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testOfferFromScope.txt
new file mode 100644
index 0000000..bf3fd3a
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testOfferFromScope.txt
@@ -0,0 +1,10 @@
+kotlinx.coroutines.RecoverableTestException
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testOfferFromScope$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:109)
+ (Coroutine boundary)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest.sendInChannel(StackTraceRecoveryChannelsTest.kt:167)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$sendWithContext$2.invokeSuspend(StackTraceRecoveryChannelsTest.kt:162)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$sendFromScope$2.invokeSuspend(StackTraceRecoveryChannelsTest.kt:172)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testOfferFromScope$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:112)
+Caused by: kotlinx.coroutines.RecoverableTestException
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testOfferFromScope$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:109)
+ at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
\ No newline at end of file
diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testOfferWithContextWrapped.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testOfferWithContextWrapped.txt
new file mode 100644
index 0000000..612d00d
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testOfferWithContextWrapped.txt
@@ -0,0 +1,9 @@
+kotlinx.coroutines.RecoverableTestException
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testOfferWithContextWrapped$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:98)
+ (Coroutine boundary)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest.sendInChannel(StackTraceRecoveryChannelsTest.kt:199)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$sendWithContext$2.invokeSuspend(StackTraceRecoveryChannelsTest.kt:194)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testOfferWithContextWrapped$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:100)
+Caused by: kotlinx.coroutines.RecoverableTestException
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testOfferWithContextWrapped$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:98)
+ at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testOfferWithCurrentContext.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testOfferWithCurrentContext.txt
new file mode 100644
index 0000000..833afbf
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testOfferWithCurrentContext.txt
@@ -0,0 +1,9 @@
+kotlinx.coroutines.RecoverableTestException
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testOfferWithCurrentContext$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:86)
+ (Coroutine boundary)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest.sendInChannel(StackTraceRecoveryChannelsTest.kt:210)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$sendWithContext$2.invokeSuspend(StackTraceRecoveryChannelsTest.kt:205)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testOfferWithCurrentContext$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:89)
+Caused by: kotlinx.coroutines.RecoverableTestException
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testOfferWithCurrentContext$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:86)
+ at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testReceiveFromChannel.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testReceiveFromChannel.txt
new file mode 100644
index 0000000..66bb5e5
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testReceiveFromChannel.txt
@@ -0,0 +1,8 @@
+kotlinx.coroutines.RecoverableTestException
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testReceiveFromChannel$1$job$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:97)
+ (Coroutine boundary)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest.channelReceive(StackTraceRecoveryChannelsTest.kt:116)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testReceiveFromChannel$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:101)
+Caused by: kotlinx.coroutines.RecoverableTestException
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testReceiveFromChannel$1$job$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:97)
+ at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
\ No newline at end of file
diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testReceiveFromClosedChannel.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testReceiveFromClosedChannel.txt
new file mode 100644
index 0000000..76c0b1a
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testReceiveFromClosedChannel.txt
@@ -0,0 +1,8 @@
+kotlinx.coroutines.RecoverableTestException
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testReceiveFromClosedChannel$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:110)
+ (Coroutine boundary)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest.channelReceive(StackTraceRecoveryChannelsTest.kt:116)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testReceiveFromClosedChannel$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:111)
+Caused by: kotlinx.coroutines.RecoverableTestException
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testReceiveFromClosedChannel$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:110)
+ at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
\ No newline at end of file
diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testSendFromScope.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testSendFromScope.txt
new file mode 100644
index 0000000..9f93203
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testSendFromScope.txt
@@ -0,0 +1,10 @@
+kotlinx.coroutines.RecoverableTestCancellationException
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testSendFromScope$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:136)
+ (Coroutine boundary)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest.sendInChannel(StackTraceRecoveryChannelsTest.kt:167)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$sendWithContext$2.invokeSuspend(StackTraceRecoveryChannelsTest.kt:162)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$sendFromScope$2.invokeSuspend(StackTraceRecoveryChannelsTest.kt:172)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testSendFromScope$1$deferred$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:126)
+Caused by: kotlinx.coroutines.RecoverableTestCancellationException
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testSendFromScope$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:136)
+ at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
\ No newline at end of file
diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testSendToChannel.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testSendToChannel.txt
new file mode 100644
index 0000000..dab728f
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testSendToChannel.txt
@@ -0,0 +1,12 @@
+java.util.concurrent.CancellationException: RendezvousChannel was cancelled
+ at kotlinx.coroutines.channels.AbstractChannel.cancel(AbstractChannel.kt:630)
+ at kotlinx.coroutines.channels.ReceiveChannel$DefaultImpls.cancel$default(Channel.kt:311)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testSendToChannel$1$job$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:52)
+ (Coroutine boundary)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest.channelSend(StackTraceRecoveryChannelsTest.kt:73)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testSendToChannel$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:56)
+Caused by: java.util.concurrent.CancellationException: RendezvousChannel was cancelled
+ at kotlinx.coroutines.channels.AbstractChannel.cancel(AbstractChannel.kt:630)
+ at kotlinx.coroutines.channels.ReceiveChannel$DefaultImpls.cancel$default(Channel.kt:311)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testSendToChannel$1$job$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:52)
+ at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
\ No newline at end of file
diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testSendToClosedChannel.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testSendToClosedChannel.txt
new file mode 100644
index 0000000..54fdbb3
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/channels/testSendToClosedChannel.txt
@@ -0,0 +1,8 @@
+kotlinx.coroutines.RecoverableTestException
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testSendToClosedChannel$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:43)
+ (Coroutine boundary)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest.channelSend(StackTraceRecoveryChannelsTest.kt:74)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testSendToClosedChannel$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:44)
+Caused by: kotlinx.coroutines.RecoverableTestException
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryChannelsTest$testSendToClosedChannel$1.invokeSuspend(StackTraceRecoveryChannelsTest.kt:43)
+ at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
\ No newline at end of file
diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testEventLoopDispatcher.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testEventLoopDispatcher.txt
new file mode 100644
index 0000000..6b40ec8
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testEventLoopDispatcher.txt
@@ -0,0 +1,12 @@
+kotlinx.coroutines.RecoverableTestException
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.testResumeModeFastPath(StackTraceRecoveryResumeModeTest.kt:61)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testEventLoopDispatcher$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:40)
+ (Coroutine boundary)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$withContext$2.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:76)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.doFastPath(StackTraceRecoveryResumeModeTest.kt:71)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.testResumeModeFastPath(StackTraceRecoveryResumeModeTest.kt:62)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testEventLoopDispatcher$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:40)
+Caused by: kotlinx.coroutines.RecoverableTestException
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.testResumeModeFastPath(StackTraceRecoveryResumeModeTest.kt:61)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testEventLoopDispatcher$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:40)
+ at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
\ No newline at end of file
diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testEventLoopDispatcherSuspending.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testEventLoopDispatcherSuspending.txt
new file mode 100644
index 0000000..5afc559
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testEventLoopDispatcherSuspending.txt
@@ -0,0 +1,10 @@
+otlinx.coroutines.RecoverableTestException
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testResumeModeSuspending$2.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:99)
+ (Coroutine boundary)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$withContext$4.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:116)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.doSuspendingPath(StackTraceRecoveryResumeModeTest.kt:110)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.testResumeModeSuspending(StackTraceRecoveryResumeModeTest.kt:101)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testEventLoopDispatcherSuspending$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:89)
+Caused by: kotlinx.coroutines.RecoverableTestException
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testResumeModeSuspending$2.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:99)
+ at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
\ No newline at end of file
diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedEventLoopChangedContext.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedEventLoopChangedContext.txt
new file mode 100644
index 0000000..406b2d1
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedEventLoopChangedContext.txt
@@ -0,0 +1,13 @@
+kotlinx.coroutines.RecoverableTestException
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.testResumeModeFastPath(StackTraceRecoveryResumeModeTest.kt:61)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testNestedEventLoopChangedContext$1$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:54)
+ (Coroutine boundary)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$withContext$2.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:76)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.doFastPath(StackTraceRecoveryResumeModeTest.kt:71)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.testResumeModeFastPath(StackTraceRecoveryResumeModeTest.kt:62)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testNestedEventLoopChangedContext$1$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:54)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testNestedEventLoopChangedContext$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:53)
+Caused by: kotlinx.coroutines.RecoverableTestException
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.testResumeModeFastPath(StackTraceRecoveryResumeModeTest.kt:61)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testNestedEventLoopChangedContext$1$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:54)
+ at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedEventLoopChangedContextSuspending.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedEventLoopChangedContextSuspending.txt
new file mode 100644
index 0000000..86ec5e4
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedEventLoopChangedContextSuspending.txt
@@ -0,0 +1,11 @@
+kotlinx.coroutines.RecoverableTestException
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testResumeModeSuspending$2.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:113)
+ (Coroutine boundary)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$withContext$4.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:130)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.doSuspendingPath(StackTraceRecoveryResumeModeTest.kt:124)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.testResumeModeSuspending(StackTraceRecoveryResumeModeTest.kt:115)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testNestedEventLoopChangedContextSuspending$1$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:103)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testNestedEventLoopChangedContextSuspending$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:102)
+Caused by: kotlinx.coroutines.RecoverableTestException
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testResumeModeSuspending$2.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:113)
+ at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
\ No newline at end of file
diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedEventLoopDispatcher.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedEventLoopDispatcher.txt
new file mode 100644
index 0000000..d9098bb
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedEventLoopDispatcher.txt
@@ -0,0 +1,13 @@
+kotlinx.coroutines.RecoverableTestException
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.testResumeModeFastPath(StackTraceRecoveryResumeModeTest.kt:61)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testNestedEventLoopDispatcher$1$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:47)
+ (Coroutine boundary)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$withContext$2.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:76)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.doFastPath(StackTraceRecoveryResumeModeTest.kt:71)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.testResumeModeFastPath(StackTraceRecoveryResumeModeTest.kt:62)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testNestedEventLoopDispatcher$1$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:47)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testNestedEventLoopDispatcher$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:46)
+Caused by: kotlinx.coroutines.RecoverableTestException
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.testResumeModeFastPath(StackTraceRecoveryResumeModeTest.kt:61)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testNestedEventLoopDispatcher$1$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:47)
+ at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedEventLoopDispatcherSuspending.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedEventLoopDispatcherSuspending.txt
new file mode 100644
index 0000000..8caed7a
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedEventLoopDispatcherSuspending.txt
@@ -0,0 +1,11 @@
+kotlinx.coroutines.RecoverableTestException
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testResumeModeSuspending$2.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:113)
+ (Coroutine boundary)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$withContext$4.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:130)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.doSuspendingPath(StackTraceRecoveryResumeModeTest.kt:124)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.testResumeModeSuspending(StackTraceRecoveryResumeModeTest.kt:115)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testNestedEventLoopDispatcherSuspending$1$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:96)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testNestedEventLoopDispatcherSuspending$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:95)
+Caused by: kotlinx.coroutines.RecoverableTestException
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testResumeModeSuspending$2.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:113)
+ at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
\ No newline at end of file
diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedUnconfined.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedUnconfined.txt
new file mode 100644
index 0000000..a2cd009
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedUnconfined.txt
@@ -0,0 +1,13 @@
+kotlinx.coroutines.RecoverableTestException
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.testResumeModeFastPath(StackTraceRecoveryResumeModeTest.kt:61)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testNestedUnconfined$1$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:27)
+ (Coroutine boundary)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$withContext$2.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:76)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.doFastPath(StackTraceRecoveryResumeModeTest.kt:71)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.testResumeModeFastPath(StackTraceRecoveryResumeModeTest.kt:62)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testNestedUnconfined$1$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:27)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testNestedUnconfined$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:26)
+Caused by: kotlinx.coroutines.RecoverableTestException
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.testResumeModeFastPath(StackTraceRecoveryResumeModeTest.kt:61)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testNestedUnconfined$1$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:27)
+ at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
\ No newline at end of file
diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedUnconfinedChangedContext.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedUnconfinedChangedContext.txt
new file mode 100644
index 0000000..a786682
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedUnconfinedChangedContext.txt
@@ -0,0 +1,13 @@
+kotlinx.coroutines.RecoverableTestException
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.testResumeModeFastPath(StackTraceRecoveryResumeModeTest.kt:61)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testNestedUnconfinedChangedContext$1$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:34)
+ (Coroutine boundary)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$withContext$2.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:76)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.doFastPath(StackTraceRecoveryResumeModeTest.kt:71)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.testResumeModeFastPath(StackTraceRecoveryResumeModeTest.kt:62)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testNestedUnconfinedChangedContext$1$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:34)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testNestedUnconfinedChangedContext$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:33)
+Caused by: kotlinx.coroutines.RecoverableTestException
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.testResumeModeFastPath(StackTraceRecoveryResumeModeTest.kt:61)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testNestedUnconfinedChangedContext$1$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:34)
+ at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedUnconfinedChangedContextSuspending.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedUnconfinedChangedContextSuspending.txt
new file mode 100644
index 0000000..8c937a7
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedUnconfinedChangedContextSuspending.txt
@@ -0,0 +1,11 @@
+kotlinx.coroutines.RecoverableTestException
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testResumeModeSuspending$2.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:128)
+ (Coroutine boundary)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$withContext$4.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:148)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.doSuspendingPath(StackTraceRecoveryResumeModeTest.kt:140)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.testResumeModeSuspending(StackTraceRecoveryResumeModeTest.kt:130)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testNestedUnconfinedChangedContextSuspending$1$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:95)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testNestedUnconfinedChangedContextSuspending$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:94)
+Caused by: kotlinx.coroutines.RecoverableTestException
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testResumeModeSuspending$2.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:128)
+ at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
\ No newline at end of file
diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedUnconfinedSuspending.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedUnconfinedSuspending.txt
new file mode 100644
index 0000000..b6eef47
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testNestedUnconfinedSuspending.txt
@@ -0,0 +1,11 @@
+kotlinx.coroutines.RecoverableTestException
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testResumeModeSuspending$2.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:128)
+ (Coroutine boundary)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$withContext$4.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:148)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.doSuspendingPath(StackTraceRecoveryResumeModeTest.kt:140)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.testResumeModeSuspending(StackTraceRecoveryResumeModeTest.kt:130)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testNestedUnconfinedSuspending$1$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:88)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testNestedUnconfinedSuspending$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:87)
+Caused by: kotlinx.coroutines.RecoverableTestException
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testResumeModeSuspending$2.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:128)
+ at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
\ No newline at end of file
diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testUnconfined.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testUnconfined.txt
new file mode 100644
index 0000000..9b9cba3
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testUnconfined.txt
@@ -0,0 +1,12 @@
+kotlinx.coroutines.RecoverableTestException
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.testResumeModeFastPath(StackTraceRecoveryResumeModeTest.kt:61)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testUnconfined$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:40)
+ (Coroutine boundary)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$withContext$2.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:76)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.doFastPath(StackTraceRecoveryResumeModeTest.kt:71)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.testResumeModeFastPath(StackTraceRecoveryResumeModeTest.kt:62)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testUnconfined$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:40)
+Caused by: kotlinx.coroutines.RecoverableTestException
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.testResumeModeFastPath(StackTraceRecoveryResumeModeTest.kt:61)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testUnconfined$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:40)
+ at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
\ No newline at end of file
diff --git a/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testUnconfinedSuspending.txt b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testUnconfinedSuspending.txt
new file mode 100644
index 0000000..ca0bbe7
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test-resources/stacktraces/resume-mode/testUnconfinedSuspending.txt
@@ -0,0 +1,9 @@
+kotlinx.coroutines.RecoverableTestException
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testResumeModeSuspending$2.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:128)
+ (Coroutine boundary)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.doSuspendingPath(StackTraceRecoveryResumeModeTest.kt:140)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest.testResumeModeSuspending(StackTraceRecoveryResumeModeTest.kt:130)
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testUnconfinedSuspending$1.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:82)
+Caused by: kotlinx.coroutines.RecoverableTestException
+ at kotlinx.coroutines.exceptions.StackTraceRecoveryResumeModeTest$testResumeModeSuspending$2.invokeSuspend(StackTraceRecoveryResumeModeTest.kt:128)
+ at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
\ No newline at end of file
diff --git a/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryChannelsTest.kt b/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryChannelsTest.kt
new file mode 100644
index 0000000..478b9ad
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryChannelsTest.kt
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.coroutines.exceptions
+
+import kotlinx.coroutines.*
+import kotlinx.coroutines.channels.*
+import org.junit.*
+import org.junit.rules.*
+import kotlin.coroutines.*
+
+class StackTraceRecoveryChannelsTest : TestBase() {
+
+ @get:Rule
+ val name = TestName()
+
+ @Test
+ fun testReceiveFromChannel() = runTest {
+ val channel = Channel<Int>()
+ val job = launch {
+ expect(2)
+ channel.close(RecoverableTestException())
+ }
+
+ expect(1)
+ channelReceive(channel)
+ expect(3)
+ job.join()
+ finish(4)
+ }
+
+ @Test
+ fun testReceiveFromClosedChannel() = runTest {
+ val channel = Channel<Int>()
+ channel.close(RecoverableTestException())
+ channelReceive(channel)
+ }
+
+
+ @Test
+ fun testSendToClosedChannel() = runTest {
+ val channel = Channel<Int>()
+ channel.close(RecoverableTestException())
+ channelSend(channel)
+ }
+
+ @Test
+ fun testSendToChannel() = runTest {
+ val channel = Channel<Int>()
+ val job = launch {
+ expect(2)
+ channel.cancel()
+ }
+
+ expect(1)
+ channelSend(channel)
+ expect(3)
+ job.join()
+ finish(4)
+ }
+
+ private suspend fun channelReceive(channel: Channel<Int>) {
+ try {
+ yield()
+ channel.receive()
+ expectUnreached()
+ } catch (e: RecoverableTestException) {
+ verifyStackTrace("channels/${name.methodName}", e)
+ }
+ }
+
+ private suspend fun channelSend(channel: Channel<Int>) {
+ try {
+ yield()
+ channel.send(1)
+ expectUnreached()
+ } catch (e: Exception) {
+ verifyStackTrace("channels/${name.methodName}", e)
+ }
+ }
+
+ @Test
+ fun testOfferWithCurrentContext() = runTest {
+ val channel = Channel<Int>()
+ channel.close(RecoverableTestException())
+
+ try {
+ channel.sendWithContext(coroutineContext)
+ } catch (e: RecoverableTestException) {
+ verifyStackTrace("channels/${name.methodName}", e)
+ }
+ }
+
+ @Test
+ fun testOfferWithContextWrapped() = runTest {
+ val channel = Channel<Int>()
+ channel.close(RecoverableTestException())
+ try {
+ channel.sendWithContext(wrapperDispatcher(coroutineContext))
+ } catch (e: Exception) {
+ verifyStackTrace("channels/${name.methodName}", e)
+ }
+ }
+
+ @Test
+ fun testOfferFromScope() = runTest {
+ val channel = Channel<Int>()
+ channel.close(RecoverableTestException())
+
+ try {
+ channel.sendFromScope()
+ } catch (e: Exception) {
+ verifyStackTrace("channels/${name.methodName}", e)
+ }
+ }
+
+ // Slow path via suspending send
+ @Test
+ fun testSendFromScope() = runTest {
+ val channel = Channel<Int>()
+ val deferred = async {
+ try {
+ expect(1)
+ channel.sendFromScope()
+ } catch (e: Exception) {
+ verifyStackTrace("channels/${name.methodName}", e)
+ }
+ }
+
+ yield()
+ expect(2)
+ // Cancel is an analogue of `produce` failure, just a shorthand
+ channel.cancel(RecoverableTestCancellationException())
+ finish(3)
+ deferred.await()
+ }
+
+ // See https://github.com/Kotlin/kotlinx.coroutines/issues/950
+ @Test
+ fun testCancelledOffer() = runTest {
+ expect(1)
+ val job = Job()
+ val actor = actor<Int>(job, Channel.UNLIMITED) {
+ consumeEach {
+ expectUnreached() // is cancelled before offer
+ }
+ }
+ job.cancel()
+ try {
+ actor.offer(1)
+ } catch (e: Exception) {
+ verifyStackTrace("channels/${name.methodName}", e)
+ finish(2)
+ }
+ }
+
+ private suspend fun Channel<Int>.sendWithContext(ctx: CoroutineContext) = withContext(ctx) {
+ sendInChannel()
+ yield() // TCE
+ }
+
+ private suspend fun Channel<Int>.sendInChannel() {
+ send(42)
+ yield() // TCE
+ }
+
+ private suspend fun Channel<Int>.sendFromScope() = coroutineScope {
+ sendWithContext(wrapperDispatcher(coroutineContext))
+ }
+}
\ No newline at end of file
diff --git a/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryNestedChannelsTest.kt b/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryNestedChannelsTest.kt
deleted file mode 100644
index 748f0c1..0000000
--- a/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryNestedChannelsTest.kt
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
- */
-
-@file:Suppress("DeferredResultUnused", "DEPRECATION")
-
-package kotlinx.coroutines.exceptions
-
-import kotlinx.coroutines.*
-import kotlinx.coroutines.channels.*
-import org.junit.*
-import kotlin.coroutines.*
-
-class StackTraceRecoveryNestedChannelsTest : TestBase() {
-
- private val channel = Channel<Int>(0)
-
- private suspend fun sendWithContext(ctx: CoroutineContext) = withContext(ctx) {
- sendInChannel()
- yield() // TCE
- }
-
- private suspend fun sendInChannel() {
- channel.send(42)
- yield() // TCE
- }
-
- private suspend fun sendFromScope() = coroutineScope {
- sendWithContext(wrapperDispatcher(coroutineContext))
- }
-
- @Test
- fun testOfferWithCurrentContext() = runTest {
- channel.close(RecoverableTestException())
-
- try {
- yield() // Will be fixed in 1.3.20 after KT-27190
- sendWithContext(coroutineContext)
- } catch (e: Exception) {
- verifyStackTrace(e,
- "kotlinx.coroutines.RecoverableTestException\n" +
- "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryNestedChannelsTest\$testOfferWithCurrentContext\$1.invokeSuspend(StackTraceRecoveryNestedChannelsTest.kt:34)\n" +
- "\t(Coroutine boundary)\n" +
- "\tat kotlinx.coroutines.channels.AbstractSendChannel.offer(AbstractChannel.kt:180)\n" +
- "\tat kotlinx.coroutines.channels.AbstractSendChannel.send(AbstractChannel.kt:168)\n" +
- "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryNestedChannelsTest.sendInChannel(StackTraceRecoveryNestedChannelsTest.kt:24)\n" +
- "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryNestedChannelsTest\$sendWithContext\$2.invokeSuspend(StackTraceRecoveryNestedChannelsTest.kt:19)\n" +
- "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryNestedChannelsTest\$sendWithContext\$2.invoke(StackTraceRecoveryNestedChannelsTest.kt)\n" +
- "\tat kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturn(Undispatched.kt:85)\n" +
- "\tat kotlinx.coroutines.BuildersKt__Builders_commonKt.withContext(Builders.common.kt:146)\n" +
- "\tat kotlinx.coroutines.BuildersKt.withContext(Unknown Source)\n" +
- "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryNestedChannelsTest.sendWithContext(StackTraceRecoveryNestedChannelsTest.kt:18)\n" +
- "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryNestedChannelsTest\$testOfferWithCurrentContext\$1.invokeSuspend(StackTraceRecoveryNestedChannelsTest.kt:37)\n" +
- "Caused by: kotlinx.coroutines.RecoverableTestException\n" +
- "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryNestedChannelsTest\$testOfferWithCurrentContext\$1.invokeSuspend(StackTraceRecoveryNestedChannelsTest.kt:34)\n" +
- "\tat kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:32)\n")
- }
- }
-
- @Test
- fun testOfferWithContextWrapped() = runTest {
- channel.close(RecoverableTestException())
-
- try {
- sendWithContext(wrapperDispatcher(coroutineContext))
- } catch (e: Exception) {
- verifyStackTrace(e,
- "kotlinx.coroutines.RecoverableTestException\n" +
- "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryNestedChannelsTest\$testOfferWithContextWrapped\$1.invokeSuspend(StackTraceRecoveryNestedChannelsTest.kt:59)\n" +
- "\t(Coroutine boundary)\n" +
- "\tat kotlinx.coroutines.channels.AbstractSendChannel.offer(AbstractChannel.kt:180)\n" +
- "\tat kotlinx.coroutines.channels.AbstractSendChannel.send(AbstractChannel.kt:168)\n" +
- "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryNestedChannelsTest.sendInChannel(StackTraceRecoveryNestedChannelsTest.kt:24)\n" +
- "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryNestedChannelsTest\$sendWithContext\$2.invokeSuspend(StackTraceRecoveryNestedChannelsTest.kt:19)\n" +
- "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryNestedChannelsTest\$testOfferWithContextWrapped\$1.invokeSuspend(StackTraceRecoveryNestedChannelsTest.kt:62)\n" +
- "Caused by: kotlinx.coroutines.RecoverableTestException\n" +
- "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryNestedChannelsTest\$testOfferWithContextWrapped\$1.invokeSuspend(StackTraceRecoveryNestedChannelsTest.kt:59)\n" +
- "\tat kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:32)")
- }
- }
-
- @Test
- fun testOfferFromScope() = runTest {
- channel.close(RecoverableTestException())
-
- try {
- sendFromScope()
- } catch (e: Exception) {
- verifyStackTrace(e,
- "kotlinx.coroutines.RecoverableTestException\n" +
- "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryNestedChannelsTest\$testOfferFromScope\$1.invokeSuspend(StackTraceRecoveryNestedChannelsTest.kt:81)\n" +
- "\t(Coroutine boundary)\n" +
- "\tat kotlinx.coroutines.channels.AbstractSendChannel.offer(AbstractChannel.kt:180)\n" +
- "\tat kotlinx.coroutines.channels.AbstractSendChannel.send(AbstractChannel.kt:168)\n" +
- "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryNestedChannelsTest.sendInChannel(StackTraceRecoveryNestedChannelsTest.kt:24)\n" +
- "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryNestedChannelsTest\$sendWithContext\$2.invokeSuspend(StackTraceRecoveryNestedChannelsTest.kt:19)\n" +
- "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryNestedChannelsTest\$sendFromScope\$2.invokeSuspend(StackTraceRecoveryNestedChannelsTest.kt:28)\n" +
- "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryNestedChannelsTest\$testOfferFromScope\$1.invokeSuspend(StackTraceRecoveryNestedChannelsTest.kt:84)\n" +
- "Caused by: kotlinx.coroutines.RecoverableTestException\n" +
- "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryNestedChannelsTest\$testOfferFromScope\$1.invokeSuspend(StackTraceRecoveryNestedChannelsTest.kt:81)\n" +
- "\tat kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:32)")
- }
- }
-
- // Slow path via suspending send
- @Test
- fun testSendFromScope() = runTest {
- val deferred = async {
- try {
- expect(1)
- sendFromScope()
- } catch (e: Exception) {
- verifyStackTrace(e,
- "kotlinx.coroutines.RecoverableTestCancellationException\n" +
- "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryNestedChannelsTest\$testSendFromScope\$1.invokeSuspend(StackTraceRecoveryNestedChannelsTest.kt:118)\n" +
- "\t(Coroutine boundary)\n" +
- "\tat kotlinx.coroutines.channels.AbstractSendChannel.offer(AbstractChannel.kt:180)\n" +
- "\tat kotlinx.coroutines.channels.AbstractSendChannel.send(AbstractChannel.kt:168)\n" +
- "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryNestedChannelsTest.sendInChannel(StackTraceRecoveryNestedChannelsTest.kt:24)\n" +
- "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryNestedChannelsTest\$sendWithContext\$2.invokeSuspend(StackTraceRecoveryNestedChannelsTest.kt:19)\n" +
- "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryNestedChannelsTest\$sendFromScope\$2.invokeSuspend(StackTraceRecoveryNestedChannelsTest.kt:29)\n" +
- "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryNestedChannelsTest\$testSendFromScope\$1\$deferred\$1.invokeSuspend(StackTraceRecoveryNestedChannelsTest.kt:109)\n" +
- "Caused by: kotlinx.coroutines.RecoverableTestCancellationException\n" +
- "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryNestedChannelsTest\$testSendFromScope\$1.invokeSuspend(StackTraceRecoveryNestedChannelsTest.kt:118)\n" +
- "\tat kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:32)")
- }
- }
-
- yield()
- expect(2)
- // Cancel is an analogue of `produce` failure, just a shorthand
- channel.cancel(RecoverableTestCancellationException())
- finish(3)
- deferred.await()
- }
-
- // See https://github.com/Kotlin/kotlinx.coroutines/issues/950
- @Test
- fun testCancelledOffer() = runTest {
- expect(1)
- val job = Job()
- val actor = actor<Int>(job, Channel.UNLIMITED) {
- consumeEach {
- expectUnreached() // is cancelled before offer
- }
- }
- job.cancel()
- try {
- actor.offer(1)
- } catch (e: Exception) {
- verifyStackTrace(e,
- "kotlinx.coroutines.JobCancellationException: Job was cancelled; job=JobImpl{Cancelling}@3af42ad0\n" +
- "\t(Coroutine boundary)\n" +
- "\tat kotlinx.coroutines.channels.AbstractSendChannel.offer(AbstractChannel.kt:186)\n" +
- "\tat kotlinx.coroutines.channels.ChannelCoroutine.offer(ChannelCoroutine.kt)\n" +
- "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryNestedChannelsTest\$testCancelledOffer\$1.invokeSuspend(StackTraceRecoveryNestedChannelsTest.kt:150)\n" +
- "Caused by: kotlinx.coroutines.JobCancellationException: Job was cancelled; job=JobImpl{Cancelling}@3af42ad0\n",
- // ... java.lang.* stuff and JobSupport.* snipped here ...
- "\tat kotlinx.coroutines.Job\$DefaultImpls.cancel\$default(Job.kt:164)\n" +
- "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryNestedChannelsTest\$testCancelledOffer\$1.invokeSuspend(StackTraceRecoveryNestedChannelsTest.kt:148)"
- )
- finish(2)
- }
- }
-}
diff --git a/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryResumeModeTest.kt b/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryResumeModeTest.kt
new file mode 100644
index 0000000..36f6052
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryResumeModeTest.kt
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.coroutines.exceptions
+
+import kotlinx.coroutines.*
+import kotlinx.coroutines.channels.*
+import org.junit.*
+import kotlin.coroutines.*
+import org.junit.rules.TestName
+import org.junit.Rule
+
+class StackTraceRecoveryResumeModeTest : TestBase() {
+
+ @get:Rule
+ val testName = TestName()
+
+ @Test
+ fun testUnconfined() = runTest {
+ testResumeModeFastPath(Dispatchers.Unconfined)
+ }
+
+ @Test
+ fun testNestedUnconfined() = runTest {
+ withContext(Dispatchers.Unconfined) {
+ testResumeModeFastPath(Dispatchers.Unconfined)
+ }
+ }
+
+ @Test
+ fun testNestedUnconfinedChangedContext() = runTest {
+ withContext(Dispatchers.Unconfined) {
+ testResumeModeFastPath(CoroutineName("Test"))
+ }
+ }
+
+ @Test
+ fun testEventLoopDispatcher() = runTest {
+ testResumeModeFastPath(wrapperDispatcher())
+ }
+
+ @Test
+ fun testNestedEventLoopDispatcher() = runTest {
+ val dispatcher = wrapperDispatcher()
+ withContext(dispatcher) {
+ testResumeModeFastPath(dispatcher)
+ }
+ }
+
+ @Test
+ fun testNestedEventLoopChangedContext() = runTest {
+ withContext(wrapperDispatcher()) {
+ testResumeModeFastPath(CoroutineName("Test"))
+ }
+ }
+
+ private suspend fun testResumeModeFastPath(context: CoroutineContext) {
+ try {
+ val channel = Channel<Int>()
+ channel.close(RecoverableTestException())
+ doFastPath(context, channel)
+ } catch (e: Throwable) {
+ verifyStackTrace("resume-mode/${testName.methodName}", e)
+ }
+ }
+
+ private suspend fun doFastPath(context: CoroutineContext, channel: Channel<Int>) {
+ yield()
+ withContext(context, channel)
+ }
+
+ private suspend fun withContext(context: CoroutineContext, channel: Channel<Int>) {
+ withContext(context) {
+ channel.receive()
+ yield()
+ }
+ }
+
+ @Test
+ fun testUnconfinedSuspending() = runTest {
+ testResumeModeSuspending(Dispatchers.Unconfined)
+ }
+
+ @Test
+ fun testNestedUnconfinedSuspending() = runTest {
+ withContext(Dispatchers.Unconfined) {
+ testResumeModeSuspending(Dispatchers.Unconfined)
+ }
+ }
+
+ @Test
+ fun testNestedUnconfinedChangedContextSuspending() = runTest {
+ withContext(Dispatchers.Unconfined) {
+ testResumeModeSuspending(CoroutineName("Test"))
+ }
+ }
+
+ @Test
+ fun testEventLoopDispatcherSuspending() = runTest {
+ testResumeModeSuspending(wrapperDispatcher())
+ }
+
+ @Test
+ fun testNestedEventLoopDispatcherSuspending() = runTest {
+ val dispatcher = wrapperDispatcher()
+ withContext(dispatcher) {
+ testResumeModeSuspending(dispatcher)
+ }
+ }
+
+ @Test
+ fun testNestedEventLoopChangedContextSuspending() = runTest {
+ withContext(wrapperDispatcher()) {
+ testResumeModeSuspending(CoroutineName("Test"))
+ }
+ }
+
+ private suspend fun testResumeModeSuspending(context: CoroutineContext) {
+ try {
+ val channel = Channel<Int>()
+ val latch = Channel<Int>()
+ GlobalScope.launch(coroutineContext) {
+ latch.receive()
+ expect(3)
+ channel.close(RecoverableTestException())
+ }
+ doSuspendingPath(context, channel, latch)
+ } catch (e: Throwable) {
+ finish(4)
+ verifyStackTrace("resume-mode/${testName.methodName}", e)
+ }
+ }
+
+ private suspend fun doSuspendingPath(context: CoroutineContext, channel: Channel<Int>, latch: Channel<Int>) {
+ yield()
+ withContext(context, channel, latch)
+ }
+
+ private suspend fun withContext(context: CoroutineContext, channel: Channel<Int>, latch: Channel<Int>) {
+ withContext(context) {
+ expect(1)
+ latch.send(1)
+ expect(2)
+ channel.receive()
+ yield()
+ }
+ }
+}
\ No newline at end of file
diff --git a/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryTest.kt b/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryTest.kt
index 0f87960..3cddee5 100644
--- a/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryTest.kt
+++ b/kotlinx-coroutines-core/jvm/test/exceptions/StackTraceRecoveryTest.kt
@@ -8,7 +8,9 @@
import kotlinx.coroutines.channels.*
import kotlinx.coroutines.intrinsics.*
import kotlinx.coroutines.selects.*
+import org.junit.*
import org.junit.Test
+import org.junit.rules.*
import java.util.concurrent.*
import kotlin.concurrent.*
import kotlin.coroutines.*
@@ -85,55 +87,6 @@
}
@Test
- fun testReceiveFromChannel() = runTest {
- val channel = Channel<Int>()
- val job = launch {
- expect(2)
- channel.close(IllegalArgumentException())
- }
-
- expect(1)
- channelNestedMethod(
- channel,
- "java.lang.IllegalArgumentException\n" +
- "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest\$testReceiveFromChannel\$1\$job\$1.invokeSuspend(StackTraceRecoveryTest.kt:93)\n" +
- "\t(Coroutine boundary)\n" +
- "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest.channelNestedMethod(StackTraceRecoveryTest.kt:110)\n" +
- "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest\$testReceiveFromChannel\$1.invokeSuspend(StackTraceRecoveryTest.kt:89)",
- "Caused by: java.lang.IllegalArgumentException\n" +
- "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest\$testReceiveFromChannel\$1\$job\$1.invokeSuspend(StackTraceRecoveryTest.kt:93)\n" +
- "\tat kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:32)\n")
- expect(3)
- job.join()
- finish(4)
- }
-
- @Test
- fun testReceiveFromClosedChannel() = runTest {
- val channel = Channel<Int>()
- channel.close(IllegalArgumentException())
- channelNestedMethod(
- channel,
- "java.lang.IllegalArgumentException\n" +
- "\t(Coroutine boundary)\n" +
- "\tat kotlinx.coroutines.channels.AbstractChannel.receiveResult(AbstractChannel.kt:574)\n" +
- "\tat kotlinx.coroutines.channels.AbstractChannel.receive(AbstractChannel.kt:567)\n" +
- "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest.channelNestedMethod(StackTraceRecoveryTest.kt:117)\n" +
- "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest\$testReceiveFromClosedChannel\$1.invokeSuspend(StackTraceRecoveryTest.kt:111)\n",
- "Caused by: java.lang.IllegalArgumentException\n" +
- "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest\$testReceiveFromClosedChannel\$1.invokeSuspend(StackTraceRecoveryTest.kt:110)")
- }
-
- private suspend fun channelNestedMethod(channel: Channel<Int>, vararg traces: String) {
- try {
- channel.receive()
- expectUnreached()
- } catch (e: IllegalArgumentException) {
- verifyStackTrace(e, *traces)
- }
- }
-
- @Test
fun testWithContext() = runTest {
val deferred = async<Unit>(NonCancellable, start = CoroutineStart.LAZY) {
throw RecoverableTestException()
@@ -312,4 +265,32 @@
"\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest\$testNonDispatchedRecovery\$await\$1.invokeSuspend(StackTraceRecoveryTest.kt:291)\n" +
"Caused by: kotlinx.coroutines.RecoverableTestException")
}
+
+ private class Callback(val cont: CancellableContinuation<*>)
+
+ @Test
+ fun testCancellableContinuation() = runTest {
+ val channel = Channel<Callback>(1)
+ launch {
+ try {
+ awaitCallback(channel)
+ } catch (e: Throwable) {
+ verifyStackTrace(e, "kotlinx.coroutines.RecoverableTestException\n" +
+ "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest\$testCancellableContinuation\$1.invokeSuspend(StackTraceRecoveryTest.kt:329)\n" +
+ "\t(Coroutine boundary)\n" +
+ "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest.awaitCallback(StackTraceRecoveryTest.kt:348)\n" +
+ "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest\$testCancellableContinuation\$1\$1.invokeSuspend(StackTraceRecoveryTest.kt:322)\n" +
+ "Caused by: kotlinx.coroutines.RecoverableTestException\n" +
+ "\tat kotlinx.coroutines.exceptions.StackTraceRecoveryTest\$testCancellableContinuation\$1.invokeSuspend(StackTraceRecoveryTest.kt:329)")
+ }
+ }
+ val callback = channel.receive()
+ callback.cont.resumeWithException(RecoverableTestException())
+ }
+
+ private suspend fun awaitCallback(channel: Channel<Callback>) {
+ suspendCancellableCoroutine<Unit> { cont ->
+ channel.offer(Callback(cont))
+ }
+ }
}
diff --git a/kotlinx-coroutines-core/jvm/test/exceptions/Stacktraces.kt b/kotlinx-coroutines-core/jvm/test/exceptions/Stacktraces.kt
index 1588433..40c6930 100644
--- a/kotlinx-coroutines-core/jvm/test/exceptions/Stacktraces.kt
+++ b/kotlinx-coroutines-core/jvm/test/exceptions/Stacktraces.kt
@@ -1,5 +1,6 @@
package kotlinx.coroutines.exceptions
+import kotlinx.coroutines.*
import java.io.*
import kotlin.test.*
@@ -19,6 +20,12 @@
assertEquals(traces.map { it.count("Caused by") }.sum(), causes)
}
+public fun verifyStackTrace(path: String, e: Throwable) {
+ val resource = Job.javaClass.classLoader.getResourceAsStream("stacktraces/$path.txt")
+ val lines = resource.reader().readLines()
+ verifyStackTrace(e, *lines.toTypedArray())
+}
+
public fun toStackTrace(t: Throwable): String {
val sw = StringWriter() as Writer
t.printStackTrace(PrintWriter(sw))