use std::time::Duration;
use windows_sys::Win32::{
Foundation::FILETIME,
System::Threading::{
CloseThreadpoolTimer, CreateThreadpoolTimer, PTP_TIMER, PTP_TIMER_CALLBACK,
SetThreadpoolTimer, WaitForThreadpoolTimerCallbacks,
},
};
pub(crate) struct ThreadpoolTimer {
handle: PTP_TIMER,
}
unsafe impl Send for ThreadpoolTimer {}
unsafe impl Sync for ThreadpoolTimer {}
impl ThreadpoolTimer {
pub(crate) unsafe fn new(
callback: PTP_TIMER_CALLBACK,
context: *mut core::ffi::c_void,
) -> Option<Self> {
let h = unsafe { CreateThreadpoolTimer(callback, context, std::ptr::null()) };
if h == 0 as PTP_TIMER {
None
} else {
Some(Self { handle: h })
}
}
pub(crate) fn set_relative(&self, due: Option<Duration>) {
match due {
Some(d) => {
let ticks = i64::try_from(d.as_nanos() / 100).unwrap_or(i64::MAX);
#[expect(
clippy::arithmetic_side_effects,
reason = "ticks is in [0, i64::MAX] (saturated above), so -ticks cannot overflow"
)]
let neg = -ticks;
let [b0, b1, b2, b3, b4, b5, b6, b7] = neg.to_le_bytes();
let ft = FILETIME {
dwLowDateTime: u32::from_le_bytes([b0, b1, b2, b3]),
dwHighDateTime: u32::from_le_bytes([b4, b5, b6, b7]),
};
unsafe { SetThreadpoolTimer(self.handle, &raw const ft, 0, 0) };
}
None => {
unsafe { SetThreadpoolTimer(self.handle, std::ptr::null(), 0, 0) };
}
}
}
#[cfg(debug_assertions)]
pub(crate) fn as_raw(&self) -> PTP_TIMER {
self.handle
}
}
impl Drop for ThreadpoolTimer {
fn drop(&mut self) {
self.set_relative(None);
unsafe {
WaitForThreadpoolTimerCallbacks(self.handle, 1);
CloseThreadpoolTimer(self.handle);
}
}
}