#[cfg(any(feature = "runtime-tokio", feature = "runtime-smol"))]
use std::sync::Arc;
use std::{
fmt::{self, Debug},
future::Future,
io::{self, IoSliceMut},
net::SocketAddr,
num::NonZeroUsize,
pin::Pin,
task::{Context, Poll},
};
use udp::{RecvMeta, Transmit};
use crate::Instant;
pub trait Runtime: Send + Sync + Debug + 'static {
fn new_timer(&self, i: Instant) -> Pin<Box<dyn AsyncTimer>>;
#[track_caller]
fn spawn(&self, future: Pin<Box<dyn Future<Output = ()> + Send>>);
#[cfg(not(wasm_browser))]
fn wrap_udp_socket(&self, t: std::net::UdpSocket) -> io::Result<Box<dyn AsyncUdpSocket>>;
fn now(&self) -> Instant {
Instant::now()
}
}
pub trait AsyncTimer: Send + Debug + 'static {
fn reset(self: Pin<&mut Self>, i: Instant);
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()>;
}
pub trait AsyncUdpSocket: Send + Sync + Debug + 'static {
fn create_sender(&self) -> Pin<Box<dyn UdpSender>>;
fn poll_recv(
&mut self,
cx: &mut Context<'_>,
bufs: &mut [IoSliceMut<'_>],
meta: &mut [RecvMeta],
) -> Poll<io::Result<usize>>;
fn local_addr(&self) -> io::Result<SocketAddr>;
fn max_receive_segments(&self) -> NonZeroUsize {
NonZeroUsize::MIN
}
fn may_fragment(&self) -> bool {
true
}
}
pub trait UdpSender: Send + Sync + Debug + 'static {
fn poll_send(
self: Pin<&mut Self>,
transmit: &Transmit<'_>,
cx: &mut Context<'_>,
) -> Poll<io::Result<()>>;
fn max_transmit_segments(&self) -> NonZeroUsize {
NonZeroUsize::MIN
}
}
pin_project_lite::pin_project! {
struct UdpSenderHelper<Socket, MakeWritableFutFn, WritableFut> {
socket: Socket,
make_writable_fut_fn: MakeWritableFutFn,
#[pin]
writable_fut: Option<WritableFut>,
}
}
impl<Socket, MakeWritableFutFn, WritableFut> Debug
for UdpSenderHelper<Socket, MakeWritableFutFn, WritableFut>
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("UdpSender")
}
}
impl<Socket, MakeWritableFutFn, WriteableFut>
UdpSenderHelper<Socket, MakeWritableFutFn, WriteableFut>
{
#[cfg(any(feature = "runtime-smol", feature = "runtime-tokio",))]
fn new(inner: Socket, make_fut: MakeWritableFutFn) -> Self {
Self {
socket: inner,
make_writable_fut_fn: make_fut,
writable_fut: None,
}
}
}
impl<Socket, MakeWritableFutFn, WritableFut> super::UdpSender
for UdpSenderHelper<Socket, MakeWritableFutFn, WritableFut>
where
Socket: UdpSenderHelperSocket,
MakeWritableFutFn: Fn(&Socket) -> WritableFut + Send + Sync + 'static,
WritableFut: Future<Output = io::Result<()>> + Send + Sync + 'static,
{
fn poll_send(
self: Pin<&mut Self>,
transmit: &udp::Transmit<'_>,
cx: &mut Context<'_>,
) -> Poll<io::Result<()>> {
let mut this = self.project();
loop {
if this.writable_fut.is_none() {
this.writable_fut
.set(Some((this.make_writable_fut_fn)(this.socket)));
}
let result =
std::task::ready!(this.writable_fut.as_mut().as_pin_mut().unwrap().poll(cx));
this.writable_fut.set(None);
result?;
match this.socket.try_send(transmit) {
Err(e) if e.kind() == io::ErrorKind::WouldBlock => continue,
result => return Poll::Ready(result),
}
}
}
fn max_transmit_segments(&self) -> NonZeroUsize {
self.socket.max_transmit_segments()
}
}
trait UdpSenderHelperSocket: Send + Sync + 'static {
fn try_send(&self, transmit: &udp::Transmit<'_>) -> io::Result<()>;
fn max_transmit_segments(&self) -> NonZeroUsize;
}
#[cfg(any(feature = "runtime-tokio", feature = "runtime-smol"))]
#[allow(clippy::needless_return)] pub fn default_runtime() -> Option<Arc<dyn Runtime>> {
#[cfg(feature = "runtime-tokio")]
{
if ::tokio::runtime::Handle::try_current().is_ok() {
return Some(Arc::new(TokioRuntime));
}
}
#[cfg(feature = "runtime-smol")]
{
return Some(Arc::new(SmolRuntime));
}
#[cfg(not(feature = "runtime-smol"))]
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::*;