use super::*;
use ::smol::spawn;
use std::io::{Read, Write};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, OnceLock};
#[derive(Debug)]
pub struct SmolRuntime;
struct SmolJoinHandle(std::sync::Mutex<Option<::smol::Task<()>>>);
impl super::JoinHandleInner for SmolJoinHandle {
fn detach(&self) {
if let Some(task) = self.0.lock().unwrap().take() {
task.detach();
}
}
fn abort(&self) {
self.0.lock().unwrap().take();
}
fn is_finished(&self) -> bool {
self.0
.lock()
.unwrap()
.as_ref()
.is_none_or(|task| task.is_finished())
}
}
struct ReactorPool {
slots: Box<[OnceLock<Option<Arc<::smol::Executor<'static>>>>]>,
next: AtomicUsize,
}
impl ReactorPool {
fn new(size: usize) -> Self {
let size = size.clamp(1, super::MAX_REACTOR_POOL_SIZE);
let slots = (0..size)
.map(|_| OnceLock::new())
.collect::<Vec<_>>()
.into_boxed_slice();
Self {
slots,
next: AtomicUsize::new(0),
}
}
fn spawn(&self, future: Pin<Box<dyn Future<Output = ()> + Send>>) -> ::smol::Task<()> {
let idx = self.next.fetch_add(1, Ordering::Relaxed) % self.slots.len();
match self.slots[idx].get_or_init(|| spawn_reactor_thread(idx)) {
Some(executor) => executor.spawn(future),
None => spawn(future),
}
}
}
fn spawn_reactor_thread(idx: usize) -> Option<Arc<::smol::Executor<'static>>> {
let executor = Arc::new(::smol::Executor::new());
let thread_executor = executor.clone();
let spawned = std::thread::Builder::new()
.name(format!("webrtc-rx{idx}"))
.spawn(move || {
::smol::block_on(thread_executor.run(std::future::pending::<()>()));
});
match spawned {
Ok(_) => Some(executor),
Err(err) => {
log::error!("failed to spawn reactor pool thread: {err}");
None
}
}
}
static REACTOR_POOL: OnceLock<ReactorPool> = OnceLock::new();
impl Runtime for SmolRuntime {
fn spawn(&self, future: Pin<Box<dyn Future<Output = ()> + Send>>) -> super::JoinHandle {
let task = spawn(future);
super::JoinHandle {
inner: Box::new(SmolJoinHandle(std::sync::Mutex::new(Some(task)))),
}
}
fn spawn_reactor(&self, future: Pin<Box<dyn Future<Output = ()> + Send>>) -> super::JoinHandle {
let task = REACTOR_POOL
.get_or_init(|| ReactorPool::new(super::reactor_pool_size()))
.spawn(future);
super::JoinHandle {
inner: Box::new(SmolJoinHandle(std::sync::Mutex::new(Some(task)))),
}
}
fn wrap_udp_socket(&self, sock: std::net::UdpSocket) -> io::Result<Arc<dyn AsyncUdpSocket>> {
Ok(Arc::new(UdpSocket::new(sock)?) as Arc<dyn AsyncUdpSocket>)
}
fn wrap_tcp_listener(
&self,
listener: std::net::TcpListener,
) -> io::Result<Arc<dyn AsyncTcpListener>> {
listener.set_nonblocking(true)?;
Ok(Arc::new(TcpListener::new(listener)?))
}
fn connect_tcp<'a>(
&'a self,
remote_addr: SocketAddr,
) -> Pin<Box<dyn Future<Output = io::Result<Arc<dyn AsyncTcpStream>>> + Send + 'a>> {
Box::pin(async move {
let std_stream = std::net::TcpStream::connect(remote_addr)?;
std_stream.set_nonblocking(true)?;
let std_stream2 = std_stream.try_clone()?;
let read_io = ::smol::Async::new(std_stream)?;
let write_io = ::smol::Async::new(std_stream2)?;
let local_addr = read_io.get_ref().local_addr()?;
let peer_addr = read_io.get_ref().peer_addr()?;
Ok(Arc::new(TcpStream {
read_io,
write_io,
local_addr,
peer_addr,
}) as Arc<dyn AsyncTcpStream>)
})
}
}
#[derive(Debug)]
struct UdpSocket {
io: Arc<::smol::Async<std::net::UdpSocket>>,
state: Arc<::quinn_udp::UdpSocketState>,
}
impl UdpSocket {
fn new(sock: std::net::UdpSocket) -> io::Result<Self> {
let async_sock = ::smol::Async::new(sock)?;
let state =
::quinn_udp::UdpSocketState::new(::quinn_udp::UdpSockRef::from(async_sock.get_ref()))?;
Ok(Self {
io: Arc::new(async_sock),
state: Arc::new(state),
})
}
}
impl AsyncUdpSocket for UdpSocket {
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.io.write_with(|s| s.send_to(buf, target)).await })
}
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 { self.io.read_with(|s| s.recv_from(buf)).await })
}
fn local_addr(&self) -> io::Result<SocketAddr> {
self.io.get_ref().local_addr()
}
fn max_gso_segments(&self) -> usize {
self.state.max_gso_segments()
}
fn max_gro_segments(&self) -> usize {
self.state.gro_segments()
}
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 transmit = ::quinn_udp::Transmit {
destination: target,
ecn: ecn.and_then(::quinn_udp::EcnCodepoint::from_bits),
contents: buf,
segment_size: Some(segment_size),
src_ip: None,
};
self.io
.write_with(|s| self.state.send(::quinn_udp::UdpSockRef::from(s), &transmit))
.await?;
Ok(buf.len())
})
}
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 mut meta = [::quinn_udp::RecvMeta::default()];
self.io
.read_with(|s| {
let mut bufs = [std::io::IoSliceMut::new(buf)];
self.state
.recv(::quinn_udp::UdpSockRef::from(s), &mut bufs, &mut meta)
})
.await?;
let m = &meta[0];
Ok(GroRecv {
len: m.len,
stride: if m.stride == 0 {
m.len.max(1)
} else {
m.stride
},
peer_addr: m.addr,
})
})
}
}
#[derive(Debug)]
struct TcpListener {
io: ::smol::Async<std::net::TcpListener>,
}
impl TcpListener {
fn new(listener: std::net::TcpListener) -> io::Result<Self> {
let async_listener = ::smol::Async::new(listener)?;
Ok(Self { io: async_listener })
}
}
impl AsyncTcpListener for TcpListener {
fn accept<'a>(
&'a self,
) -> Pin<Box<dyn Future<Output = io::Result<(Arc<dyn AsyncTcpStream>, SocketAddr)>> + Send + 'a>>
{
Box::pin(async move {
let (std_stream, addr) = self.io.read_with(|io| io.accept()).await?;
std_stream.set_nonblocking(true)?;
let std_stream2 = std_stream.try_clone()?;
let read_io = ::smol::Async::new(std_stream)?;
let write_io = ::smol::Async::new(std_stream2)?;
let local_addr = read_io.get_ref().local_addr()?;
let peer_addr = read_io.get_ref().peer_addr()?;
Ok((
Arc::new(TcpStream {
read_io,
write_io,
local_addr,
peer_addr,
}) as Arc<dyn AsyncTcpStream>,
addr,
))
})
}
fn local_addr(&self) -> io::Result<SocketAddr> {
self.io.get_ref().local_addr()
}
}
#[derive(Debug)]
struct TcpStream {
read_io: ::smol::Async<std::net::TcpStream>,
write_io: ::smol::Async<std::net::TcpStream>,
local_addr: SocketAddr,
peer_addr: SocketAddr,
}
impl AsyncTcpStream for TcpStream {
fn read<'a, 'b>(
&'a self,
buf: &'b mut [u8],
) -> Pin<Box<dyn Future<Output = io::Result<usize>> + Send + 'b>>
where
'a: 'b,
{
Box::pin(async move { self.read_io.read_with(|mut io| io.read(buf)).await })
}
fn write_all<'a, 'b>(
&'a self,
buf: &'b [u8],
) -> Pin<Box<dyn Future<Output = io::Result<()>> + Send + 'b>>
where
'a: 'b,
{
Box::pin(async move { self.write_io.write_with(|mut io| io.write_all(buf)).await })
}
fn local_addr(&self) -> io::Result<SocketAddr> {
Ok(self.local_addr)
}
fn peer_addr(&self) -> io::Result<SocketAddr> {
Ok(self.peer_addr)
}
}
pub async fn sleep(duration: Duration) {
::smol::Timer::after(duration).await;
}
pub async fn yield_now() {
::smol::future::yield_now().await;
}
pub struct SmolInterval {
period: Duration,
deadline: std::time::Instant,
first: bool,
}
impl SmolInterval {
pub async fn tick(&mut self) {
if self.first {
self.first = false;
} else {
::smol::Timer::at(self.deadline).await;
}
self.deadline += self.period;
}
}
pub fn interval(period: Duration) -> SmolInterval {
SmolInterval {
period,
deadline: std::time::Instant::now() + period,
first: true,
}
}
pub async fn timeout<F, T>(duration: Duration, future: F) -> Result<T, ()>
where
F: std::future::Future<Output = T>,
{
::smol::future::or(async { Ok(future.await) }, async {
sleep(duration).await;
Err(())
})
.await
}
pub async fn resolve_host(host: &str) -> io::Result<Vec<SocketAddr>> {
::smol::net::resolve(host).await
}
pub struct SmolMutex<T: ?Sized>(pub Arc<::smol::lock::Mutex<T>>);
impl<T: ?Sized> Clone for SmolMutex<T> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<T> SmolMutex<T> {
pub fn new(value: T) -> Self {
Self(Arc::new(::smol::lock::Mutex::new(value)))
}
pub async fn lock(&self) -> ::smol::lock::MutexGuard<'_, T> {
self.0.lock().await
}
}
impl<T: ?Sized + Send> AsyncMutex<T> for SmolMutex<T> {
type Guard<'a>
= ::smol::lock::MutexGuard<'a, T>
where
T: 'a;
fn lock(&self) -> Pin<Box<dyn Future<Output = Self::Guard<'_>> + Send + '_>> {
Box::pin(self.0.lock())
}
}
pub struct SmolNotify(pub Arc<std::sync::Mutex<(bool, Vec<::smol::channel::Sender<()>>)>>);
impl Clone for SmolNotify {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl Default for SmolNotify {
fn default() -> Self {
Self::new()
}
}
impl SmolNotify {
pub fn new() -> Self {
Self(Arc::new(std::sync::Mutex::new((false, Vec::new()))))
}
pub fn notify_one(&self) {
let mut state = self.0.lock().unwrap();
state.0 = true;
if let Some(tx) = state.1.pop() {
let _ = tx.try_send(());
}
}
pub fn notify_waiters(&self) {
let mut state = self.0.lock().unwrap();
state.0 = true;
for tx in state.1.drain(..) {
let _ = tx.try_send(());
}
}
pub async fn notified(&self) {
let (tx, rx) = ::smol::channel::bounded(1);
{
let mut state = self.0.lock().unwrap();
if state.0 {
state.0 = false;
return;
}
state.1.push(tx);
}
let _ = rx.recv().await;
}
}
impl AsyncNotify for SmolNotify {
fn notify_one(&self) {
self.notify_one();
}
fn notify_waiters(&self) {
self.notify_waiters();
}
fn notified(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> {
let notify = self.0.clone();
Box::pin(async move {
let (tx, rx) = ::smol::channel::bounded(1);
{
let mut state = notify.lock().unwrap();
if state.0 {
state.0 = false;
return;
}
state.1.push(tx);
}
let _ = rx.recv().await;
})
}
}
pub struct SmolSender<T>(pub ::smol::channel::Sender<T>);
impl<T> Clone for SmolSender<T> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<T: Send> SmolSender<T> {
pub async fn send(&self, value: T) -> Result<(), SendError<T>> {
self.0.send(value).await.map_err(|e| SendError(e.0))
}
pub fn try_send(&self, value: T) -> Result<(), TrySendError<T>> {
self.0.try_send(value).map_err(|e| match e {
::smol::channel::TrySendError::Full(v) => TrySendError::Full(v),
::smol::channel::TrySendError::Closed(v) => TrySendError::Disconnected(v),
})
}
}
impl<T: Send> AsyncSender<T> for SmolSender<T> {
fn send(
&self,
value: T,
) -> Pin<Box<dyn Future<Output = Result<(), SendError<T>>> + Send + '_>> {
Box::pin(async move { self.0.send(value).await.map_err(|e| SendError(e.0)) })
}
fn try_send(&self, value: T) -> Result<(), TrySendError<T>> {
self.0.try_send(value).map_err(|e| match e {
::smol::channel::TrySendError::Full(v) => TrySendError::Full(v),
::smol::channel::TrySendError::Closed(v) => TrySendError::Disconnected(v),
})
}
}
pub struct SmolReceiver<T>(pub ::smol::channel::Receiver<T>);
impl<T: Send> SmolReceiver<T> {
pub async fn recv(&mut self) -> Option<T> {
self.0.recv().await.ok()
}
pub fn try_recv(&mut self) -> Result<T, TryRecvError> {
self.0.try_recv().map_err(|e| match e {
::smol::channel::TryRecvError::Empty => TryRecvError::Empty,
::smol::channel::TryRecvError::Closed => TryRecvError::Disconnected,
})
}
}
impl<T: Send> AsyncReceiver<T> for SmolReceiver<T> {
fn recv(&mut self) -> Pin<Box<dyn Future<Output = Option<T>> + Send + '_>> {
Box::pin(async move { self.0.recv().await.ok() })
}
fn try_recv(&mut self) -> Result<T, TryRecvError> {
self.0.try_recv().map_err(|e| match e {
::smol::channel::TryRecvError::Empty => TryRecvError::Empty,
::smol::channel::TryRecvError::Closed => TryRecvError::Disconnected,
})
}
}
pub fn channel<T: Send>(capacity: usize) -> (SmolSender<T>, SmolReceiver<T>) {
let (tx, rx) = ::smol::channel::bounded(capacity);
(SmolSender(tx), SmolReceiver(rx))
}
#[derive(Clone)]
pub struct SmolBroadcastSender<T>(pub ::async_broadcast::Sender<T>);
impl<T: Send + Clone + 'static> SmolBroadcastSender<T> {
pub fn send(&self, value: T) -> Result<usize, super::BroadcastSendError<T>> {
match self.0.try_broadcast(value) {
Ok(_) => Ok(self.0.receiver_count()),
Err(::async_broadcast::TrySendError::Inactive(v)) => Err(super::BroadcastSendError(v)),
Err(::async_broadcast::TrySendError::Closed(v)) => Err(super::BroadcastSendError(v)),
Err(::async_broadcast::TrySendError::Full(v)) => Err(super::BroadcastSendError(v)),
}
}
pub fn subscribe(&self) -> SmolBroadcastReceiver<T> {
SmolBroadcastReceiver(self.0.new_receiver())
}
pub fn receiver_count(&self) -> usize {
self.0.receiver_count()
}
}
pub struct SmolBroadcastReceiver<T>(pub ::async_broadcast::Receiver<T>);
impl<T: Send + Clone + 'static> SmolBroadcastReceiver<T> {
pub async fn recv(&mut self) -> Result<T, super::BroadcastRecvError> {
self.0.recv().await.map_err(|e| match e {
::async_broadcast::RecvError::Overflowed(n) => super::BroadcastRecvError::Lagged(n),
::async_broadcast::RecvError::Closed => super::BroadcastRecvError::Closed,
})
}
}
pub fn broadcast_channel<T: Send + Clone + 'static>(capacity: usize) -> SmolBroadcastSender<T> {
let (mut tx, _rx) = ::async_broadcast::broadcast(capacity);
tx.set_overflow(true);
SmolBroadcastSender(tx)
}
pub fn block_on<F: std::future::Future>(future: F) -> F::Output {
::smol::block_on(future)
}