Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wasi-sockets: Implement initial listen backlog #7034

Merged
2 changes: 2 additions & 0 deletions crates/test-programs/wasi-sockets-tests/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ pub fn example_body(net: tcp::Network, sock: tcp::TcpSocket, family: network::Ip

let sub = tcp::subscribe(sock);

tcp::set_listen_backlog_size(sock, 32).unwrap();

tcp::start_listen(sock).unwrap();
wait(sub);
tcp::finish_listen(sock).unwrap();
Expand Down
41 changes: 33 additions & 8 deletions crates/wasi/src/preview2/host/tcp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ impl<T: WasiView> tcp::Host for T {
socket
.tcp_socket()
.as_socketlike_view::<TcpListener>()
.listen(None)?;
.listen(socket.listen_backlog_size)?;

socket.tcp_state = HostTcpState::ListenStarted;

Expand Down Expand Up @@ -302,16 +302,41 @@ impl<T: WasiView> tcp::Host for T {
this: tcp::TcpSocket,
value: u64,
) -> Result<(), network::Error> {
let table = self.table();
let socket = table.get_tcp_socket(this)?;
const MIN_BACKLOG: i32 = 1;
const MAX_BACKLOG: i32 = i32::MAX; // OS'es will most likely limit it down even further.

let table = self.table_mut();
let socket = table.get_tcp_socket_mut(this)?;

// Silently clamp backlog size. This is OK for us to do, because operating systems do this too.
let value = value
.try_into()
.unwrap_or(i32::MAX)
.clamp(MIN_BACKLOG, MAX_BACKLOG);

match socket.tcp_state {
HostTcpState::Listening => {}
_ => return Err(ErrorCode::NotInProgress.into()),
}
HostTcpState::Default | HostTcpState::BindStarted | HostTcpState::Bound => {
// Socket not listening yet. Stash value for first invocation to `listen`.
socket.listen_backlog_size = Some(value);

let value = value.try_into().map_err(|_| ErrorCode::OutOfMemory)?;
Ok(rustix::net::listen(socket.tcp_socket(), value)?)
Ok(())
}
HostTcpState::Listening => {
// Try to update the backlog by calling `listen` again.
// Not all platforms support this. We'll only update our own value if the OS supports changing the backlog size after the fact.

rustix::net::listen(socket.tcp_socket(), value)
.map_err(|_| ErrorCode::AlreadyListening)?;

socket.listen_backlog_size = Some(value);

Ok(())
}
HostTcpState::Connected => Err(ErrorCode::AlreadyConnected.into()),
HostTcpState::Connecting | HostTcpState::ConnectReady | HostTcpState::ListenStarted => {
Err(ErrorCode::ConcurrencyConflict.into())
}
}
}

fn keep_alive(&mut self, this: tcp::TcpSocket) -> Result<bool, network::Error> {
Expand Down
4 changes: 4 additions & 0 deletions crates/wasi/src/preview2/tcp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ pub(crate) struct HostTcpSocket {

/// The current state in the bind/listen/accept/connect progression.
pub(crate) tcp_state: HostTcpState,

/// The desired listen queue size. Set to None to use the system's default.
pub(crate) listen_backlog_size: Option<i32>,
}

pub(crate) struct TcpReadStream {
Expand Down Expand Up @@ -238,6 +241,7 @@ impl HostTcpSocket {
Ok(Self {
inner: Arc::new(stream),
tcp_state: HostTcpState::Default,
listen_backlog_size: None,
})
}

Expand Down