Skip to content

Commit

Permalink
fix(udp): retry on ErrorKind::Interrupted
Browse files Browse the repository at this point in the history
On Linux and Android, `quinn-udp` will retry a send on `ErrorKind::Interrupted`:

https://github.com/quinn-rs/quinn/blob/e318cc4a80436fd9fa19c02886d682c49efca185/quinn-udp/src/unix.rs#L305-L312

Do the same on all other unix platforms.
  • Loading branch information
mxinden committed Nov 29, 2024
1 parent e318cc4 commit 4b02283
Showing 1 changed file with 41 additions and 36 deletions.
77 changes: 41 additions & 36 deletions quinn-udp/src/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -383,31 +383,33 @@ fn send(state: &UdpSocketState, io: SockRef<'_>, transmit: &Transmit<'_>) -> io:
hdrs[i].msg_datalen = chunk.len();
cnt += 1;
}
let n = unsafe { sendmsg_x(io.as_raw_fd(), hdrs.as_ptr(), cnt as u32, 0) };
if n >= 0 {
return Ok(());
}
let e = io::Error::last_os_error();
match e.kind() {
io::ErrorKind::Interrupted => {
// Retry the transmission
}
io::ErrorKind::WouldBlock => return Err(e),
_ => {
// Other errors are ignored, since they will usually be handled
// by higher level retransmits and timeouts.
// - PermissionDenied errors have been observed due to iptable rules.
// Those are not fatal errors, since the
// configuration can be dynamically changed.
// - Destination unreachable errors have been observed for other
// - EMSGSIZE is expected for MTU probes. Future work might be able to avoid
// these by automatically clamping the MTUD upper bound to the interface MTU.
if e.raw_os_error() != Some(libc::EMSGSIZE) {
log_sendmsg_error(&state.last_send_error, e, transmit);
loop {
let n = unsafe { sendmsg_x(io.as_raw_fd(), hdrs.as_ptr(), cnt as u32, 0) };
if n < 0 {
let e = io::Error::last_os_error();
match e.kind() {
io::ErrorKind::Interrupted => {
// Retry the transmission
continue;
}
io::ErrorKind::WouldBlock => return Err(e),
_ => {
// Other errors are ignored, since they will usually be handled
// by higher level retransmits and timeouts.
// - PermissionDenied errors have been observed due to iptable rules.
// Those are not fatal errors, since the
// configuration can be dynamically changed.
// - Destination unreachable errors have been observed for other
// - EMSGSIZE is expected for MTU probes. Future work might be able to avoid
// these by automatically clamping the MTUD upper bound to the interface MTU.
if e.raw_os_error() != Some(libc::EMSGSIZE) {
log_sendmsg_error(&state.last_send_error, e, transmit);
}
}
}
}
return Ok(());
}
Ok(())
}

#[cfg(any(target_os = "openbsd", target_os = "netbsd", apple_slow))]
Expand All @@ -425,24 +427,27 @@ fn send(state: &UdpSocketState, io: SockRef<'_>, transmit: &Transmit<'_>) -> io:
cfg!(apple) || cfg!(target_os = "openbsd") || cfg!(target_os = "netbsd"),
state.sendmsg_einval(),
);
let n = unsafe { libc::sendmsg(io.as_raw_fd(), &hdr, 0) };
if n == -1 {
let e = io::Error::last_os_error();
match e.kind() {
io::ErrorKind::Interrupted => {
// Retry the transmission
}
io::ErrorKind::WouldBlock => return Err(e),
_ => {
// - EMSGSIZE is expected for MTU probes. Future work might be able to avoid
// these by automatically clamping the MTUD upper bound to the interface MTU.
if e.raw_os_error() != Some(libc::EMSGSIZE) {
return Err(e);
loop {
let n = unsafe { libc::sendmsg(io.as_raw_fd(), &hdr, 0) };
if n == -1 {
let e = io::Error::last_os_error();
match e.kind() {
io::ErrorKind::Interrupted => {
// Retry the transmission
continue;
}
io::ErrorKind::WouldBlock => return Err(e),
_ => {
// - EMSGSIZE is expected for MTU probes. Future work might be able to avoid
// these by automatically clamping the MTUD upper bound to the interface MTU.
if e.raw_os_error() != Some(libc::EMSGSIZE) {
return Err(e);
}
}
}
}
return Ok(());
}
Ok(())
}

#[cfg(not(any(apple, target_os = "openbsd", target_os = "netbsd", solarish)))]
Expand Down

0 comments on commit 4b02283

Please sign in to comment.