fix: drive inbound peer connections non-blocking so downloads don't stall

A peer actor multiplexes socket reads with its mailbox on one thread: tick()
reads the socket while outgoing messages (e.g. a queued QueueUpload that starts
a download) are delivered through the mailbox between ticks. Inbound
connections accepted by the listener were left in blocking mode, so once such a
peer went idle tick() parked inside read() and never drained the mailbox — a
download queued against that peer never sent its QueueUpload and hung. Outbound
connections already set non-blocking; do the same for inbound P connections.

Covered by a new server-optional e2e test that downloads a file end to end from
an in-process mock peer over real P (control) and F (transfer) connections,
asserting the bytes written match the source. The mock speaks the library's own
public Message wire format, so no dependencies are added.
This commit is contained in:
michel
2026-07-19 13:58:05 +02:00
parent 4717e2787e
commit 06cd638736
2 changed files with 232 additions and 1 deletions
+12
View File
@@ -115,6 +115,18 @@ fn handle_peer_connection(
_peer_ip: &str,
_peer_port: u16,
) {
// The peer actor multiplexes socket reads with its mailbox on a single
// thread: `tick()` reads the socket, but outgoing messages (e.g. a queued
// QueueUpload for a download) are delivered through the mailbox between
// ticks. A blocking socket would park `tick()` inside `read` whenever the
// peer is idle, starving the mailbox and stalling downloads. Match the
// outbound path and drive this connection non-blocking.
if let Err(e) = stream.set_nonblocking(true) {
error!("[listener] failed to set peer stream non-blocking: {}", e);
return;
}
stream.set_nodelay(true).ok();
let client_context = match context.client_context.read_safe() {
Ok(c) => c,
Err(e) => {