-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
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
Tokio in current_thread not releasing open file handles once the limit is reached. #4782
Comments
@nohupped this reproduces more reliably: use std::fs::remove_file;
use tokio::net::{UnixListener, UnixStream};
use ulimit_echo::SOCK;
#[tokio::main(flavor = "current_thread")]
async fn main() {
let _ = remove_file(SOCK);
let sock = UnixListener::bind(SOCK).unwrap();
loop {
match sock.accept().await {
Ok((stream, _)) => {
tokio::spawn(handle_stream(stream));
},
Err(e) => eprintln!("Failed to accept: {:?}", e),
}
}
}
async fn handle_stream(mut stream: UnixStream) {
let (mut rx, mut tx) = stream.split();
tokio::io::copy(&mut rx, &mut tx).await.unwrap();
}
use tokio::io::AsyncWriteExt;
use tokio::net::UnixStream;
use ulimit_echo::SOCK;
#[tokio::main(flavor = "current_thread")]
async fn main() {
loop {
match UnixStream::connect(SOCK).await {
Ok(stream) => {
tokio::spawn(handle_stream(stream));
}
Err(e) => eprintln!("Error in creating stream: {:?}", e),
}
}
}
async fn handle_stream(mut stream: UnixStream) {
stream.write_all(b"UNLIMITED FILES!!!").await.unwrap();
let (mut rx, mut tx) = stream.split();
tokio::io::copy(&mut rx, &mut tx).await.unwrap();
}
pub const SOCK: &str = "sock"; |
Adding |
Might be a budgeting issue? |
I'll look at this more later tonight. |
Using the unstable |
Yes, this does appear to be due to |
@Darksonn I think the issue is in |
Right, good point. That was a typo. |
@nohupped workaround for now is to use |
More generally though, you probably don't want to be hot-looping over accept errors. Sleeping for a bit of time after an error comes out of accept would keep the CPU from needlessly getting slammed. |
@sfackler I agree completely especially with a ulimit issue, however I do think that it is problematic that we just busy-loop here. |
Thank you. I tried the yield_now, and that's working for me.
Thank you. I'm planning to add that. Before using a |
Fixes #5946, #4782. This change adds budgeting to most of the remaining unbudgeted IO operations which can complete instantly, including datagram send/recv operations and listener socket accepts. This is particularly significant for scenarios in which resource limits are hit, as it can be common for things like listener tasks to spin when receiving errors and just log them, busy looping worker threads which might otherwise be handling existing connections and closing them. This can also sometimes lead to complex failure scenarios within datagram systems experiencing resource exhaustion.
Fixes #5946, #4782. This change adds budgeting to most of the remaining unbudgeted IO operations which can complete instantly, including datagram send/recv operations and listener socket accepts. This is particularly significant for scenarios in which resource limits are hit, as it can be common for things like listener tasks to spin when receiving errors and just log them, busy looping worker threads which might otherwise be handling existing connections and closing them. This can also sometimes lead to complex failure scenarios within datagram systems experiencing resource exhaustion.
Fixes #5946. Fixes #4782. This change adds budgeting to most of the remaining unbudgeted IO operations which can complete instantly, including datagram send/recv operations and listener socket accepts. This is particularly significant for scenarios in which resource limits are hit, as it can be common for things like listener tasks to spin when receiving errors and just log them, busy looping worker threads which might otherwise be handling existing connections and closing them. This can also sometimes lead to complex failure scenarios within datagram systems experiencing resource exhaustion.
Fixes #5946. Fixes #4782. This change adds budgeting to most of the remaining unbudgeted IO operations which can complete instantly, including datagram send/recv operations and listener socket accepts. This is particularly significant for scenarios in which resource limits are hit, as it can be common for things like listener tasks to spin when receiving errors and just log them, busy looping worker threads which might otherwise be handling existing connections and closing them. This can also sometimes lead to complex failure scenarios within datagram systems experiencing resource exhaustion.
Fixes #5946. Fixes #4782. This change adds budgeting to most of the remaining unbudgeted IO operations which can complete instantly, including datagram send/recv operations and listener socket accepts. This is particularly significant for scenarios in which resource limits are hit, as it can be common for things like listener tasks to spin when receiving errors and just log them, busy looping worker threads which might otherwise be handling existing connections and closing them. This can also sometimes lead to complex failure scenarios within datagram systems experiencing resource exhaustion.
Version
cargo tree | grep tokio
Platform
The output of
uname -a
(UNIX), or version and 32 or 64-bit (Windows)Description
While reading from a Unix Socket over a buffered stream, the tokio executor in current_thread doesn't release open files once the open file OS limit is hit and will not accept any more incoming requests, but if the number of open files stays within the OS limit, the file handles are released.
If the executor is switched to a multi_threaded one, this issue doesn't happen. Although the open file limits are hit, it frees the openfiles and the program continues to work.
I am not sure if I am using the BufStream wrong or I might have overlooked something.
I tried this code:
A minimal version of the server code that accepts connection over a unix socket and prints it
src/main.rs
Client code that generates parallel requests
src/client/main.rs
cargo.toml
I expected to see this happen:
When the server is run as
and when the client is run as
I expect to see
from the server's stdout once the
ulimit
to openfiles are hit, but I also expect it to recover after the client is stopped.Instead, this happened:
With the above code, the server keeps on printing
even after the client is exited. I watched the output of
lsof
for some time and it was as belowThe number of open file handles to the socket never comes down because it was never released.
Note:
This happens only when running in
current_thread
. If the executer is switched tomulti_threaded
by running the server as./server --worker-threads=1
, even if the server hits open file limit, it recovers andlsof
output shows the number of open filehandles to the socket coming down.I tried to reproduce this in a docker running on my mac, but it didn't occur. I tried running this on baremetal linux and linux running on vmware fusion and I was able to reproduce this.
I have this code added into my repo if anybody want to try it locally on a linux machine. (https://github.com/nohupped/buggy)
The text was updated successfully, but these errors were encountered: