async-rs / async-rs/async-std

Allow `UdpSocket` to perform "fire and forget" sends

Open
#887 1 comment 1 reaction 0 assignees View on GitHub
Dominant language
Rust
Stars
4.1k
Forks
339
PR merge metrics
No merged PRs in 30d

Description

I'm writing a udp server using async-std. One thing I'm trying to ensure is that I avoid queuing any outbound udp packets within my application.

The basic pattern of the application is:

1. single await recv loop, which then calls
2. packet handling, which then sends a udp packet (a reply)

Typical sources of in-application queuing in rust async programs appear to be both explicit queues (if one has specific tasks handle sending for some reason) or the implicit queue formed when one uses `task::spawn()` to send a udp packet.

In the basic pattern above, it's not desirable to await in `2` because this would block the recv of all clients while waiting for the outbound packet to send (ie: we'd end up capping our rx through put). Right now, this appears to leave us with either: `task::spawn()` for each recv'd packet (probably inefficient and forming an implicit queue there) or `task::spawn()` when we want to send a packet in `2` (probably more efficient, but is creating a queue of outbound packets).

When approaching this problem in C, I'd normally have the sends essentially be "fire and forget": iow, make the udp socket fd non-blocking, but never register it for write readiness. Instead, just call `send`(etc) and ignore errors that indicate lack of write readiness (allowing the packet to be dropped, preventing queue formation within the application).

It _seems_ like the most straight forward way to allow this pattern in async-std would be to allow obtaining a reference to the `std::net::UdpSocket` within `async_std::net::UdpSocket`, and then performing sends via calls on the inner structure. There are some downsides here: it would depend on having the socket in a non-blocking mode, and generally be somewhat sensitive to the underlying state of the socket. Alternately, async_std could expose a method on it's `UdpSocket` to allow this `sendto_nonblocking()` functionality.

Edit: for those looking for a workaround: creating a `std::net::UdpSocket`, then cloning it, and constructing the `async_std::net::UdpSocket` from one of the clones, keeping the other around to use for sending udp packets works. This does consume an extra file descriptor per `UdpSocket` that you want to bind to (though that may not be very many).

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.