Allow `UdpSocket` to perform "fire and forget" sends
- 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
Assessment
This issue has not been assessed yet.