use super::component_api::OmpComponentHandle;
use super::server::{ServerComponent, query_component};
use super::types::UID;
use std::ptr::NonNull;
pub const TIMERS_COMPONENT_UID: UID = 0x2ad8_124c_5ea2_57a3;
const SLOT_CREATE_INTERVAL: usize = 16;
const SLOT_TIMER_KILL: usize = 10;
#[repr(C)]
pub struct ITimersComponent {
_opaque: [u8; 0],
}
#[repr(C)]
pub struct ITimer {
_opaque: [u8; 0],
}
#[cfg(not(target_env = "msvc"))]
#[repr(C)]
pub struct TimerHandlerVTable {
pub timeout: unsafe extern "C" fn(*mut TimerTimeOutHandler, *mut ITimer),
pub free: unsafe extern "C" fn(*mut TimerTimeOutHandler, *mut ITimer),
}
#[cfg(target_env = "msvc")]
#[repr(C)]
pub struct TimerHandlerVTable {
pub timeout: unsafe extern "thiscall" fn(*mut TimerTimeOutHandler, *mut ITimer),
pub free: unsafe extern "thiscall" fn(*mut TimerTimeOutHandler, *mut ITimer),
}
#[repr(C)]
pub struct TimerTimeOutHandler {
pub vtable: *const TimerHandlerVTable,
}
unsafe impl Send for TimerTimeOutHandler {}
unsafe impl Sync for TimerTimeOutHandler {}
#[cfg(not(target_env = "msvc"))]
type CreateFn = unsafe extern "C" fn(
this: *mut ITimersComponent,
handler: *mut TimerTimeOutHandler,
interval_ms: i64,
repeating: bool,
) -> *mut ITimer;
#[cfg(target_env = "msvc")]
type CreateFn = unsafe extern "thiscall" fn(
this: *mut ITimersComponent,
handler: *mut TimerTimeOutHandler,
interval_ms: i64,
repeating: bool,
) -> *mut ITimer;
#[cfg(not(target_env = "msvc"))]
type KillFn = unsafe extern "C" fn(this: *mut ITimer);
#[cfg(target_env = "msvc")]
type KillFn = unsafe extern "thiscall" fn(this: *mut ITimer);
pub unsafe fn query_timers_component(
components: *mut super::server::ServerComponentList,
) -> *mut ITimersComponent {
if components.is_null() {
return std::ptr::null_mut();
}
let raw = unsafe { query_component(components, TIMERS_COMPONENT_UID) };
raw.cast::<ITimersComponent>()
}
pub unsafe fn create_repeating_timer(
timers: *mut ITimersComponent,
handler: *mut TimerTimeOutHandler,
interval_ms: i64,
) -> *mut ITimer {
if handler.is_null() {
return std::ptr::null_mut();
}
let Some((_, slot)) = (unsafe {
super::vtable::secondary_call_target(timers.cast::<u8>(), 0, SLOT_CREATE_INTERVAL)
}) else {
return std::ptr::null_mut();
};
let create: CreateFn = unsafe { std::mem::transmute(slot) };
unsafe { create(timers, handler, interval_ms, true) }
}
#[derive(Debug, Clone, Copy)]
pub struct TimersComponent {
ptr: NonNull<ServerComponent>,
}
impl OmpComponentHandle for TimersComponent {
const UID: UID = TIMERS_COMPONENT_UID;
unsafe fn from_raw(ptr: NonNull<ServerComponent>) -> Self {
Self { ptr }
}
fn as_raw(&self) -> NonNull<ServerComponent> {
self.ptr
}
}
impl TimersComponent {
#[must_use]
pub fn name(&self) -> Option<String> {
super::component_api::component_name(self)
}
#[must_use]
pub fn version(&self) -> Option<super::types::SemanticVersion> {
super::component_api::component_version(self)
}
pub unsafe fn create_repeating(
&self,
handler: *mut TimerTimeOutHandler,
interval_ms: i64,
) -> *mut ITimer {
unsafe {
create_repeating_timer(
self.ptr.as_ptr().cast::<ITimersComponent>(),
handler,
interval_ms,
)
}
}
}
pub unsafe fn kill_timer(timer: *mut ITimer) {
let Some((_, slot)) =
(unsafe { super::vtable::secondary_call_target(timer.cast::<u8>(), 0, SLOT_TIMER_KILL) })
else {
return;
};
let kill: KillFn = unsafe { std::mem::transmute(slot) };
unsafe { kill(timer) };
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn timers_component_uid_is_known_value() {
assert_eq!(TIMERS_COMPONENT_UID, 0x2ad8_124c_5ea2_57a3);
}
#[test]
fn timers_component_uid_via_trait() {
assert_eq!(
<TimersComponent as OmpComponentHandle>::UID,
TIMERS_COMPONENT_UID
);
}
#[test]
fn timer_handler_has_vtable_at_offset_zero() {
assert_eq!(std::mem::offset_of!(TimerTimeOutHandler, vtable), 0);
}
#[test]
fn timer_handler_size_is_one_pointer() {
assert_eq!(
std::mem::size_of::<TimerTimeOutHandler>(),
std::mem::size_of::<*const ()>()
);
}
#[test]
fn timer_handler_vtable_has_two_slots() {
assert_eq!(
std::mem::size_of::<TimerHandlerVTable>(),
2 * std::mem::size_of::<*const ()>()
);
}
#[test]
fn create_repeating_timer_returns_null_when_handler_is_null() {
let timers = std::ptr::null_mut::<ITimersComponent>();
let ret = unsafe { create_repeating_timer(timers, std::ptr::null_mut(), 5) };
assert!(ret.is_null());
}
#[test]
fn create_repeating_timer_returns_null_when_component_is_null() {
let fake_handler = std::ptr::dangling_mut::<TimerTimeOutHandler>();
let ret = unsafe { create_repeating_timer(std::ptr::null_mut(), fake_handler, 5) };
assert!(ret.is_null());
}
#[test]
fn kill_timer_is_noop_for_null_pointer() {
unsafe { kill_timer(std::ptr::null_mut()) };
}
#[test]
fn query_timers_component_returns_null_for_null_list() {
let ret = unsafe { query_timers_component(std::ptr::null_mut()) };
assert!(ret.is_null());
}
}