Skip to content

Commit c7844d7

Browse files
authored
Rollup merge of rust-lang#94407 - BartMassey-upstream:with-backlog, r=joshtriplett
Add `with_backlog` functionality to `TcpListener` and `UnixListener` Adds * `std::net::TcpListener::DEFAULT_BACKLOG` * `std::net::TcpListener::bind_with_backlog` * `std::os::unix::net::UnixListener::DEFAULT_BACKLOG` * `std::os::unix::net::UnixListener::bind_with_backlog` * `std::os::unix::net::UnixListener::bind_addr_with_backlog` Closes rust-lang#94406
2 parents 8622ee2 + b98c009 commit c7844d7

File tree

5 files changed

+178
-16
lines changed

5 files changed

+178
-16
lines changed

library/std/src/net/tcp.rs

+40-2
Original file line numberDiff line numberDiff line change
@@ -707,10 +707,48 @@ impl fmt::Debug for TcpStream {
707707
}
708708

709709
impl TcpListener {
710+
/// Default listen backlog.
711+
const DEFAULT_BACKLOG: usize = 128;
712+
710713
/// Creates a new `TcpListener` which will be bound to the specified
711714
/// address.
712715
///
713-
/// The returned listener is ready for accepting connections.
716+
/// The given backlog specifies the maximum number of outstanding
717+
/// connections that will be buffered in the OS waiting to be accepted by
718+
/// [`TcpListener::accept`]. The backlog argument overrides the default
719+
/// value of 128; that default is reasonable for most use cases.
720+
///
721+
/// This function is otherwise [`TcpListener::bind`]: see that
722+
/// documentation for full details of operation.
723+
///
724+
/// # Examples
725+
///
726+
/// Creates a TCP listener bound to `127.0.0.1:80` with a backlog of 1000:
727+
///
728+
/// ```no_run
729+
/// #![feature(bind_with_backlog)]
730+
/// use std::net::TcpListener;
731+
///
732+
/// let listener = TcpListener::bind_with_backlog("127.0.0.1:80", 1000).unwrap();
733+
/// ```
734+
///
735+
/// # Errors
736+
///
737+
/// The specified backlog may be larger than supported by the underlying
738+
/// system. In this case an [`io::Error`] with
739+
/// [`io::ErrorKind::InvalidData`] will be returned.
740+
#[unstable(feature = "bind_with_backlog", issue = "94406")]
741+
pub fn bind_with_backlog<A: ToSocketAddrs>(addr: A, backlog: usize) -> io::Result<TcpListener> {
742+
super::each_addr(addr, move |a| net_imp::TcpListener::bind_with_backlog(a, backlog))
743+
.map(TcpListener)
744+
}
745+
746+
/// Creates a new `TcpListener` which will be bound to the specified
747+
/// address. The returned listener is ready for accepting
748+
/// connections.
749+
///
750+
/// The listener will have a backlog of 128. See the documentation for
751+
/// [`TcpListener::bind_with_backlog`] for further information.
714752
///
715753
/// Binding with a port number of 0 will request that the OS assigns a port
716754
/// to this listener. The port allocated can be queried via the
@@ -748,7 +786,7 @@ impl TcpListener {
748786
/// ```
749787
#[stable(feature = "rust1", since = "1.0.0")]
750788
pub fn bind<A: ToSocketAddrs>(addr: A) -> io::Result<TcpListener> {
751-
super::each_addr(addr, net_imp::TcpListener::bind).map(TcpListener)
789+
Self::bind_with_backlog(addr, TcpListener::DEFAULT_BACKLOG)
752790
}
753791

754792
/// Returns the local socket address of this listener.

library/std/src/net/tcp/tests.rs

+27-5
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ fn connect_error() {
4848
#[test]
4949
fn listen_localhost() {
5050
let socket_addr = next_test_ip4();
51-
let listener = t!(TcpListener::bind(&socket_addr));
51+
let listener = t!(TcpListener::bind_with_backlog(&socket_addr, 1));
5252

5353
let _t = thread::spawn(move || {
5454
let mut stream = t!(TcpStream::connect(&("localhost", socket_addr.port())));
@@ -64,7 +64,7 @@ fn listen_localhost() {
6464
#[test]
6565
fn connect_loopback() {
6666
each_ip(&mut |addr| {
67-
let acceptor = t!(TcpListener::bind(&addr));
67+
let acceptor = t!(TcpListener::bind_with_backlog(&addr, 1));
6868

6969
let _t = thread::spawn(move || {
7070
let host = match addr {
@@ -85,7 +85,7 @@ fn connect_loopback() {
8585
#[test]
8686
fn smoke_test() {
8787
each_ip(&mut |addr| {
88-
let acceptor = t!(TcpListener::bind(&addr));
88+
let acceptor = t!(TcpListener::bind_with_backlog(&addr, 1));
8989

9090
let (tx, rx) = channel();
9191
let _t = thread::spawn(move || {
@@ -172,11 +172,33 @@ fn multiple_connect_serial() {
172172
})
173173
}
174174

175+
#[test]
176+
fn multiple_connect_serial_with_backlog() {
177+
each_ip(&mut |addr| {
178+
let max = 10;
179+
let acceptor = t!(TcpListener::bind_with_backlog(&addr, max));
180+
181+
let _t = thread::spawn(move || {
182+
for _ in 0..max {
183+
let mut stream = t!(TcpStream::connect(&addr));
184+
t!(stream.write(&[99]));
185+
}
186+
});
187+
188+
for stream in acceptor.incoming().take(max) {
189+
let mut stream = t!(stream);
190+
let mut buf = [0];
191+
t!(stream.read(&mut buf));
192+
assert_eq!(buf[0], 99);
193+
}
194+
})
195+
}
196+
175197
#[test]
176198
fn multiple_connect_interleaved_greedy_schedule() {
177199
const MAX: usize = 10;
178200
each_ip(&mut |addr| {
179-
let acceptor = t!(TcpListener::bind(&addr));
201+
let acceptor = t!(TcpListener::bind_with_backlog(&addr, MAX));
180202

181203
let _t = thread::spawn(move || {
182204
let acceptor = acceptor;
@@ -213,7 +235,7 @@ fn multiple_connect_interleaved_greedy_schedule() {
213235
fn multiple_connect_interleaved_lazy_schedule() {
214236
const MAX: usize = 10;
215237
each_ip(&mut |addr| {
216-
let acceptor = t!(TcpListener::bind(&addr));
238+
let acceptor = t!(TcpListener::bind_with_backlog(&addr, MAX));
217239

218240
let _t = thread::spawn(move || {
219241
for stream in acceptor.incoming().take(MAX) {

library/std/src/os/unix/net/listener.rs

+96-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use crate::sys::cvt;
55
use crate::sys::net::Socket;
66
use crate::sys_common::{AsInner, FromInner, IntoInner};
77
use crate::{fmt, io, mem};
8+
use core::convert::TryInto;
89

910
/// A structure representing a Unix domain socket server.
1011
///
@@ -53,8 +54,14 @@ impl fmt::Debug for UnixListener {
5354
}
5455

5556
impl UnixListener {
57+
/// Default backlog for `bind` and `bind_addr`.
58+
const DEFAULT_BACKLOG: usize = 128;
59+
5660
/// Creates a new `UnixListener` bound to the specified socket.
5761
///
62+
/// The listener will have a backlog of 128. See the documentation for
63+
/// [`UnixListener::bind_with_backlog`] for further information.
64+
///
5865
/// # Examples
5966
///
6067
/// ```no_run
@@ -70,19 +77,61 @@ impl UnixListener {
7077
/// ```
7178
#[stable(feature = "unix_socket", since = "1.10.0")]
7279
pub fn bind<P: AsRef<Path>>(path: P) -> io::Result<UnixListener> {
80+
UnixListener::bind_with_backlog(path, UnixListener::DEFAULT_BACKLOG)
81+
}
82+
83+
/// Creates a new `UnixListener` bound to the specified socket.
84+
///
85+
/// The given backlog specifies the maximum number of outstanding
86+
/// connections that will be buffered in the OS waiting to be accepted
87+
/// by [`UnixListener::accept`]. The backlog argument overrides the
88+
/// default backlog of 128; that default is reasonable for most use
89+
/// cases.
90+
///
91+
/// This function is otherwise [`UnixListener::bind`]: see that
92+
/// documentation for full details of operation.
93+
///
94+
/// # Examples
95+
///
96+
/// ```no_run
97+
/// #![feature(bind_with_backlog)]
98+
/// use std::os::unix::net::UnixListener;
99+
///
100+
/// let listener = match UnixListener::bind_with_backlog("/path/to/the/socket", 1000) {
101+
/// Ok(sock) => sock,
102+
/// Err(e) => {
103+
/// println!("Couldn't connect: {:?}", e);
104+
/// return
105+
/// }
106+
/// };
107+
/// ```
108+
///
109+
/// # Errors
110+
///
111+
/// The specified backlog may be larger than supported by the underlying
112+
/// system. In this case an [`io::Error`] with
113+
/// [`io::ErrorKind::InvalidData`] will be returned.
114+
#[unstable(feature = "bind_with_backlog", issue = "94406")]
115+
pub fn bind_with_backlog<P: AsRef<Path>>(path: P, backlog: usize) -> io::Result<UnixListener> {
73116
unsafe {
117+
let backlog = backlog
118+
.try_into()
119+
.map_err(|e| crate::io::Error::new(crate::io::ErrorKind::InvalidData, e))?;
74120
let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?;
75121
let (addr, len) = sockaddr_un(path.as_ref())?;
76122

77123
cvt(libc::bind(inner.as_inner().as_raw_fd(), &addr as *const _ as *const _, len as _))?;
78-
cvt(libc::listen(inner.as_inner().as_raw_fd(), 128))?;
124+
cvt(libc::listen(inner.as_inner().as_raw_fd(), backlog))?;
79125

80126
Ok(UnixListener(inner))
81127
}
82128
}
83129

84130
/// Creates a new `UnixListener` bound to the specified [`socket address`].
85131
///
132+
/// The listener will have a backlog of 128. See the documentation for
133+
/// [`UnixListener::bind_addr_with_backlog`] for further information.
134+
///
86135
/// [`socket address`]: crate::os::unix::net::SocketAddr
87136
///
88137
/// # Examples
@@ -107,14 +156,59 @@ impl UnixListener {
107156
/// ```
108157
#[unstable(feature = "unix_socket_abstract", issue = "85410")]
109158
pub fn bind_addr(socket_addr: &SocketAddr) -> io::Result<UnixListener> {
159+
UnixListener::bind_addr_with_backlog(socket_addr, UnixListener::DEFAULT_BACKLOG)
160+
}
161+
162+
/// Creates a new `UnixListener` bound to the specified [`socket address`].
163+
///
164+
/// The given backlog specifies the maximum number of outstanding
165+
/// connections that will be buffered in the OS waiting to be accepted
166+
/// by [`UnixListener::accept`]. The backlog argument overrides the
167+
/// default of 128; that default is reasonable for most use cases.
168+
///
169+
/// This function is otherwise [`UnixListener::bind_addr`]: see that
170+
/// documentation for full details of operation.
171+
///
172+
/// [`socket address`]: crate::os::unix::net::SocketAddr
173+
///
174+
/// # Examples
175+
///
176+
/// ```no_run
177+
/// #![feature(unix_socket_abstract)]
178+
/// #![feature(bind_with_backlog)]
179+
/// use std::os::unix::net::{UnixListener};
180+
///
181+
/// fn main() -> std::io::Result<()> {
182+
/// let listener1 = UnixListener::bind("path/to/socket")?;
183+
/// let addr = listener1.local_addr()?;
184+
///
185+
/// let listener2 = match UnixListener::bind_addr_with_backlog(&addr, 1000) {
186+
/// Ok(sock) => sock,
187+
/// Err(err) => {
188+
/// println!("Couldn't bind: {:?}", err);
189+
/// return Err(err);
190+
/// }
191+
/// };
192+
/// Ok(())
193+
/// }
194+
/// ```
195+
//#[unstable(feature = "unix_socket_abstract", issue = "85410")]
196+
#[unstable(feature = "bind_with_backlog", issue = "94406")]
197+
pub fn bind_addr_with_backlog(
198+
socket_addr: &SocketAddr,
199+
backlog: usize,
200+
) -> io::Result<UnixListener> {
110201
unsafe {
202+
let backlog = backlog
203+
.try_into()
204+
.map_err(|e| crate::io::Error::new(crate::io::ErrorKind::InvalidData, e))?;
111205
let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?;
112206
cvt(libc::bind(
113207
inner.as_raw_fd(),
114208
&socket_addr.addr as *const _ as *const _,
115209
socket_addr.len as _,
116210
))?;
117-
cvt(libc::listen(inner.as_raw_fd(), 128))?;
211+
cvt(libc::listen(inner.as_raw_fd(), backlog))?;
118212
Ok(UnixListener(inner))
119213
}
120214
}

library/std/src/os/unix/net/tests.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ fn basic() {
4141
let msg1 = b"hello";
4242
let msg2 = b"world!";
4343

44-
let listener = or_panic!(UnixListener::bind(&socket_path));
44+
let listener = or_panic!(UnixListener::bind_with_backlog(&socket_path, 1));
4545
let thread = thread::spawn(move || {
4646
let mut stream = or_panic!(listener.accept()).0;
4747
let mut buf = [0; 5];
@@ -111,7 +111,7 @@ fn try_clone() {
111111
let msg1 = b"hello";
112112
let msg2 = b"world";
113113

114-
let listener = or_panic!(UnixListener::bind(&socket_path));
114+
let listener = or_panic!(UnixListener::bind_with_backlog(&socket_path, 1));
115115
let thread = thread::spawn(move || {
116116
let mut stream = or_panic!(listener.accept()).0;
117117
or_panic!(stream.write_all(msg1));
@@ -135,7 +135,7 @@ fn iter() {
135135
let dir = tmpdir();
136136
let socket_path = dir.path().join("sock");
137137

138-
let listener = or_panic!(UnixListener::bind(&socket_path));
138+
let listener = or_panic!(UnixListener::bind_with_backlog(&socket_path, 2));
139139
let thread = thread::spawn(move || {
140140
for stream in listener.incoming().take(2) {
141141
let mut stream = or_panic!(stream);
@@ -423,7 +423,7 @@ fn test_abstract_stream_connect() {
423423
let msg2 = b"world";
424424

425425
let socket_addr = or_panic!(SocketAddr::from_abstract_namespace(b"namespace"));
426-
let listener = or_panic!(UnixListener::bind_addr(&socket_addr));
426+
let listener = or_panic!(UnixListener::bind_addr_with_backlog(&socket_addr, 1));
427427

428428
let thread = thread::spawn(move || {
429429
let mut stream = or_panic!(listener.accept()).0;
@@ -451,7 +451,7 @@ fn test_abstract_stream_connect() {
451451
#[test]
452452
fn test_abstract_stream_iter() {
453453
let addr = or_panic!(SocketAddr::from_abstract_namespace(b"hidden"));
454-
let listener = or_panic!(UnixListener::bind_addr(&addr));
454+
let listener = or_panic!(UnixListener::bind_addr_with_backlog(&addr, 2));
455455

456456
let thread = thread::spawn(move || {
457457
for stream in listener.incoming().take(2) {

library/std/src/sys_common/net.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -363,9 +363,17 @@ pub struct TcpListener {
363363
}
364364

365365
impl TcpListener {
366-
pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result<TcpListener> {
366+
pub fn bind_with_backlog(
367+
addr: io::Result<&SocketAddr>,
368+
backlog: usize,
369+
) -> io::Result<TcpListener> {
367370
let addr = addr?;
368371

372+
// Type-convert the backlog
373+
let backlog = backlog
374+
.try_into()
375+
.map_err(|e| crate::io::Error::new(crate::io::ErrorKind::InvalidData, e))?;
376+
369377
init();
370378

371379
let sock = Socket::new(addr, c::SOCK_STREAM)?;
@@ -385,7 +393,7 @@ impl TcpListener {
385393
cvt(unsafe { c::bind(sock.as_raw(), addrp, len as _) })?;
386394

387395
// Start listening
388-
cvt(unsafe { c::listen(sock.as_raw(), 128) })?;
396+
cvt(unsafe { c::listen(sock.as_raw(), backlog) })?;
389397
Ok(TcpListener { inner: sock })
390398
}
391399

0 commit comments

Comments
 (0)