fix: survive busy sessions without exhausting file descriptors
The peer listener died with "Too many open files (os error 24)" on any long-lived non-daemon session: only the daemon raised RLIMIT_NOFILE, and peer sockets were unbounded — no idle reaping, no deadlines on accepted transfer streams, and one peer could hold two connections under split registry identities. - raise the open-file limit at the top of run(), for every entry point, and warn from Client::connect when the soft limit is below 1024 - reap peer control connections idle for five minutes; a clean disconnect no longer discards the peer's queued uploads - give listener-supplied download streams and upload serving the same read/write deadlines dialled sockets already had - register inbound peers under their plain username so the registry holds one connection per peer, and announce them like pierced ones - hold the registry lock across actor spawn + insert and surface thread-spawn failure, so an instantly-dead peer cannot leave a zombie entry
This commit is contained in:
@@ -2,7 +2,7 @@ use super::{
|
||||
Arc, Client, ClientContext, ClientOperation, ConnectionType, DownloadPeer,
|
||||
DownloadStatus, Listen, Peer, PeerRegistry, Receiver, Result, RwLock,
|
||||
RwLockExt, Sender, ServerActor, ServerMessage, Shares, SoulseekRs,
|
||||
TcpStream, error, info, mpsc, thread, trace,
|
||||
TcpStream, error, info, mpsc, thread, trace, warn,
|
||||
};
|
||||
|
||||
/// Ceiling on the wait for a login verdict. Generous enough for a slow server
|
||||
@@ -10,8 +10,40 @@ use super::{
|
||||
const LOGIN_RESPONSE_TIMEOUT: std::time::Duration =
|
||||
std::time::Duration::from_secs(45);
|
||||
|
||||
/// Soft open-file limit below which a session should expect "too many open
|
||||
/// files": every peer holds a socket, and one busy search talks to hundreds
|
||||
/// of them.
|
||||
const COMFORTABLE_OPEN_FILES: u64 = 1024;
|
||||
|
||||
/// Say up front when the process cannot hold a busy session's sockets, so an
|
||||
/// operator reads a warning with a number in it instead of an EMFILE storm an
|
||||
/// hour later. The embedding process owns its rlimits; the library only looks.
|
||||
#[cfg(unix)]
|
||||
fn warn_if_open_file_limit_is_low() {
|
||||
// SAFETY: getrlimit only fills the struct it is handed.
|
||||
let limit = unsafe {
|
||||
let mut limit = std::mem::zeroed::<libc::rlimit>();
|
||||
if libc::getrlimit(libc::RLIMIT_NOFILE, &raw mut limit) != 0 {
|
||||
return;
|
||||
}
|
||||
limit.rlim_cur
|
||||
};
|
||||
if limit < COMFORTABLE_OPEN_FILES {
|
||||
warn!(
|
||||
"only {limit} open files allowed; a busy session wants \
|
||||
{COMFORTABLE_OPEN_FILES} or more — expect \"too many open \
|
||||
files\" under load"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(unix))]
|
||||
fn warn_if_open_file_limit_is_low() {}
|
||||
|
||||
impl Client {
|
||||
pub fn connect(&mut self) -> Result<()> {
|
||||
warn_if_open_file_limit_is_low();
|
||||
|
||||
let (sender, message_reader): (
|
||||
Sender<ClientOperation>,
|
||||
Receiver<ClientOperation>,
|
||||
|
||||
Reference in New Issue
Block a user