From 7bc34930c2b814c911dd949a1d3c3b4e3d07d50e Mon Sep 17 00:00:00 2001 From: Alexander Shirokov Date: Wed, 27 Aug 2025 14:49:43 +0200 Subject: [PATCH 1/3] listen:detect backlog parameter as in std. library Define the `listen` backlog parameters as in the standard library. On one hand, this helps avoid hardcoded or unsynchronized values. On the other hand, it allows better control of default values depending on the target. This is an initial version which deals with issue #1872 --- src/net/tcp/listener.rs | 3 +- src/sys/mod.rs | 57 ++++++++++++++++++++++++++++++++++++ src/sys/shell/tcp.rs | 2 +- src/sys/unix/tcp.rs | 4 +-- src/sys/unix/uds/listener.rs | 3 +- src/sys/windows/tcp.rs | 3 +- 6 files changed, 64 insertions(+), 8 deletions(-) diff --git a/src/net/tcp/listener.rs b/src/net/tcp/listener.rs index 6a2bab154..ccd85e129 100644 --- a/src/net/tcp/listener.rs +++ b/src/net/tcp/listener.rs @@ -13,6 +13,7 @@ use std::{fmt, io}; use crate::io_source::IoSource; use crate::net::TcpStream; +use crate::sys::get_listen_backlog_size; #[cfg(any(unix, target_os = "hermit"))] use crate::sys::tcp::set_reuseaddr; #[cfg(not(target_os = "wasi"))] @@ -78,7 +79,7 @@ impl TcpListener { set_reuseaddr(&listener.inner, true)?; bind(&listener.inner, addr)?; - listen(&listener.inner, 1024)?; + listen(&listener.inner, get_listen_backlog_size())?; Ok(listener) } diff --git a/src/sys/mod.rs b/src/sys/mod.rs index 8bfbdd9b8..60445a216 100644 --- a/src/sys/mod.rs +++ b/src/sys/mod.rs @@ -80,3 +80,60 @@ cfg_not_os_poll! { pub use self::unix::SourceFd; } } + +/// Define the `listen` backlog parameters as in the standard library. This +/// helps avoid hardcoded unsynchronized values and allows better control of +/// default values depending on the target. +/// +/// Selecting a “valid” default value can be tricky due to: +/// +/// - It often serves only as a hint and may be rounded, trimmed, or ignored by +/// the OS. +/// +/// - It is sometimes provided as a "magic" value, for example, -1. This +/// value is undocumented and not standard, but it is often used to represent +/// the largest possible backlog size. This happens due to signed/unsigned +/// conversion and rounding to the upper bound performed by the OS. +/// +/// - Default values vary depending on the OS and its version. Common defaults +/// include: -1, 128, 1024, and 4096. +/// +#[allow(dead_code)] +pub(crate) const fn get_listen_backlog_size() -> i32 { + // Here is the original code from the standard library + // https://github.com/rust-lang/rust/blob/4f808ba6bf9f1c8dde30d009e73386d984491587/library/std/src/os/unix/net/listener.rs#L72 + #[cfg(any( + target_os = "windows", + target_os = "redox", + target_os = "espidf", + target_os = "horizon" + ))] + const BACKLOG_SIZE: i32 = 128; + + #[allow(dead_code)] + #[cfg(any( + // Silently capped to `/proc/sys/net/core/somaxconn`. + target_os = "linux", + // Silently capped to `kern.ipc.soacceptqueue`. + target_os = "freebsd", + // Silently capped to `kern.somaxconn sysctl`. + target_os = "openbsd", + // Silently capped to the default 128. + target_vendor = "apple", + ))] + const BACKLOG_SIZE: i32 = -1; + + #[allow(dead_code)] + #[cfg(not(any( + target_os = "windows", + target_os = "redox", + target_os = "espidf", + target_os = "horizon", + target_os = "linux", + target_os = "freebsd", + target_os = "openbsd", + target_vendor = "apple", + )))] + const BACKLOG_SIZE: i32 = libc::SOMAXCONN; + BACKLOG_SIZE +} diff --git a/src/sys/shell/tcp.rs b/src/sys/shell/tcp.rs index b61a4ffc9..4f7326ab8 100644 --- a/src/sys/shell/tcp.rs +++ b/src/sys/shell/tcp.rs @@ -17,7 +17,7 @@ pub(crate) fn connect(_: &net::TcpStream, _: SocketAddr) -> io::Result<()> { } #[cfg(not(target_os = "wasi"))] -pub(crate) fn listen(_: &net::TcpListener, _: u32) -> io::Result<()> { +pub(crate) fn listen(_: &net::TcpListener, _: i32) -> io::Result<()> { os_required!(); } diff --git a/src/sys/unix/tcp.rs b/src/sys/unix/tcp.rs index 4dbdb73c5..a3560c225 100644 --- a/src/sys/unix/tcp.rs +++ b/src/sys/unix/tcp.rs @@ -1,4 +1,3 @@ -use std::convert::TryInto; use std::io; use std::mem::{size_of, MaybeUninit}; use std::net::{self, SocketAddr}; @@ -38,8 +37,7 @@ pub(crate) fn connect(socket: &net::TcpStream, addr: SocketAddr) -> io::Result<( } } -pub(crate) fn listen(socket: &net::TcpListener, backlog: u32) -> io::Result<()> { - let backlog = backlog.try_into().unwrap_or(i32::MAX); +pub(crate) fn listen(socket: &net::TcpListener, backlog: i32) -> io::Result<()> { syscall!(listen(socket.as_raw_fd(), backlog))?; Ok(()) } diff --git a/src/sys/unix/uds/listener.rs b/src/sys/unix/uds/listener.rs index 5b4219a29..b38323b8c 100644 --- a/src/sys/unix/uds/listener.rs +++ b/src/sys/unix/uds/listener.rs @@ -6,6 +6,7 @@ use std::path::Path; use std::{io, mem}; use crate::net::UnixStream; +use crate::sys::get_listen_backlog_size; use crate::sys::unix::net::new_socket; use crate::sys::unix::uds::{path_offset, unix_addr}; @@ -16,7 +17,7 @@ pub(crate) fn bind_addr(address: &SocketAddr) -> io::Result { let (unix_address, addrlen) = unix_addr(address); let sockaddr = &unix_address as *const libc::sockaddr_un as *const libc::sockaddr; syscall!(bind(fd, sockaddr, addrlen))?; - syscall!(listen(fd, 1024))?; + syscall!(listen(fd, get_listen_backlog_size()))?; Ok(socket) } diff --git a/src/sys/windows/tcp.rs b/src/sys/windows/tcp.rs index 4f77d5d6e..a02cf7199 100644 --- a/src/sys/windows/tcp.rs +++ b/src/sys/windows/tcp.rs @@ -46,11 +46,10 @@ pub(crate) fn connect(socket: &net::TcpStream, addr: SocketAddr) -> io::Result<( } } -pub(crate) fn listen(socket: &net::TcpListener, backlog: u32) -> io::Result<()> { +pub(crate) fn listen(socket: &net::TcpListener, backlog: i32) -> io::Result<()> { use std::convert::TryInto; use WinSock::listen; - let backlog = backlog.try_into().unwrap_or(i32::MAX); syscall!( listen(socket.as_raw_socket() as _, backlog), PartialEq::eq, From b0a951bf810fdd30ef8226f488ebfea761923290 Mon Sep 17 00:00:00 2001 From: Alexander Shirokov Date: Wed, 27 Aug 2025 18:12:07 +0200 Subject: [PATCH 2/3] listen: backlog improvements after review - backlog size is now a constant, not a function - removed unused import (windows) - improved formatting --- src/net/tcp/listener.rs | 4 +-- src/sys/mod.rs | 70 ++++++++++++++++++------------------ src/sys/unix/uds/listener.rs | 4 +-- src/sys/windows/tcp.rs | 1 - 4 files changed, 38 insertions(+), 41 deletions(-) diff --git a/src/net/tcp/listener.rs b/src/net/tcp/listener.rs index ccd85e129..fb213bc34 100644 --- a/src/net/tcp/listener.rs +++ b/src/net/tcp/listener.rs @@ -13,11 +13,11 @@ use std::{fmt, io}; use crate::io_source::IoSource; use crate::net::TcpStream; -use crate::sys::get_listen_backlog_size; #[cfg(any(unix, target_os = "hermit"))] use crate::sys::tcp::set_reuseaddr; #[cfg(not(target_os = "wasi"))] use crate::sys::tcp::{bind, listen, new_for_addr}; +use crate::sys::LISTEN_BACKLOG_SIZE; use crate::{event, sys, Interest, Registry, Token}; /// A structure representing a socket server @@ -79,7 +79,7 @@ impl TcpListener { set_reuseaddr(&listener.inner, true)?; bind(&listener.inner, addr)?; - listen(&listener.inner, get_listen_backlog_size())?; + listen(&listener.inner, LISTEN_BACKLOG_SIZE)?; Ok(listener) } diff --git a/src/sys/mod.rs b/src/sys/mod.rs index 60445a216..208cc645a 100644 --- a/src/sys/mod.rs +++ b/src/sys/mod.rs @@ -98,42 +98,40 @@ cfg_not_os_poll! { /// - Default values vary depending on the OS and its version. Common defaults /// include: -1, 128, 1024, and 4096. /// +// Here is the original code from the standard library +// https://github.com/rust-lang/rust/blob/4f808ba6bf9f1c8dde30d009e73386d984491587/library/std/src/os/unix/net/listener.rs#L72 +// #[allow(dead_code)] -pub(crate) const fn get_listen_backlog_size() -> i32 { - // Here is the original code from the standard library - // https://github.com/rust-lang/rust/blob/4f808ba6bf9f1c8dde30d009e73386d984491587/library/std/src/os/unix/net/listener.rs#L72 - #[cfg(any( - target_os = "windows", - target_os = "redox", - target_os = "espidf", - target_os = "horizon" - ))] - const BACKLOG_SIZE: i32 = 128; +#[cfg(any( + target_os = "windows", + target_os = "redox", + target_os = "espidf", + target_os = "horizon" +))] +pub(crate) const LISTEN_BACKLOG_SIZE: i32 = 128; - #[allow(dead_code)] - #[cfg(any( - // Silently capped to `/proc/sys/net/core/somaxconn`. - target_os = "linux", - // Silently capped to `kern.ipc.soacceptqueue`. - target_os = "freebsd", - // Silently capped to `kern.somaxconn sysctl`. - target_os = "openbsd", - // Silently capped to the default 128. - target_vendor = "apple", - ))] - const BACKLOG_SIZE: i32 = -1; +#[allow(dead_code)] +#[cfg(any( + // Silently capped to `/proc/sys/net/core/somaxconn`. + target_os = "linux", + // Silently capped to `kern.ipc.soacceptqueue`. + target_os = "freebsd", + // Silently capped to `kern.somaxconn sysctl`. + target_os = "openbsd", + // Silently capped to the default 128. + target_vendor = "apple", +))] +pub(crate) const LISTEN_BACKLOG_SIZE: i32 = -1; - #[allow(dead_code)] - #[cfg(not(any( - target_os = "windows", - target_os = "redox", - target_os = "espidf", - target_os = "horizon", - target_os = "linux", - target_os = "freebsd", - target_os = "openbsd", - target_vendor = "apple", - )))] - const BACKLOG_SIZE: i32 = libc::SOMAXCONN; - BACKLOG_SIZE -} +#[allow(dead_code)] +#[cfg(not(any( + target_os = "windows", + target_os = "redox", + target_os = "espidf", + target_os = "horizon", + target_os = "linux", + target_os = "freebsd", + target_os = "openbsd", + target_vendor = "apple", +)))] +pub(crate) const LISTEN_BACKLOG_SIZE: i32 = libc::SOMAXCONN; diff --git a/src/sys/unix/uds/listener.rs b/src/sys/unix/uds/listener.rs index b38323b8c..d3c3eee3b 100644 --- a/src/sys/unix/uds/listener.rs +++ b/src/sys/unix/uds/listener.rs @@ -6,9 +6,9 @@ use std::path::Path; use std::{io, mem}; use crate::net::UnixStream; -use crate::sys::get_listen_backlog_size; use crate::sys::unix::net::new_socket; use crate::sys::unix::uds::{path_offset, unix_addr}; +use crate::sys::LISTEN_BACKLOG_SIZE; pub(crate) fn bind_addr(address: &SocketAddr) -> io::Result { let fd = new_socket(libc::AF_UNIX, libc::SOCK_STREAM)?; @@ -17,7 +17,7 @@ pub(crate) fn bind_addr(address: &SocketAddr) -> io::Result { let (unix_address, addrlen) = unix_addr(address); let sockaddr = &unix_address as *const libc::sockaddr_un as *const libc::sockaddr; syscall!(bind(fd, sockaddr, addrlen))?; - syscall!(listen(fd, get_listen_backlog_size()))?; + syscall!(listen(fd, LISTEN_BACKLOG_SIZE))?; Ok(socket) } diff --git a/src/sys/windows/tcp.rs b/src/sys/windows/tcp.rs index a02cf7199..a59389676 100644 --- a/src/sys/windows/tcp.rs +++ b/src/sys/windows/tcp.rs @@ -47,7 +47,6 @@ pub(crate) fn connect(socket: &net::TcpStream, addr: SocketAddr) -> io::Result<( } pub(crate) fn listen(socket: &net::TcpListener, backlog: i32) -> io::Result<()> { - use std::convert::TryInto; use WinSock::listen; syscall!( From 11380e868dd6f79bdc97051479e595168c48c37f Mon Sep 17 00:00:00 2001 From: Alexander Shirokov Date: Thu, 28 Aug 2025 14:17:29 +0200 Subject: [PATCH 3/3] listen:set backlog for 'hermit' & 'wasi' targets --- src/net/tcp/listener.rs | 6 ++++-- src/sys/mod.rs | 10 ++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/net/tcp/listener.rs b/src/net/tcp/listener.rs index fb213bc34..8e8f6eeeb 100644 --- a/src/net/tcp/listener.rs +++ b/src/net/tcp/listener.rs @@ -16,8 +16,10 @@ use crate::net::TcpStream; #[cfg(any(unix, target_os = "hermit"))] use crate::sys::tcp::set_reuseaddr; #[cfg(not(target_os = "wasi"))] -use crate::sys::tcp::{bind, listen, new_for_addr}; -use crate::sys::LISTEN_BACKLOG_SIZE; +use crate::sys::{ + tcp::{bind, listen, new_for_addr}, + LISTEN_BACKLOG_SIZE, +}; use crate::{event, sys, Interest, Registry, Token}; /// A structure representing a socket server diff --git a/src/sys/mod.rs b/src/sys/mod.rs index 208cc645a..6ec1e9bf7 100644 --- a/src/sys/mod.rs +++ b/src/sys/mod.rs @@ -110,6 +110,14 @@ cfg_not_os_poll! { ))] pub(crate) const LISTEN_BACKLOG_SIZE: i32 = 128; +/// This is a special case for some target(s) supported by `mio`. This value +/// is needed because `libc::SOMAXCON` (used as a fallback for unknown targets) +/// is not implemented for them. Feel free to update this if the `libc` crate +/// changes. +#[allow(dead_code)] +#[cfg(target_os = "hermit")] +pub(crate) const LISTEN_BACKLOG_SIZE: i32 = 1024; + #[allow(dead_code)] #[cfg(any( // Silently capped to `/proc/sys/net/core/somaxconn`. @@ -132,6 +140,8 @@ pub(crate) const LISTEN_BACKLOG_SIZE: i32 = -1; target_os = "linux", target_os = "freebsd", target_os = "openbsd", + target_os = "wasi", + target_os = "hermit", target_vendor = "apple", )))] pub(crate) const LISTEN_BACKLOG_SIZE: i32 = libc::SOMAXCONN;