use std::{
fmt::Debug,
future::Future,
io,
ops::DerefMut,
pin::Pin,
sync::{Arc, Weak},
task::{Context, Poll},
};
use tokio_util::task::AbortOnDropHandle;
use crate::{
QuicIO, QuicIoExt,
iface::{RwInterface, physical::InterfaceEventReceiver},
route::Router,
};
pub struct InterfaceContext {
iface: Weak<RwInterface>,
_task: AbortOnDropHandle<()>,
}
impl Debug for InterfaceContext {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("InterfaceContext")
.field("iface", &"..")
.field("task", &"..")
.finish()
}
}
impl InterfaceContext {
pub fn new(rw_iface: Arc<RwInterface>, mut events: InterfaceEventReceiver) -> Self {
let bind_uri = rw_iface.bind_uri();
let device = bind_uri
.as_iface_bind_uri()
.map(|(_, device, _)| device.to_owned());
let iface = Arc::downgrade(&rw_iface);
let task = AbortOnDropHandle::new(tokio::spawn({
let rw_iface = iface.clone();
let mut receive_task =
ReceiveTask::Running(Box::pin(receive_and_deliver(rw_iface.clone())));
async move {
loop {
tokio::select! {
Some(event) = events.recv() => {
if Some(event.device()) != device.as_deref() {
continue;
}
let Some(rw_iface) = rw_iface.upgrade() else { break };
if matches!(receive_task, ReceiveTask::Stopped)
|| rw_iface.is_alive().await.is_err_and(|e| {
tracing::debug!(target: "interface", %bind_uri, "Interface may not alive: {e}");
e.is_recoverable()
})
{
tracing::debug!(target: "interface", %bind_uri, "Rebinding interface");
_ = rw_iface.close().await;
rw_iface.rebind();
receive_task =
ReceiveTask::Running(Box::pin(receive_and_deliver(Arc::downgrade(&rw_iface))));
}
}
result = &mut receive_task => {
match result {
Ok(()) => tracing::debug!(target: "interface", %bind_uri, "Receive task completed due to interface freed"),
Err(e) => tracing::debug!(target: "interface", %bind_uri, "Receive task failed with error: {e}"),
}
receive_task = ReceiveTask::Stopped;
}
}
}
}
}));
Self { iface, _task: task }
}
pub fn iface(&self) -> &Weak<RwInterface> {
&self.iface
}
}
enum ReceiveTask<F> {
Running(F),
Stopped,
}
impl<F: Future + Unpin> Future for ReceiveTask<F> {
type Output = F::Output;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
if let ReceiveTask::Running(future) = self.deref_mut() {
return Pin::new(future).poll(cx);
}
Poll::Pending
}
}
async fn receive_and_deliver(iface: Weak<RwInterface>) -> io::Result<()> {
let (mut bufs, mut hdrs) = (vec![], vec![]);
loop {
let pkts = match iface.upgrade() {
Some(iface) => iface.recvmpkt(bufs.as_mut(), hdrs.as_mut()).await?,
None => return Ok(()),
};
for (pkt, way) in pkts {
Router::global().deliver(pkt, way).await;
}
}
}