use std::io;
use std::os::fd::{AsFd, AsRawFd, RawFd};
use tokio::io::Interest;
use tokio::io::unix::AsyncFd;
use crate::block::{Block, Frame, FrameMeta, PacketType};
use crate::channel::{ChannelBuilder, RxBackend, TxBackend};
use crate::error::Result;
use crate::sys::Stats;
struct FdHolder(RawFd);
impl AsRawFd for FdHolder {
fn as_raw_fd(&self) -> RawFd {
self.0
}
}
impl ChannelBuilder {
pub fn build_async(&self) -> Result<(AsyncSender, AsyncReceiver)> {
let parts = self.open()?;
let tx = AsyncSender::new(parts.tx)?;
let rx = AsyncReceiver::new(parts.rx)?;
Ok((tx, rx))
}
pub fn build_fanout_rx_async(&self, count: usize) -> Result<Vec<AsyncReceiver>> {
self.build_fanout_backends(count)?.into_iter().map(AsyncReceiver::new).collect()
}
}
pub struct AsyncSender {
readiness: AsyncFd<FdHolder>,
backend: TxBackend,
}
impl AsyncSender {
fn new(backend: TxBackend) -> Result<Self> {
let fd = backend.borrow_fd().as_raw_fd();
let readiness = AsyncFd::with_interest(FdHolder(fd), Interest::WRITABLE)
.map_err(crate::Error::Io)?;
Ok(AsyncSender { readiness, backend })
}
pub async fn send(&mut self, frame: &[u8]) -> Result<()> {
if let Some(max) = self.backend.max_frame_len() {
if frame.len() > max {
return Err(crate::Error::FrameTooLarge { len: frame.len(), max });
}
}
loop {
match self.backend.try_send(frame) {
Ok(_) => return Ok(()),
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
let mut guard = self.readiness.writable().await.map_err(crate::Error::Io)?;
guard.clear_ready();
}
Err(e) => return Err(e.into()),
}
}
}
pub async fn send_batch(&mut self, frames: &[&[u8]]) -> Result<usize> {
loop {
match self.backend.try_send_batch(frames) {
Ok(0) => {
let mut guard = self.readiness.writable().await.map_err(crate::Error::Io)?;
guard.clear_ready();
}
Ok(n) => return Ok(n),
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
let mut guard = self.readiness.writable().await.map_err(crate::Error::Io)?;
guard.clear_ready();
}
Err(e) => return Err(e.into()),
}
}
}
pub fn as_fd(&self) -> std::os::fd::BorrowedFd<'_> {
self.backend.borrow_fd()
}
}
pub struct AsyncReceiver {
readiness: AsyncFd<FdHolder>,
backend: RxBackend,
stats_total: Stats,
}
impl AsyncReceiver {
fn new(backend: RxBackend) -> Result<Self> {
let fd = backend.borrow_fd().as_raw_fd();
let readiness = AsyncFd::with_interest(FdHolder(fd), Interest::READABLE)
.map_err(crate::Error::Io)?;
Ok(AsyncReceiver { readiness, backend, stats_total: Stats::default() })
}
pub async fn recv_block(&mut self) -> Result<Block<'_>> {
match &self.backend {
RxBackend::Ring(_) => {
loop {
if let RxBackend::Ring(ring) = &self.backend {
if ring.block_ready() {
break;
}
}
let mut guard = self.readiness.readable().await.map_err(crate::Error::Io)?;
let ready = matches!(&self.backend, RxBackend::Ring(r) if r.block_ready());
if !ready {
guard.clear_ready();
}
}
match &mut self.backend {
RxBackend::Ring(ring) => Ok(ring.recv_block().expect("block reported ready")),
_ => unreachable!("backend kind is stable"),
}
}
RxBackend::Basic { .. } => {
let (n, pkttype) = loop {
let attempt = match &mut self.backend {
RxBackend::Basic { sock, buf } => sock.recv_into(buf),
_ => unreachable!("backend kind is stable"),
};
match attempt {
Ok(v) => break v,
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
let mut g = self.readiness.readable().await.map_err(crate::Error::Io)?;
g.clear_ready();
}
Err(e) => return Err(e.into()),
}
};
let RxBackend::Basic { buf, .. } = &self.backend else {
unreachable!("backend kind is stable")
};
let captured = n.min(buf.len());
let meta = FrameMeta {
wire_len: n,
timestamp: None,
vlan: None,
packet_type: PacketType::from_raw(pkttype),
};
Ok(Block::single(Some(Frame::new(&buf[..captured], meta))))
}
RxBackend::Dummy { .. } => {
let frame = loop {
let popped = match &mut self.backend {
RxBackend::Dummy { queue, .. } => {
queue.lock().expect("dummy queue poisoned").pop_front()
}
_ => unreachable!("backend kind is stable"),
};
match popped {
Some(Ok(v)) => break v,
Some(Err(e)) => return Err(e.into()),
None => {
let mut g = self.readiness.readable().await.map_err(crate::Error::Io)?;
let eof = match &self.backend {
RxBackend::Dummy { readiness, .. } => {
crate::dummy::drain_readiness(readiness.as_fd())
}
_ => false,
};
g.clear_ready();
let empty = match &self.backend {
RxBackend::Dummy { queue, .. } => {
queue.lock().expect("dummy queue poisoned").is_empty()
}
_ => true,
};
if eof && empty {
return Err(io::Error::from(io::ErrorKind::UnexpectedEof).into());
}
}
}
};
let RxBackend::Dummy { last, .. } = &mut self.backend else {
unreachable!("backend kind is stable")
};
*last = frame;
let meta = FrameMeta {
wire_len: last.len(),
timestamp: None,
vlan: None,
packet_type: crate::channel::dummy_packet_type(last),
};
Ok(Block::single(Some(Frame::new(&last[..], meta))))
}
}
}
pub fn stats(&mut self) -> Result<Stats> {
let delta = match &mut self.backend {
RxBackend::Ring(ring) => ring.stats()?,
RxBackend::Basic { sock, .. } => sock.statistics()?,
RxBackend::Dummy { .. } => Stats::default(),
};
self.stats_total.accumulate(delta);
Ok(self.stats_total)
}
pub fn set_promiscuous(&self, on: bool) -> Result<()> {
match &self.backend {
RxBackend::Ring(ring) => ring.set_promiscuous(on),
RxBackend::Basic { sock, .. } => sock.set_promiscuous(on),
RxBackend::Dummy { .. } => Ok(()),
}
}
pub fn as_fd(&self) -> std::os::fd::BorrowedFd<'_> {
self.backend.borrow_fd()
}
}