-
Notifications
You must be signed in to change notification settings - Fork 1.9k
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
Flow hangs even when using terminal operators #4035
Comments
Sorry, but could you clarify your intentions and why this doesn't match them? Your code literally says: "await cancellation after everything's finished if the flow completed without errors and didn't need to be cancelled internally." |
@dkhalanskyjb the behavior is inconsistent, e.g.
Hangs
Doesn't Similarly,
doesn't hang. So the same (from functional perspective) chain behaves differently.
In this case, that means the second and third snippets are actually those with a bug |
Adding or removing flow operators can change the number of suspensions that collection goes through. That's expected and is intentionally left as an implementation detail. In a future release, the behavior may change. In effect, you have a race condition in your code: either the collection finishes before the last element is emitted, or it doesn't. So, there's no bug so far. |
But Also, how can there be a race condition when a single thread is involved? The code also should not have any issues since an operator like I see the timeline as:
Judging by the implementation, I don't see how this code should behave differently than: flowOf(1)
.take(1)
.let { upstream ->
flow {
emitAll(upstream)
awaitCancellation()
}
} I don't see why/how |
public fun <T> flowOf(vararg elements: T): Flow<T> = flow {
for (element in elements) {
emit(element)
}
// <-- this line
} // <-- this brace (
After the last We could tweak this behavior of
Easy: runBlocking {
launch {
repeat(100) { yield() }
println("A")
}
launch {
repeat(100) { yield() }
println("B")
}
} A slight change in implementation of either The code is still deterministic: rerunning it several times will yield the same result. But the race is there: there's no explicit dependency between the moments of
This would be the case, but the result of
The problem is at point 2. It essentially emits a "here's the item, but also, I'm done." |
Though this is all theoretical. It can be fun to dig into implementation details, but clearly, no one would write the code like the one in the opening post: they'd just write |
This is interesting and super helpful.
But looking at the implementation, I see that |
Oh, yeah. Yes, you are right. This is not due to an extra suspension in A side note: now that I think about it, a well-behaved I've localized the issue. Here's the fun part: the following will also hang! flowOf(1, 2, 3)
.take(100) // won't hang if this is removed
.onCompletion { if (it == null) awaitCancellation() }
.first() That's clearly a bug, because
|
Ah, great find. I think I understand a bit more now. Does this mean that an operator like |
No-no, the opposite: The issue is due to an internal implementation detail: when |
Ah, so |
Before this change, it could happen that some size-limiting operators upstream swallowed the requests to limit the flow size emitted by the operators downstream. This could cause `onCompletion` calls between these operators to incorrectly report that the flow was not in fact limited by the downstream operators. Additionally, in the presence of additional size-limiting operators in the chain, `first` and `single` and their variants could exhibit incorrect behavior where emitting a value from `onCompletion` would overwrite their output. Fixes #4035
Describe the bug
A flow that uses
take(1)
,onCompletion { if (it == null) awaitCancellation() }
andfirst()
can hang forever instead of returning the first value.Interestingly, removing
take(1)
will cause the flow to behave as expected.Provide a Reproducer
Here is such an example:
This code is expected to return
1
, instead if suspends forever. If you removetake(1)
it returns1
as expected.The text was updated successfully, but these errors were encountered: