jay-compositor 1.15.0

The Jay compositor
use crate::io_uring::IoUring;
use crate::io_uring::IoUringData;
use crate::io_uring::IoUringError;
use crate::io_uring::IoUringTaskId;
use crate::io_uring::Task;
use crate::io_uring::ops::timeout::timespec64;
use crate::io_uring::sys::IORING_OP_TIMEOUT;
use crate::io_uring::sys::IORING_TIMEOUT_ABS;
use crate::io_uring::sys::io_uring_cqe;
use crate::io_uring::sys::io_uring_sqe;
use jay_algorithms::oserror::OsError;
use std::cell::Cell;
use std::rc::Rc;
use uapi::c;

pub trait TimeoutCallback {
    fn completed(self: Rc<Self>, res: Result<(), OsError>, data: u64);
}

pub struct PendingTimeout {
    data: Rc<IoUringData>,
    shared: Rc<TimeoutExternalTaskShared>,
    id: IoUringTaskId,
}

impl Drop for PendingTimeout {
    fn drop(&mut self) {
        if self.shared.id.get() != self.id {
            return;
        }
        self.shared.callback.take();
        self.data.cancel_task(self.id);
    }
}

#[derive(Default)]
struct TimeoutExternalTaskShared {
    id: Cell<IoUringTaskId>,
    callback: Cell<Option<Rc<dyn TimeoutCallback>>>,
}

#[derive(Default)]
pub struct TimeoutExternalTask {
    timespec: timespec64,
    shared: Rc<TimeoutExternalTaskShared>,
    data: u64,
}

impl IoUring {
    pub fn timeout_external(
        &self,
        timeout_nsec: u64,
        callback: Rc<dyn TimeoutCallback>,
        data: u64,
    ) -> Result<PendingTimeout, IoUringError> {
        self.ring.check_destroyed()?;
        let mut pw = self.ring.cached_timeouts_external.pop().unwrap_or_default();
        pw.shared.id.set(self.ring.id_raw());
        pw.shared.callback.set(Some(callback));
        pw.timespec = timespec64 {
            tv_sec: (timeout_nsec / 1_000_000_000) as _,
            tv_nsec: (timeout_nsec % 1_000_000_000) as _,
        };
        pw.data = data;
        let pending = PendingTimeout {
            data: self.ring.clone(),
            shared: pw.shared.clone(),
            id: pw.shared.id.get(),
        };
        self.ring.schedule(pw);
        Ok(pending)
    }
}

unsafe impl Task for TimeoutExternalTask {
    fn id(&self) -> IoUringTaskId {
        self.shared.id.get()
    }

    fn complete(self: Box<Self>, ring: &IoUringData, cqe: &io_uring_cqe) {
        let res = cqe.res;
        if let Some(pr) = self.shared.callback.take() {
            let res = if res == -c::ETIME {
                Ok(())
            } else {
                map_err!(res).map(drop)
            };
            pr.completed(res, self.data);
        }
        ring.cached_timeouts_external.push(self);
    }

    fn encode(&self, sqe: &mut io_uring_sqe) {
        sqe.opcode = IORING_OP_TIMEOUT;
        sqe.u2.addr = &self.timespec as *const _ as _;
        sqe.len = 1;
        sqe.u3.timeout_flags = IORING_TIMEOUT_ABS;
        sqe.u1.off = 0;
    }
}