use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Duration;
use tokio::sync::mpsc;
use crate::error::{Result, VmmError};
use arcbox_hypervisor::VcpuExit;
#[derive(Debug)]
pub enum VmmEvent {
VcpuExit {
vcpu_id: u32,
exit: VcpuExit,
},
DeviceIo {
device_id: u32,
is_read: bool,
addr: u64,
data: Option<u64>,
},
Timer {
id: u32,
},
Shutdown,
}
pub struct EventLoop {
running: Arc<AtomicBool>,
event_tx: mpsc::UnboundedSender<VmmEvent>,
event_rx: mpsc::UnboundedReceiver<VmmEvent>,
}
impl EventLoop {
pub fn new() -> Result<Self> {
let (event_tx, event_rx) = mpsc::unbounded_channel();
Ok(Self {
running: Arc::new(AtomicBool::new(false)),
event_tx,
event_rx,
})
}
#[must_use]
pub fn event_sender(&self) -> mpsc::UnboundedSender<VmmEvent> {
self.event_tx.clone()
}
#[must_use]
pub fn is_running(&self) -> bool {
self.running.load(Ordering::SeqCst)
}
pub fn start(&mut self) -> Result<()> {
self.running.store(true, Ordering::SeqCst);
tracing::debug!("Event loop started");
Ok(())
}
pub fn stop(&mut self) {
self.running.store(false, Ordering::SeqCst);
tracing::debug!("Event loop stopped");
}
pub fn post_event(&self, event: VmmEvent) -> Result<()> {
self.event_tx
.send(event)
.map_err(|e| VmmError::EventLoop(format!("failed to post event: {e}")))
}
pub async fn poll(&mut self) -> Option<VmmEvent> {
if !self.is_running() {
return None;
}
tokio::select! {
event = self.event_rx.recv() => {
event
}
() = tokio::time::sleep(Duration::from_millis(100)) => {
None
}
}
}
pub fn try_poll(&mut self) -> Option<VmmEvent> {
self.event_rx.try_recv().ok()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_event_loop_creation() {
let event_loop = EventLoop::new().unwrap();
assert!(!event_loop.is_running());
}
#[tokio::test]
async fn test_event_posting() {
let mut event_loop = EventLoop::new().unwrap();
event_loop.start().unwrap();
event_loop.post_event(VmmEvent::Shutdown).unwrap();
let event = event_loop.poll().await;
assert!(matches!(event, Some(VmmEvent::Shutdown)));
}
#[tokio::test]
async fn test_event_loop_stop() {
let mut event_loop = EventLoop::new().unwrap();
event_loop.start().unwrap();
assert!(event_loop.is_running());
event_loop.stop();
assert!(!event_loop.is_running());
let event = event_loop.poll().await;
assert!(event.is_none());
}
}