#![allow(clippy::type_complexity)]
use std::sync::atomic::{AtomicUsize, Ordering};
use std::{fmt::Debug, future::Future, io, net::SocketAddr, pin::Pin, sync::Arc, time::Duration};
static REACTOR_POOL_SIZE: AtomicUsize = AtomicUsize::new(0);
pub(crate) const MAX_REACTOR_POOL_SIZE: usize = 1024;
pub fn set_reactor_pool_size(size: usize) {
REACTOR_POOL_SIZE.store(size, Ordering::Relaxed);
}
pub(crate) fn reactor_pool_size() -> usize {
let override_size = REACTOR_POOL_SIZE.load(Ordering::Relaxed);
let resolved = if override_size != 0 {
override_size
} else {
std::env::var("WEBRTC_REACTOR_POOL_SIZE")
.ok()
.and_then(|v| v.parse::<usize>().ok())
.filter(|&n| n != 0)
.unwrap_or_else(|| {
std::thread::available_parallelism()
.map(|n| n.get())
.unwrap_or(4)
})
};
resolved.clamp(1, MAX_REACTOR_POOL_SIZE)
}
pub struct JoinHandle {
inner: Box<dyn JoinHandleInner>,
}
impl JoinHandle {
pub fn abort(&self) {
self.inner.abort();
}
pub fn is_finished(&self) -> bool {
self.inner.is_finished()
}
}
impl Drop for JoinHandle {
fn drop(&mut self) {
self.inner.detach();
}
}
trait JoinHandleInner: 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>>) -> JoinHandle;
fn spawn_reactor(&self, future: Pin<Box<dyn Future<Output = ()> + Send>>) -> 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>>;
}
#[derive(Debug, Clone, Copy)]
pub struct GroRecv {
pub len: usize,
pub stride: usize,
pub peer_addr: SocketAddr,
}
pub trait AsyncUdpSocket: Send + Sync + Debug + 'static {
fn send_to<'a>(
&'a self,
buf: &'a [u8],
target: SocketAddr,
) -> Pin<Box<dyn Future<Output = io::Result<usize>> + Send + 'a>>;
fn recv_from<'a>(
&'a self,
buf: &'a mut [u8],
) -> Pin<Box<dyn Future<Output = io::Result<(usize, SocketAddr)>> + Send + 'a>>;
fn local_addr(&self) -> io::Result<SocketAddr>;
fn max_gso_segments(&self) -> usize {
1
}
fn max_gro_segments(&self) -> usize {
1
}
fn send_segments<'a>(
&'a self,
buf: &'a [u8],
segment_size: usize,
target: SocketAddr,
ecn: Option<u8>,
) -> Pin<Box<dyn Future<Output = io::Result<usize>> + Send + 'a>> {
Box::pin(async move {
let _ = ecn;
let step = if segment_size == 0 {
buf.len().max(1)
} else {
segment_size
};
let mut sent = 0;
for chunk in buf.chunks(step) {
sent += self.send_to(chunk, target).await?;
}
Ok(sent)
})
}
fn recv_gro<'a>(
&'a self,
buf: &'a mut [u8],
) -> Pin<Box<dyn Future<Output = io::Result<GroRecv>> + Send + 'a>> {
Box::pin(async move {
let (len, peer_addr) = self.recv_from(buf).await?;
Ok(GroRecv {
len,
stride: if len == 0 { 1 } else { len },
peer_addr,
})
})
}
}
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 trait AsyncMutex<T: ?Sized>: Send + Sync {
type Guard<'a>: std::ops::Deref<Target = T> + std::ops::DerefMut + Send + 'a
where
Self: 'a,
T: 'a;
fn lock(&self) -> Pin<Box<dyn Future<Output = Self::Guard<'_>> + Send + '_>>;
}
pub trait AsyncNotify: Send + Sync {
fn notify_one(&self);
fn notify_waiters(&self);
fn notified(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>>;
}
pub trait AsyncSender<T>: Send + Sync {
fn send(&self, value: T)
-> Pin<Box<dyn Future<Output = Result<(), SendError<T>>> + Send + '_>>;
fn try_send(&self, value: T) -> Result<(), TrySendError<T>>;
}
pub trait AsyncReceiver<T>: Send {
fn recv(&mut self) -> Pin<Box<dyn Future<Output = Option<T>> + Send + '_>>;
fn try_recv(&mut self) -> Result<T, TryRecvError>;
}
#[derive(Debug)]
pub struct SendError<T>(pub T);
#[derive(Debug)]
pub enum TrySendError<T> {
Full(T),
Disconnected(T),
}
#[derive(Debug)]
pub enum TryRecvError {
Empty,
Disconnected,
}
impl<T> std::fmt::Display for SendError<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "channel disconnected")
}
}
impl<T: std::fmt::Debug> std::error::Error for SendError<T> {}
#[derive(Debug)]
pub struct BroadcastSendError<T>(pub T);
#[derive(Debug)]
pub enum BroadcastRecvError {
Closed,
Lagged(u64),
}
impl<T> std::fmt::Display for BroadcastSendError<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "broadcast send failed: no receivers")
}
}
impl<T: std::fmt::Debug> std::error::Error for BroadcastSendError<T> {}
impl std::fmt::Display for BroadcastRecvError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
BroadcastRecvError::Closed => write!(f, "broadcast channel closed"),
BroadcastRecvError::Lagged(n) => write!(f, "broadcast receiver lagged by {n}"),
}
}
}
impl std::error::Error for BroadcastRecvError {}
#[cfg(any(feature = "runtime-tokio", feature = "runtime-smol"))]
pub fn default_runtime() -> Option<std::sync::Arc<dyn Runtime>> {
#[cfg(feature = "runtime-tokio")]
{
Some(std::sync::Arc::new(TokioRuntime))
}
#[cfg(all(not(feature = "runtime-tokio"), feature = "runtime-smol"))]
{
Some(std::sync::Arc::new(smol::SmolRuntime))
}
}
#[cfg(not(any(feature = "runtime-tokio", feature = "runtime-smol")))]
pub fn default_runtime() -> Option<std::sync::Arc<dyn Runtime>> {
None
}
#[cfg(any(feature = "runtime-tokio", feature = "runtime-smol"))]
pub fn smol_runtime() -> Option<std::sync::Arc<dyn Runtime>> {
#[cfg(feature = "runtime-smol")]
{
Some(std::sync::Arc::new(smol::SmolRuntime))
}
#[cfg(not(feature = "runtime-smol"))]
None
}
#[cfg(feature = "runtime-tokio")]
mod tokio;
#[cfg(feature = "runtime-tokio")]
pub use tokio::TokioRuntime;
#[cfg(feature = "runtime-tokio")]
pub use tokio::{
TokioInterval, block_on, broadcast_channel, channel, interval, resolve_host, sleep, timeout,
yield_now,
};
#[cfg(feature = "runtime-tokio")]
pub type Interval = TokioInterval;
#[cfg(feature = "runtime-tokio")]
pub type Mutex<T> = tokio::TokioMutex<T>;
#[cfg(feature = "runtime-tokio")]
pub type Notify = tokio::TokioNotify;
#[cfg(feature = "runtime-tokio")]
pub type Sender<T> = tokio::TokioSender<T>;
#[cfg(feature = "runtime-tokio")]
pub type Receiver<T> = tokio::TokioReceiver<T>;
#[cfg(feature = "runtime-tokio")]
pub type BroadcastSender<T> = tokio::TokioBroadcastSender<T>;
#[cfg(feature = "runtime-tokio")]
pub type BroadcastReceiver<T> = tokio::TokioBroadcastReceiver<T>;
#[cfg(feature = "runtime-smol")]
mod smol;
#[cfg(all(not(feature = "runtime-tokio"), feature = "runtime-smol"))]
pub use smol::SmolRuntime;
#[cfg(all(not(feature = "runtime-tokio"), feature = "runtime-smol"))]
pub use smol::{
SmolInterval, block_on, broadcast_channel, channel, interval, resolve_host, sleep, timeout,
yield_now,
};
#[cfg(all(not(feature = "runtime-tokio"), feature = "runtime-smol"))]
pub type Interval = SmolInterval;
#[cfg(all(not(feature = "runtime-tokio"), feature = "runtime-smol"))]
pub type Mutex<T> = smol::SmolMutex<T>;
#[cfg(all(not(feature = "runtime-tokio"), feature = "runtime-smol"))]
pub type Notify = smol::SmolNotify;
#[cfg(all(not(feature = "runtime-tokio"), feature = "runtime-smol"))]
pub type Sender<T> = smol::SmolSender<T>;
#[cfg(all(not(feature = "runtime-tokio"), feature = "runtime-smol"))]
pub type Receiver<T> = smol::SmolReceiver<T>;
#[cfg(all(not(feature = "runtime-tokio"), feature = "runtime-smol"))]
pub type BroadcastSender<T> = smol::SmolBroadcastSender<T>;
#[cfg(all(not(feature = "runtime-tokio"), feature = "runtime-smol"))]
pub type BroadcastReceiver<T> = smol::SmolBroadcastReceiver<T>;
#[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>>,
}
impl AsyncUdpSocket for FakeUdp {
fn send_to<'a>(
&'a self,
buf: &'a [u8],
_target: SocketAddr,
) -> Pin<Box<dyn Future<Output = io::Result<usize>> + Send + 'a>> {
Box::pin(async move {
self.sent.lock().unwrap().push(buf.to_vec());
Ok(buf.len())
})
}
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 data = self.to_recv.lock().unwrap();
let n = data.len().min(buf.len());
buf[..n].copy_from_slice(&data[..n]);
Ok((n, "127.0.0.1:9".parse::<SocketAddr>().unwrap()))
})
}
fn local_addr(&self) -> io::Result<SocketAddr> {
Ok("127.0.0.1:0".parse::<SocketAddr>().unwrap())
}
}
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_segments_loops_send_to() {
let s = FakeUdp::default();
let buf = [1u8, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4];
let sent = futures::executor::block_on(s.send_segments(&buf, 3, addr(), None)).unwrap();
assert_eq!(sent, 11);
let calls = s.sent.lock().unwrap();
assert_eq!(calls.len(), 4);
assert_eq!(calls[0], vec![1, 1, 1]);
assert_eq!(calls[3], vec![4, 4]);
}
#[test]
fn default_send_segments_zero_size_is_one_datagram() {
let s = FakeUdp::default();
let buf = [7u8; 10];
futures::executor::block_on(s.send_segments(&buf, 0, addr(), Some(2))).unwrap();
let calls = s.sent.lock().unwrap();
assert_eq!(
calls.len(),
1,
"segment_size 0 must send one datagram, not shred"
);
assert_eq!(calls[0].len(), 10);
}
#[test]
fn default_recv_gro_is_single_datagram() {
let s = FakeUdp::default();
*s.to_recv.lock().unwrap() = vec![9, 9, 9, 9, 9];
let mut buf = [0u8; 32];
let gro = futures::executor::block_on(s.recv_gro(&mut buf)).unwrap();
assert_eq!(gro.len, 5);
assert_eq!(
gro.stride, 5,
"stride == len for a single (non-GRO) datagram"
);
assert_eq!(gro.peer_addr, "127.0.0.1:9".parse::<SocketAddr>().unwrap());
}
}