msb_krun_utils 0.1.18

Shared utilities for msb_krun microVMs
Documentation
// Copyright 2026 Super Rad Company.
// SPDX-License-Identifier: Apache-2.0

//! Windows waitable-handle backend exposed through libkrun's epoll-shaped API.

use std::collections::HashMap;
use std::io;
use std::os::windows::io::{AsRawHandle, RawHandle};
use std::sync::Mutex;

use bitflags::bitflags;

use crate::event::{EventSource, WaitContext, WaitEvent};

#[repr(i32)]
pub enum ControlOperation {
    Add,
    Modify,
    Delete,
}

bitflags! {
    pub struct EventSet: u32 {
        const IN = 0b0000_0001;
        const OUT = 0b0000_0010;
        const ERROR = 0b0000_0100;
        const READ_HANG_UP = 0b0000_1000;
        const EDGE_TRIGGERED = 0b0001_0000;
        const HANG_UP = 0b0010_0000;
        const PRIORITY = 0b0100_0000;
        const WAKE_UP = 0b1000_0000;
        const ONE_SHOT = 0b0001_0000_0000;
        const EXCLUSIVE = 0b0010_0000_0000;
    }
}

#[derive(Clone, Copy, Default)]
pub struct EpollEvent {
    events: u32,
    data: u64,
}

#[derive(Debug)]
pub struct Epoll {
    context: Mutex<WaitContext>,
    registrations: Mutex<HashMap<RawHandle, u64>>,
}

impl EpollEvent {
    pub fn new(events: EventSet, data: u64) -> Self {
        Self {
            events: events.bits(),
            data,
        }
    }

    pub fn events(&self) -> u32 {
        self.events
    }

    pub fn event_set(&self) -> EventSet {
        EventSet::from_bits_truncate(self.events)
    }

    pub fn data(&self) -> u64 {
        self.data
    }

    pub fn fd(&self) -> RawHandle {
        self.data as usize as RawHandle
    }
}

impl std::fmt::Debug for EpollEvent {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{{ events: {}, data: {} }}", self.events(), self.data())
    }
}

impl Epoll {
    pub fn new() -> io::Result<Self> {
        Ok(Self {
            context: Mutex::new(WaitContext::new()),
            registrations: Mutex::new(HashMap::new()),
        })
    }

    pub fn ctl(
        &self,
        operation: ControlOperation,
        handle: RawHandle,
        event: &EpollEvent,
    ) -> io::Result<()> {
        let mut context = self.context.lock().unwrap();
        let mut registrations = self.registrations.lock().unwrap();

        match operation {
            ControlOperation::Add => {
                context.add(
                    EventSource::waitable_handle(handle, event.data),
                    event_set_to_wait_event_set(event.event_set()),
                )?;
                registrations.insert(handle, event.data);
                Ok(())
            }
            ControlOperation::Modify => {
                let token = registrations.get(&handle).copied().ok_or_else(|| {
                    io::Error::new(io::ErrorKind::NotFound, "handle is not registered")
                })?;
                context.modify(token, event_set_to_wait_event_set(event.event_set()))
            }
            ControlOperation::Delete => {
                let token = registrations.remove(&handle).ok_or_else(|| {
                    io::Error::new(io::ErrorKind::NotFound, "handle is not registered")
                })?;
                context.delete(token)
            }
        }
    }

    pub fn wait(
        &self,
        max_events: usize,
        timeout: i32,
        events: &mut [EpollEvent],
    ) -> io::Result<usize> {
        let mut wait_events = vec![WaitEvent::default(); max_events.min(events.len())];
        let count = self
            .context
            .lock()
            .unwrap()
            .wait(timeout, &mut wait_events)?;

        for (index, event) in wait_events.into_iter().take(count).enumerate() {
            events[index] =
                EpollEvent::new(wait_event_set_to_event_set(event.events()), event.token());
        }

        Ok(count)
    }
}

impl AsRawHandle for Epoll {
    fn as_raw_handle(&self) -> RawHandle {
        std::ptr::null_mut()
    }
}

fn event_set_to_wait_event_set(events: EventSet) -> crate::event::EventSet {
    let mut wait_events = crate::event::EventSet::empty();
    if events.contains(EventSet::IN) {
        wait_events |= crate::event::EventSet::IN;
    }
    if events.contains(EventSet::OUT) {
        wait_events |= crate::event::EventSet::OUT;
    }
    wait_events
}

fn wait_event_set_to_event_set(events: crate::event::EventSet) -> EventSet {
    let mut epoll_events = EventSet::empty();
    if events.contains(crate::event::EventSet::IN) {
        epoll_events |= EventSet::IN;
    }
    if events.contains(crate::event::EventSet::OUT) {
        epoll_events |= EventSet::OUT;
    }
    if events.contains(crate::event::EventSet::ERROR) {
        epoll_events |= EventSet::ERROR;
    }
    if events.contains(crate::event::EventSet::HANG_UP) {
        epoll_events |= EventSet::HANG_UP;
    }
    if events.contains(crate::event::EventSet::READ_HANG_UP) {
        epoll_events |= EventSet::READ_HANG_UP;
    }
    epoll_events
}