use crossbeam_queue::ArrayQueue;
use polling::{Events, Poller};
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::sync::Arc;
use std::time::Duration;
pub const DEFAULT_FRAME_QUEUE_CAPACITY: usize = 1024;
pub struct NetworkFrameQueues {
pub guest_to_host: ArrayQueue<Vec<u8>>,
pub host_to_guest: ArrayQueue<Vec<u8>>,
pub guest_wake: WakePipe,
pub host_wake: WakePipe,
pub relay_wake: WakePipe,
shutting_down: AtomicBool,
egress_bytes: Arc<AtomicU64>,
}
impl NetworkFrameQueues {
pub fn shared(capacity: usize) -> Arc<Self> {
let poll_loop = WakePipe::new();
let relay_wake = poll_loop.share();
Arc::new(Self {
guest_to_host: ArrayQueue::new(capacity),
host_to_guest: ArrayQueue::new(capacity),
guest_wake: poll_loop,
host_wake: WakePipe::new(),
relay_wake,
shutting_down: AtomicBool::new(false),
egress_bytes: Arc::new(AtomicU64::new(0)),
})
}
pub fn add_egress_bytes(&self, n: u64) {
self.egress_bytes.fetch_add(n, Ordering::Relaxed);
}
pub fn egress_bytes(&self) -> u64 {
self.egress_bytes.load(Ordering::Relaxed)
}
pub fn egress_counter(&self) -> Arc<AtomicU64> {
self.egress_bytes.clone()
}
pub fn begin_shutdown(&self) {
self.shutting_down.store(true, Ordering::SeqCst);
self.guest_wake.wake();
self.host_wake.wake();
self.relay_wake.wake();
}
pub fn is_shutting_down(&self) -> bool {
self.shutting_down.load(Ordering::SeqCst)
}
}
#[derive(Clone, Debug)]
pub struct WakePipe {
poller: Arc<Poller>,
}
impl WakePipe {
pub fn new() -> Self {
Self {
poller: Arc::new(Poller::new().expect("create poller for wake notification")),
}
}
pub fn share(&self) -> Self {
Self {
poller: self.poller.clone(),
}
}
pub fn poller(&self) -> &Arc<Poller> {
&self.poller
}
pub fn wake(&self) {
let _ = self.poller.notify();
}
pub fn drain(&self) {}
pub fn wait(&self, timeout: Option<Duration>) -> std::io::Result<bool> {
let mut events = Events::new();
let start = std::time::Instant::now();
let count = self.poller.wait(&mut events, timeout)?;
if count > 0 {
return Ok(true);
}
match timeout {
None => Ok(true),
Some(timeout) => Ok(start.elapsed() < timeout),
}
}
}
impl Default for WakePipe {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn wake_pipe_round_trip() {
let pipe = WakePipe::new();
pipe.wake();
assert!(pipe.wait(Some(Duration::from_millis(10))).unwrap());
pipe.drain();
assert!(!pipe.wait(Some(Duration::from_millis(1))).unwrap());
}
#[test]
fn shared_wake_notifies_same_waiter() {
let a = WakePipe::new();
let b = a.share();
b.wake();
assert!(a.wait(Some(Duration::from_millis(10))).unwrap());
a.drain();
assert!(!a.wait(Some(Duration::from_millis(1))).unwrap());
}
#[test]
fn queues_are_fifo() {
let queues = NetworkFrameQueues::shared(4);
queues.guest_to_host.push(vec![1, 2, 3]).unwrap();
queues.guest_to_host.push(vec![4, 5, 6]).unwrap();
assert_eq!(queues.guest_to_host.pop(), Some(vec![1, 2, 3]));
assert_eq!(queues.guest_to_host.pop(), Some(vec![4, 5, 6]));
assert_eq!(queues.guest_to_host.pop(), None);
}
}