#![allow(clippy::type_complexity)]
use std::io::IoSliceMut;
use std::task::{Context, Poll};
use std::{fmt::Debug, future::Future, io, net::SocketAddr, pin::Pin, sync::Arc, time::Duration};
pub mod primitives;
pub use primitives::{
BATCH_SIZE, BroadcastReceiver, BroadcastRecvError, BroadcastSendError, BroadcastSender,
EcnCodepoint, Mutex, Notify, Receiver, RecvMeta, SendError, Sender, Transmit, TryRecvError,
TrySendError, UdpSockRef, UdpSocketState, broadcast_channel, channel,
};
pub(crate) const MAX_REACTOR_POOL_SIZE: usize = 1024;
pub trait JoinHandle: Send + Sync {
fn detach(&self);
fn abort(&self);
fn is_finished(&self) -> bool;
}
pub trait Runtime: Send + Sync + Debug + 'static {
#[track_caller]
fn spawn(&self, future: Pin<Box<dyn Future<Output = ()> + Send>>) -> Box<dyn JoinHandle>;
fn spawn_reactor(
&self,
_reactor_pool_size: usize,
future: Pin<Box<dyn Future<Output = ()> + Send>>,
) -> Box<dyn JoinHandle> {
self.spawn(future)
}
fn wrap_udp_socket(&self, socket: std::net::UdpSocket) -> io::Result<Arc<dyn AsyncUdpSocket>>;
fn wrap_tcp_listener(
&self,
listener: std::net::TcpListener,
) -> io::Result<Arc<dyn AsyncTcpListener>>;
fn connect_tcp<'a>(
&'a self,
remote_addr: SocketAddr,
) -> Pin<Box<dyn Future<Output = io::Result<Arc<dyn AsyncTcpStream>>> + Send + 'a>>;
fn resolve_host<'a>(
&'a self,
host: &'a str,
) -> Pin<Box<dyn Future<Output = io::Result<Vec<SocketAddr>>> + Send + 'a>>;
fn sleep(&self, duration: Duration) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>>;
fn interval(&self, period: Duration) -> Box<dyn AsyncInterval>;
fn block_on(&self, future: Pin<Box<dyn Future<Output = ()> + '_>>);
fn yield_now(&self) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>> {
let mut yielded = false;
Box::pin(futures::future::poll_fn(move |cx| {
if yielded {
return std::task::Poll::Ready(());
}
yielded = true;
cx.waker().wake_by_ref();
std::task::Poll::Pending
}))
}
fn name(&self) -> &'static str {
"custom"
}
}
pub trait AsyncInterval: Send + Sync {
fn tick(&mut self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>>;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Elapsed;
impl std::fmt::Display for Elapsed {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "deadline has elapsed")
}
}
impl std::error::Error for Elapsed {}
pub async fn timeout<T>(
runtime: &dyn Runtime,
duration: Duration,
future: impl Future<Output = T>,
) -> Result<T, Elapsed> {
use futures::future::{Either, select};
match select(Box::pin(future), runtime.sleep(duration)).await {
Either::Left((value, _)) => Ok(value),
Either::Right(_) => Err(Elapsed),
}
}
pub trait AsyncUdpSocket: Send + Sync + Debug + 'static {
fn local_addr(&self) -> io::Result<SocketAddr>;
fn poll_send(&self, cx: &mut Context<'_>, transmit: &Transmit<'_>) -> Poll<io::Result<usize>>;
fn poll_recv(
&self,
cx: &mut Context<'_>,
bufs: &mut [IoSliceMut<'_>],
meta: &mut [RecvMeta],
) -> Poll<io::Result<usize>>;
fn max_gso_segments(&self) -> usize {
1
}
fn max_gro_segments(&self) -> usize {
1
}
fn send_to<'a>(
&'a self,
buf: &'a [u8],
target: SocketAddr,
) -> Pin<Box<dyn Future<Output = io::Result<usize>> + Send + 'a>> {
let transmit = Transmit {
destination: target,
ecn: None,
contents: buf,
segment_size: None,
src_ip: None,
};
Box::pin(futures::future::poll_fn(move |cx| {
self.poll_send(cx, &transmit)
}))
}
fn recv_from<'a>(
&'a self,
buf: &'a mut [u8],
) -> Pin<Box<dyn Future<Output = io::Result<(usize, SocketAddr)>> + Send + 'a>> {
Box::pin(async move {
let mut meta = [RecvMeta::default(); 1];
futures::future::poll_fn(|cx| {
let mut bufs = [IoSliceMut::new(buf)];
self.poll_recv(cx, &mut bufs, &mut meta)
})
.await?;
Ok((meta[0].len, meta[0].addr))
})
}
}
pub(crate) fn poll_once<T>(f: impl FnOnce(&mut Context<'_>) -> Poll<T>) -> Option<T> {
let waker = std::task::Waker::noop();
let mut cx = Context::from_waker(waker);
match f(&mut cx) {
Poll::Ready(v) => Some(v),
Poll::Pending => None,
}
}
pub trait AsyncTcpListener: Send + Sync + Debug + 'static {
fn accept<'a>(
&'a self,
) -> Pin<Box<dyn Future<Output = io::Result<(Arc<dyn AsyncTcpStream>, SocketAddr)>> + Send + 'a>>;
fn local_addr(&self) -> io::Result<SocketAddr>;
}
pub trait AsyncTcpStream: Send + Sync + Debug + 'static {
fn read<'a, 'b>(
&'a self,
buf: &'b mut [u8],
) -> Pin<Box<dyn Future<Output = io::Result<usize>> + Send + 'b>>
where
'a: 'b;
fn write_all<'a, 'b>(
&'a self,
buf: &'b [u8],
) -> Pin<Box<dyn Future<Output = io::Result<()>> + Send + 'b>>
where
'a: 'b;
fn local_addr(&self) -> io::Result<SocketAddr>;
fn peer_addr(&self) -> io::Result<SocketAddr>;
}
pub fn default_runtime() -> Option<Arc<dyn Runtime>> {
#[cfg(all(
feature = "runtime-tokio",
not(feature = "runtime-smol"),
not(feature = "runtime-mock")
))]
{
Some(Arc::new(TokioRuntime))
}
#[cfg(all(
not(feature = "runtime-tokio"),
feature = "runtime-smol",
not(feature = "runtime-mock")
))]
{
Some(Arc::new(SmolRuntime))
}
#[cfg(all(
not(feature = "runtime-tokio"),
not(feature = "runtime-smol"),
feature = "runtime-mock"
))]
{
Some(Arc::new(MockRuntime::new()))
}
#[cfg(not(any(
feature = "runtime-tokio",
feature = "runtime-smol",
feature = "runtime-mock"
)))]
{
None
}
}
#[cfg(feature = "runtime-tokio")]
mod tokio;
#[cfg(feature = "runtime-tokio")]
pub use tokio::TokioRuntime;
#[cfg(feature = "runtime-smol")]
mod smol;
#[cfg(feature = "runtime-smol")]
pub use smol::SmolRuntime;
#[cfg(feature = "runtime-mock")]
pub mod mock;
#[cfg(feature = "runtime-mock")]
pub use mock::MockRuntime;
#[cfg(test)]
mod default_impl_tests {
use super::*;
use std::sync::Mutex;
#[derive(Debug, Default)]
struct FakeUdp {
sent: Mutex<Vec<Vec<u8>>>,
to_recv: Mutex<Vec<u8>>,
never_ready: bool,
}
impl AsyncUdpSocket for FakeUdp {
fn local_addr(&self) -> io::Result<SocketAddr> {
Ok("127.0.0.1:0".parse::<SocketAddr>().unwrap())
}
fn poll_send(
&self,
_cx: &mut Context<'_>,
transmit: &Transmit<'_>,
) -> Poll<io::Result<usize>> {
if self.never_ready {
return Poll::Pending;
}
self.sent.lock().unwrap().push(transmit.contents.to_vec());
Poll::Ready(Ok(transmit.contents.len()))
}
fn poll_recv(
&self,
_cx: &mut Context<'_>,
bufs: &mut [IoSliceMut<'_>],
meta: &mut [RecvMeta],
) -> Poll<io::Result<usize>> {
if self.never_ready {
return Poll::Pending;
}
let data = self.to_recv.lock().unwrap();
let n = data.len().min(bufs[0].len());
bufs[0][..n].copy_from_slice(&data[..n]);
meta[0] = RecvMeta::default();
meta[0].len = n;
meta[0].stride = n.max(1);
meta[0].addr = "127.0.0.1:9".parse::<SocketAddr>().unwrap();
Poll::Ready(Ok(1))
}
}
fn addr() -> SocketAddr {
"127.0.0.1:5".parse::<SocketAddr>().unwrap()
}
#[test]
fn default_caps_are_one() {
let s = FakeUdp::default();
assert_eq!(s.max_gso_segments(), 1);
assert_eq!(s.max_gro_segments(), 1);
}
#[test]
fn default_send_to_forwards_one_datagram() {
let s = FakeUdp::default();
let n = futures::executor::block_on(s.send_to(b"abcd", addr())).unwrap();
assert_eq!(n, 4);
assert_eq!(s.sent.lock().unwrap().as_slice(), &[b"abcd".to_vec()]);
}
#[test]
fn default_recv_from_derives_from_poll_recv() {
let s = FakeUdp::default();
*s.to_recv.lock().unwrap() = vec![7, 7, 7];
let mut buf = [0u8; 16];
let (n, from) = futures::executor::block_on(s.recv_from(&mut buf)).unwrap();
assert_eq!(n, 3);
assert_eq!(from, "127.0.0.1:9".parse::<SocketAddr>().unwrap());
}
#[test]
fn poll_once_probes_without_blocking_or_allocating() {
let ready = FakeUdp::default();
let mut buf = [0u8; 8];
let mut meta = [RecvMeta::default(); 1];
assert!(
poll_once(|cx| {
let mut bufs = [IoSliceMut::new(&mut buf)];
ready.poll_recv(cx, &mut bufs, &mut meta)
})
.is_some(),
"ready socket yields a value"
);
let pending = FakeUdp {
never_ready: true,
..Default::default()
};
assert!(
poll_once(|cx| {
let mut bufs = [IoSliceMut::new(&mut buf)];
pending.poll_recv(cx, &mut bufs, &mut meta)
})
.is_none(),
"pending socket yields None instead of parking"
);
}
}