-
Notifications
You must be signed in to change notification settings - Fork 7.6k
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
3.x and 2.x - Schedulers.io() fails to produce new thread when needed, leading to deadlock #7153
Comments
Hi. Indeed the operator releases the worker and then blocks on it, preventing any progress when the worker is reused. The only workaround is to not block on the code path and avoid blocking inside flows in general. The current behavior is a trade-off. In some cases, a late release would lead to excess thread creation. |
Thanks for your feedback @akarnokd, As a workaround, I did this modification in
I ran some tests in a complex app and the number of total threads created was similar. I see your point but with the trade-off. But in my opinion having no deadlocks is more important than a temporary thread excess. Or maybe it's something that we can allow to be configured externally. Regarding my workaround, I'd really appreciate your opinion on the code as I'm not very familiar with the library internals. Thanks! |
There is no reason to use RxJava in such a blocking fashion; there are operators that can get you in-sequence execution.
The problem was the sustained creation of excess threads as the scheduled release couldn't keep up. At best, we might add a system parameter to enable delayed release, similar to your code. However, |
Thanks for the feedback. Blocking operators are frequently abused. And it's usually a red flag when you are reviewing code. Let me try to explain why I was blocking in the first place. Here it's some pseudo-code to illustrate my point:
For those two reasons I don't see how blocking can be avoided here. Regarding the workaround. I really appreciate your suggestion.
I run many unit tests, and I've also tested with a real-world app, and I didn't see a noticeable increase of threads. I'd like to do a PR with a flag to turn it on, but I'm not sure about the impact on other places, or maybe the same logic should be applied to other Thanks for taking a look at this |
This release only affects the IO scheduler. Alright, you can post a PR along these lines:
@Override
public void dispose() {
if (once.compareAndSet(false, true)) {
tasks.dispose();
if (USE_SCHEDULED_RELEASE) {
threadWorker.scheduleActual(this, 0, TimeUnit.NANOSECONDS, null);
} else {
// releasing the pool should be the last action
pool.release(threadWorker);
}
}
}
@Override
public void run() {
pool.release(threadWorker);
} |
I've tested this with the latest versions of 3.x and 2.x (v3.0.9 and v2.2.20 at the time)
If my understanding is correct,
Schedulers.io()
should create a new thread if all the other threads are busy. This is not happening here, and it leads to a deadlock.I managed to simplify our code to this minimal reproducible example. I'm aware that
.blockingAwait()
doesn't make sense here but it does in the original codeThe issue is easier to reproduce if the IO thread pool contains just a few threads, but it can still happen with bigger pools.
I think it can be related to some operator releasing the worker too soon. It will work fine if I replace the
firstOrError
operator like thisAny help with this is much appreciated.
Cheers
The text was updated successfully, but these errors were encountered: