embassy/embassy-time/src/driver_wasm.rs
2024-12-22 21:00:23 +01:00

104 lines
2.5 KiB
Rust

use std::sync::Mutex;
use embassy_time_driver::Driver;
use embassy_time_queue_utils::Queue;
use wasm_bindgen::prelude::*;
use wasm_timer::Instant as StdInstant;
struct AlarmState {
token: Option<f64>,
}
impl AlarmState {
const fn new() -> Self {
Self { token: None }
}
}
#[wasm_bindgen]
extern "C" {
fn setTimeout(closure: &Closure<dyn FnMut()>, millis: u32) -> f64;
fn clearTimeout(token: f64);
}
struct TimeDriver {
inner: Mutex<Inner>,
}
struct Inner {
alarm: AlarmState,
zero_instant: Option<StdInstant>,
queue: Queue,
closure: Option<Closure<dyn FnMut()>>,
}
unsafe impl Send for Inner {}
embassy_time_driver::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver {
inner: Mutex::new(Inner{
zero_instant: None,
queue: Queue::new(),
alarm: AlarmState::new(),
closure: None,
}),
});
impl Inner {
fn init(&mut self) -> StdInstant {
*self.zero_instant.get_or_insert_with(StdInstant::now)
}
fn now(&mut self) -> u64 {
StdInstant::now().duration_since(self.zero_instant.unwrap()).as_micros() as u64
}
fn set_alarm(&mut self, timestamp: u64) -> bool {
if let Some(token) = self.alarm.token {
clearTimeout(token);
}
let now = self.now();
if timestamp <= now {
false
} else {
let timeout = (timestamp - now) as u32;
let closure = self.closure.get_or_insert_with(|| Closure::new(dispatch));
self.alarm.token = Some(setTimeout(closure, timeout / 1000));
true
}
}
}
impl Driver for TimeDriver {
fn now(&self) -> u64 {
let mut inner = self.inner.lock().unwrap();
let zero = inner.init();
StdInstant::now().duration_since(zero).as_micros() as u64
}
fn schedule_wake(&self, at: u64, waker: &core::task::Waker) {
let mut inner = self.inner.lock().unwrap();
inner.init();
if inner.queue.schedule_wake(at, waker) {
let now = inner.now();
let mut next = inner.queue.next_expiration(now);
while !inner.set_alarm(next) {
let now = inner.now();
next = inner.queue.next_expiration(now);
}
}
}
}
fn dispatch() {
let inner = &mut *DRIVER.inner.lock().unwrap();
let now = inner.now();
let mut next = inner.queue.next_expiration(now);
while !inner.set_alarm(next) {
let now = inner.now();
next = inner.queue.next_expiration(now);
}
}