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:
Michel de Graaf
2026-08-09 22:34:36 +02:00
parent d11326f149
commit 0bbb559f68
17 changed files with 442 additions and 132 deletions
+11
View File
@@ -42,6 +42,17 @@ pub fn serve_file(
})?;
let mut stream =
TcpStream::connect_timeout(&socket, Duration::from_secs(20))?;
// A downloader that connects and then goes silent must cost seconds, not
// the rest of the session: without deadlines a wedged `read_exact` (or a
// zero-window `write_all`) holds this thread, the socket, the open file,
// and — because the upload stays InProgress — an upload slot, forever.
// Thirty seconds of total silence is a dead transfer, not a slow one: even
// a slow reader drains some buffer within that. The write deadline is
// deliberately looser than the download side's 5s: bulk data flows this
// way, and a congested but living peer may hold the send buffer full far
// longer than a peer draining our tiny control frames ever would.
stream.set_read_timeout(Some(Duration::from_secs(30)))?;
stream.set_write_timeout(Some(Duration::from_secs(30)))?;
stream.set_nodelay(true).ok();
// PeerInit(F) + the 4-byte token in a single write so they coalesce.