diff --git a/iroh/src/magicsock/relay_actor.rs b/iroh/src/magicsock/relay_actor.rs index eb94754b43..de0adebf6f 100644 --- a/iroh/src/magicsock/relay_actor.rs +++ b/iroh/src/magicsock/relay_actor.rs @@ -13,8 +13,6 @@ use std::{ use anyhow::Context; use backoff::backoff::Backoff; use bytes::{Bytes, BytesMut}; -use futures_lite::future; -use futures_util::future::Either; use iroh_metrics::{inc, inc_by}; use iroh_relay::{self as relay, client::ClientError, ReceivedMessage, RelayUrl, MAX_PACKET_SIZE}; use tokio::{ @@ -28,6 +26,7 @@ use tracing::{debug, error, info, info_span, trace, warn, Instrument}; use crate::{ key::{NodeId, PUBLIC_KEY_LENGTH}, magicsock::{MagicSock, Metrics as MagicsockMetrics, RelayContents, RelayDatagramsQueue}, + util::MaybeFuture, }; /// How long a non-home relay connection needs to be idle (last written to) before we close it. @@ -113,11 +112,10 @@ impl ConnectedRelayActor { let relay_client = self.relay_client.clone(); relay_client.connect().await.context("initial connection")?; - // When this future is Either::Right this is a future which is currently sending + // When this future has an inner, it is a future which is currently sending // something to the relay server. Nothing else can be sent to the relay server at - // the same time. If this future is Either::Left nothing is being sent to the - // relay server. - let mut relay_send_fut = Either::Left(future::pending()); + // the same time. + let mut relay_send_fut = MaybeFuture::none(); loop { // If a read error occurred on the connection it might have been lost. But we @@ -137,13 +135,15 @@ impl ConnectedRelayActor { break; } } - // Only poll relay_send_fut if it is sending to the future. - _ = &mut relay_send_fut, if matches!(relay_send_fut, Either::Right(_)) => { - relay_send_fut = Either::Left(future::pending()); + // Only poll relay_send_fut if it is sending to the relay. + _ = &mut relay_send_fut, if relay_send_fut.is_some() => { + relay_send_fut = MaybeFuture::none(); } // Only poll for new datagrams if relay_send_fut is not busy. - Some(msg) = self.relay_datagrams_send.recv(), if matches!(relay_send_fut, Either::Left(_)) => { - relay_send_fut = Either::Right(Box::pin(relay_client.send(msg.0, msg.1))); + Some(msg) = self.relay_datagrams_send.recv(), if relay_send_fut.is_none() => { + relay_send_fut = MaybeFuture::with_future( + Box::pin(relay_client.send(msg.0, msg.1)) + ); self.last_write = Instant::now(); } diff --git a/iroh/src/util.rs b/iroh/src/util.rs index 27c114c1b0..ee777da8fb 100644 --- a/iroh/src/util.rs +++ b/iroh/src/util.rs @@ -6,17 +6,44 @@ use std::{ task::{Context, Poll}, }; -/// Resolves to pending if the inner is `None`. +/// A future which may not be present. +/// +/// This is a single type which may optionally contain a future. If there is no inner +/// future polling will always return [`Poll::Pending`]. +/// +/// The [`Default`] impl will create a [`MaybeFuture`] without an inner. #[derive(Debug)] pub(crate) struct MaybeFuture { /// Future to be polled. pub inner: Option, } +impl MaybeFuture { + /// Creates a [`MaybeFuture`] without an inner future. + pub(crate) fn none() -> Self { + Self { inner: None } + } + + /// Creates a [`MaybeFuture`] with an inner future. + pub(crate) fn with_future(fut: T) -> Self { + Self { inner: Some(fut) } + } + + /// Returns `true` if the inner is empty. + pub(crate) fn is_none(&self) -> bool { + self.inner.is_none() + } + + /// Returns `true` if the inner contains a future. + pub(crate) fn is_some(&self) -> bool { + self.inner.is_some() + } +} + // NOTE: explicit implementation to bypass derive unnecessary bounds impl Default for MaybeFuture { fn default() -> Self { - MaybeFuture { inner: None } + Self::none() } }