Comments (10)
Why is nested2(1, 2, 3) not returning "outer timeout"?
- The outer timeout fires at 1 second, and makes
delay(3)
resume with aCancellationException
- The inner
withTimeoutOrNull
catches the exception, concludes that it timed out, returns null which gets converted to the string "inner timeout" - The block in outer timeout completes with a valid value ("inner timeout"), has no idea it actually timed out because the
CancellationException
was swallowed by the innerwithTimeoutOrNull
For the other cases:
test(1, 3, 2)
is identical totest(1, 2, 3)
- outer times out while suspended in thedelay()
test(2, 1, 3)
: inner times out first so you get "inner timeout" as expected (ie: behavior is correct)test(2, 3, 1)
: delay is lower than time outs, so you get the correct behaviortest(3, 1, 2)
: inner times out first, so you get the correct behaviortest(3, 2, 1)
: delay is lower than time outs, so you get the correct behavior
For reference, the output of the test above is:
test(1, 2, 3) = { 1: inner timeout, 2: inner timeout }
test(1, 3, 2) = { 1: inner timeout, 2: inner timeout }
test(2, 1, 3) = { 1: inner timeout, 2: inner timeout }
test(2, 3, 1) = { 1: delay, 2: delay }
test(3, 1, 2) = { 1: inner timeout, 2: inner timeout }
test(3, 2, 1) = { 1: delay, 2: delay }
from kotlinx.coroutines.
Modifiying nextValueWithTimout
like so:
suspend fun nextValueWithTimout(t: Long): Int? {
val result = withTimeoutOrNull(t) { nexValue() }
yield()
return result
}
Fixes the issue for me, but Iām not sure I fully understand the semantics of yield. Is this logically correct? Iām worried that the call to yield()
might suspend my coroutine for an undetermined amount of time.
from kotlinx.coroutines.
Yield just reschedules the task and checks for cancellation. That is why your particular problem is fixed. It does not address the core issue itself. The root questions here is what semantics should withTimeoutOrNull
have when some other withTimeout
invocation (either inside or outside of it) times out.
from kotlinx.coroutines.
I'm personally inclined to rule that withTimeoutOrNull
shall return null only if its own timeout is exceeded and change its implementation accordingly.
from kotlinx.coroutines.
And same goes for withTimeout
, right? Should return null only if its own timeout fires.
from kotlinx.coroutines.
There is no problem with withTimeout
. It throws an exception on timeout and always rethrows an inner exception if any. It cannot/should not consume or alter inner exception in any way, so withTimeout
does not provide any way to distinguish which timeout had fired and it has to stay this way.
from kotlinx.coroutines.
I see. I originally encountered this problem because I wasn't aware of withTimeoutOrNull
(it looks like it's a recent addition) and was doing things this way:
suspend fun nextValueWithTimout(t: Long): Int? {
try {
return withTimeout(t) { nexValue() }
catch (e: CancellationException) {
return null
}
}
I understand they might be different use cases, but from a user point of view this way of doing things also looks pretty innocent at first. Hard to see that you're shooting yourself in the foot here.
I wonder if there's a way to make it harder for users to do stupid things like this.
from kotlinx.coroutines.
Another test program. I'm surprised that both nested1
and nested2
print the same value for all permutations. Why is nested2(1, 2, 3)
not returning "outer timeout"?
import kotlinx.coroutines.experimental.*
import java.util.concurrent.TimeUnit
val S = TimeUnit.SECONDS
suspend fun <T> withTimeoutOrNull2(time: Long, unit: TimeUnit, block: suspend () -> T): T? = try {
withTimeout(time, unit) {
block()
}
} catch (_: CancellationException) { null }
suspend fun nested1(outer: Long, inner: Long, delayed: Long) =
withTimeoutOrNull(outer, S) {
withTimeoutOrNull(inner, S) {
delay(delayed, S)
"delay"
} ?: "inner timeout"
} ?: "outer timeout"
suspend fun nested2(outer: Long, inner: Long, delayed: Long) =
withTimeoutOrNull2(outer, S) {
withTimeoutOrNull2(inner, S) {
delay(delayed, S)
"delay"
} ?: "inner timeout"
} ?: "outer timeout"
suspend fun test(outer: Long, inner: Long, delayed: Long) {
val result1 = nested1(outer, inner, delayed)
val result2 = nested2(outer, inner, delayed)
println("test($outer, $inner, $delayed) = { 1: $result1, 2: $result2 }")
}
fun main(args: Array<String>) = runBlocking {
test(1, 2, 3)
test(1, 3, 2)
test(2, 1, 3)
test(2, 3, 1)
test(3, 1, 2)
test(3, 2, 1)
}
from kotlinx.coroutines.
FYI, here's my workaround to the problem:
/**
* Throws if the current coroutine is completed or cancelled
*/
suspend fun checkStillActive() {
suspendCoroutine<Unit> { cont ->
val job = cont.context[Job]
if (job?.isCompleted == true) throw job.getCompletionException()
cont.resume(Unit)
}
}
The call checkStillActive()
after each call to withTimeout[OrNull]
, like so:
suspend fun nested1(outer: Long, inner: Long, delayed: Long) =
withTimeoutOrNull(outer, S) {
val result = withTimeoutOrNull(inner, S) {
delay(delayed, S)
"delay"
} ?: "inner timeout"
checkStillActive() // <---- here
result
} ?: "outer timeout"
And:
suspend fun <T> withTimeoutOrNull2(time: Long, unit: TimeUnit, block: suspend () -> T): T? = try {
withTimeout(time, unit) {
block()
}
} catch (_: CancellationException) {
checkStillActive() // <---- here
null
}
This gives the correct results:
test(1, 2, 3) = { 1: outer timeout, 2: outer timeout }
test(1, 3, 2) = { 1: outer timeout, 2: outer timeout }
test(2, 1, 3) = { 1: inner timeout, 2: inner timeout }
test(2, 3, 1) = { 1: delay, 2: delay }
test(3, 1, 2) = { 1: inner timeout, 2: inner timeout }
test(3, 2, 1) = { 1: delay, 2: delay }
from kotlinx.coroutines.
Fixed in develop
branch
from kotlinx.coroutines.
Related Issues (20)
- `withContext` may execute code in the wrong context if the `coroutineContext` misleads it HOT 3
- SharedFlow doesn't have same parameters as in constructor function HOT 2
- Consider deprecation cycle for `CoroutineDispatcher.invoke` HOT 6
- How to prevent a SharedFlow collect values when the activity resumes? HOT 1
- Consider discouraging `CoroutineStart.LAZY`
- Some problem about `addLast` in LockFreeTaskQueue HOT 1
- Maybe it's reasonable to recommend wrapping a `callbackFlow` initialization into `try`-`finally`? HOT 4
- Dispatcher failures may leave coroutines uncompleted HOT 1
- `DelayWithTimeoutDiagnostics` missing from R8 rules? HOT 3
- Introduce Flow.all/any/none operators HOT 3
- [WASM] JsException: Exception was thrown while running JavaScript code kotlinx.coroutines.error_$external_fun HOT 6
- Suppressed exceptions are lost during stacktrace recovery HOT 1
- `runBlocking` executes other tasks instead of its own HOT 1
- "Kotlin Compiler Error: NoClassDefFoundError for kotlin/reflect/full/KClasses during Gradle build" HOT 1
- Add argument to sample to sample at the start of a flow instead of the end? HOT 5
- Mapping a list asynchronously HOT 2
- rx Flowable that use Unconfined dispatcher result in a deadlock when using a runblocking() that execute a suspend function with (Semaphore, mutex or ReentrantLock) HOT 1
- Some way of subscribe to shared flow without slowing-down others HOT 7
- Wrong function's return type called inside lambda when using launch HOT 11
- Flow collect may cause the Gamepad testing failed when I use webview to loading the Gamepad testing url after used collect function HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
š Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. ššš
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ā¤ļø Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from kotlinx.coroutines.