Skip to content

Commit

Permalink
Merge pull request #3804 from Easyoakland/fix-udp-send
Browse files Browse the repository at this point in the history
Return Truncate error instead of looping forever on udp send.
  • Loading branch information
Dirbaio authored Jan 26, 2025
2 parents ecc1aab + c0d14a1 commit 7ec0b30
Showing 1 changed file with 22 additions and 2 deletions.
24 changes: 22 additions & 2 deletions embassy-net/src/udp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,19 @@ pub enum BindError {
NoRoute,
}

/// Error returned by [`UdpSocket::recv_from`] and [`UdpSocket::send_to`].
/// Error returned by [`UdpSocket::send_to`].
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum SendError {
/// No route to host.
NoRoute,
/// Socket not bound to an outgoing port.
SocketNotBound,
/// There is not enough transmit buffer capacity to ever send this packet.
PacketTooLarge,
}

/// Error returned by [`UdpSocket::recv_from`] and [`UdpSocket::send_to`].
/// Error returned by [`UdpSocket::recv_from`].
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum RecvError {
Expand Down Expand Up @@ -224,6 +226,8 @@ impl<'a> UdpSocket<'a> {
///
/// This method will wait until the datagram has been sent.
///
/// If the socket's send buffer is too small to fit `buf`, this method will return `Poll::Ready(Err(SendError::Truncated))`
///
/// When the remote endpoint is not reachable, this method will return `Err(SendError::NoRoute)`
pub async fn send_to<T>(&self, buf: &[u8], remote_endpoint: T) -> Result<(), SendError>
where
Expand All @@ -240,11 +244,19 @@ impl<'a> UdpSocket<'a> {
/// When the socket's send buffer is full, this method will return `Poll::Pending`
/// and register the current task to be notified when the buffer has space available.
///
/// If the socket's send buffer is too small to fit `buf`, this method will return `Poll::Ready(Err(SendError::Truncated))`
///
/// When the remote endpoint is not reachable, this method will return `Poll::Ready(Err(Error::NoRoute))`.
pub fn poll_send_to<T>(&self, buf: &[u8], remote_endpoint: T, cx: &mut Context<'_>) -> Poll<Result<(), SendError>>
where
T: Into<UdpMetadata>,
{
// Don't need to wake waker in `with_mut` if the buffer will never fit the udp tx_buffer.
let send_capacity_too_small = self.with(|s, _| s.payload_send_capacity() < buf.len());
if send_capacity_too_small {
return Poll::Ready(Err(SendError::PacketTooLarge));
}

self.with_mut(|s, _| match s.send_slice(buf, remote_endpoint) {
// Entire datagram has been sent
Ok(()) => Poll::Ready(Ok(())),
Expand All @@ -268,12 +280,20 @@ impl<'a> UdpSocket<'a> {
/// This method will wait until the buffer can fit the requested size before
/// calling the function to fill its contents.
///
/// If the socket's send buffer is too small to fit `size`, this method will return `Poll::Ready(Err(SendError::Truncated))`
///
/// When the remote endpoint is not reachable, this method will return `Err(SendError::NoRoute)`
pub async fn send_to_with<T, F, R>(&mut self, size: usize, remote_endpoint: T, f: F) -> Result<R, SendError>
where
T: Into<UdpMetadata> + Copy,
F: FnOnce(&mut [u8]) -> R,
{
// Don't need to wake waker in `with_mut` if the buffer will never fit the udp tx_buffer.
let send_capacity_too_small = self.with(|s, _| s.payload_send_capacity() < size);
if send_capacity_too_small {
return Err(SendError::PacketTooLarge);
}

let mut f = Some(f);
poll_fn(move |cx| {
self.with_mut(|s, _| {
Expand Down

0 comments on commit 7ec0b30

Please sign in to comment.