use alloc::boxed::Box;
use alloc::rc::Rc;
use core::cell::Cell;
use core::time::Duration;
use wasefire_applet_api::timer as api;
use wasefire_common::ptr::SharedPtr;
pub use self::api::Mode;
pub use self::api::Mode::*;
use crate::{convert, convert_unit};
pub trait Handler: 'static {
fn event(&self);
}
impl<F: Fn() + 'static> Handler for F {
fn event(&self) {
self()
}
}
#[must_use]
pub struct Timer<H: Handler> {
id: usize,
running: Cell<bool>,
handler: SharedPtr<H>,
}
impl<H: Handler> Timer<H> {
pub fn new(handler: H) -> Self {
let handler_func = Self::call;
let handler = Box::into_raw(Box::new(handler));
let handler_data = handler as *const u8;
let params = api::allocate::Params { handler_func, handler_data };
let id = convert(unsafe { api::allocate(params) }).unwrap();
Timer { id, running: Cell::new(false), handler: SharedPtr(handler) }
}
pub fn start_ms(&self, mode: Mode, duration_ms: usize) {
if self.running.replace(true) {
return;
}
let params = api::start::Params { id: self.id, mode: mode as usize, duration_ms };
convert_unit(unsafe { api::start(params) }).unwrap();
}
pub fn start(&self, mode: Mode, duration: Duration) {
self.start_ms(mode, duration.as_millis() as usize)
}
pub fn stop(&self) {
if !self.running.replace(false) {
return;
}
convert_unit(unsafe { api::stop(api::stop::Params { id: self.id }) }).unwrap();
}
pub fn leak(self) {
core::mem::forget(self);
}
extern "C" fn call(data: *const u8) {
let handler = unsafe { &*(data as *const H) };
handler.event();
}
}
impl<H: Handler> Drop for Timer<H> {
fn drop(&mut self) {
if self.running.get() {
self.stop();
}
let params = api::free::Params { id: self.id };
convert_unit(unsafe { api::free(params) }).unwrap();
drop(unsafe { Box::from_raw(self.handler.0 as *mut H) });
}
}
pub fn sleep_ms(duration_ms: usize) {
let done = Rc::new(Cell::new(false));
let timer = Timer::new({
let done = done.clone();
move || done.set(true)
});
timer.start_ms(Oneshot, duration_ms);
while !done.get() {
crate::scheduling::wait_for_callback();
}
}
pub fn sleep(duration: Duration) {
sleep_ms(duration.as_millis() as usize)
}
pub struct Timeout {
_timer: Timer<TimeoutHandler>,
handler: TimeoutHandler,
}
impl Timeout {
pub fn new_ms(timeout_ms: usize) -> Self {
let handler = TimeoutHandler { elapsed: Rc::new(Cell::new(false)) };
let timer = Timer::new(handler.clone());
timer.start_ms(Oneshot, timeout_ms);
Timeout { _timer: timer, handler }
}
pub fn new(timeout: Duration) -> Self {
Self::new_ms(timeout.as_millis() as usize)
}
pub fn is_over(&self) -> bool {
self.handler.elapsed.get()
}
}
#[derive(Clone)]
struct TimeoutHandler {
elapsed: Rc<Cell<bool>>,
}
impl Handler for TimeoutHandler {
fn event(&self) {
self.elapsed.set(true);
}
}