Newly created Jobs considered as cancelled after only 1 occurence of an exception
See original GitHub issueHello,
I am trying to build an async flow whose role is to merge identical calls into one (but that is not really related to this bug) https://github.com/libetl/coroutines-pooling-client
I am using an CoroutineScope.async operation where I happen to throw an exception in case of an error received from the user implementation. https://github.com/libetl/coroutines-pooling-client/blob/master/src/main/kotlin/org/toilelibre/libe/poolingclient/PoolingClientStrategy.kt#L153
The flow starts failing systematically once the first exception bubbled up, making all the coroutine executor threads to vanish.
Once the first ResultFailure is read, the statement https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-core/common/src/CompletedExceptionally.kt#L12 always decides to use CompletedExceptionally class while I am expecting to have a call to throwOnFailure().
As a result, every subsequent call to the same async operation will call recoverAndThrow
https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-core/jvm/src/internal/StackTraceRecovery.kt#L150
and will keep on returning the previously bubbled up exception instead of re-executing the user implementation.
- Did I make myself clear enough in the above description ?
- Is it an expected behaviour ?
- What could have gone wrong ?
- Thank you for your help
Issue Analytics
- State:
- Created 4 years ago
- Comments:5 (2 by maintainers)
Thanks, that was exactly the piece of advice I needed. I changed the context to be SupervisorJob and the new jobs remain healthy even after a failure.
All your async jobs share a common parent job, which lives in
coroutineScope
. The first time any of those jobs fail (you throw an UOE), that exception gets stored in theDeferred
, but it also gets reported to the parent job, incoroutineScope
, which cancels that job and stores the failure there as well. This happens The first timetoRun[0]
callsawait()
. The next time you try using that job to launch a new coroutine (i.e. whentoRun[1]
callsawait
), it won’t even get started because the parent job is already cancelled, and the resultingDeferred
will just immediately report the same error.async
is weird like that, it reports exceptions both up (to its parent) and down (to theDeferred
).In your test, you can fix this by making the parent job a
SupervisorJob
:SupervisorJob
“is similar to a regular Job with the only exception that cancellation is propagated only downwards.”