time: allow storing state inside the driver struct.

This commit is contained in:
Dario Nieuwenhuis
2021-08-25 18:50:05 +02:00
parent 55b2d7b524
commit 7c0990ad1e
5 changed files with 218 additions and 197 deletions

View File

@ -0,0 +1,67 @@
use std::marker::PhantomData;
use std::sync::{Condvar, Mutex};
use super::{raw, Spawner};
pub struct Executor {
inner: raw::Executor,
not_send: PhantomData<*mut ()>,
signaler: &'static Signaler,
}
impl Executor {
pub fn new() -> Self {
let signaler = &*Box::leak(Box::new(Signaler::new()));
Self {
inner: raw::Executor::new(
|p| unsafe {
let s = &*(p as *const () as *const Signaler);
s.signal()
},
signaler as *const _ as _,
),
not_send: PhantomData,
signaler,
}
}
/// Runs the executor.
///
/// This function never returns.
pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
init(unsafe { self.inner.spawner() });
loop {
unsafe { self.inner.run_queued() };
self.signaler.wait()
}
}
}
struct Signaler {
mutex: Mutex<bool>,
condvar: Condvar,
}
impl Signaler {
fn new() -> Self {
Self {
mutex: Mutex::new(false),
condvar: Condvar::new(),
}
}
fn wait(&self) {
let mut signaled = self.mutex.lock().unwrap();
while !*signaled {
signaled = self.condvar.wait(signaled).unwrap();
}
*signaled = false;
}
fn signal(&self) {
let mut signaled = self.mutex.lock().unwrap();
*signaled = true;
self.condvar.notify_one();
}
}

View File

@ -26,32 +26,34 @@
//! This method has a few key advantages for something as foundational as timekeeping:
//!
//! - The time driver is available everywhere easily, without having to thread the implementation
//~ through generic parameters. This is especially helpful for libraries.
//! through generic parameters. This is especially helpful for libraries.
//! - It means comparing `Instant`s will always make sense: if there were multiple drivers
//! active, one could compare an `Instant` from driver A to an `Instant` from driver B, which
//! would yield incorrect results.
//!
/// # Example
///
/// ```
/// struct MyDriver; // not public!
/// embassy::time_driver_impl!(MyDriver);
///
/// unsafe impl embassy::time::driver::Driver for MyDriver {
/// fn now() -> u64 {
/// todo!()
/// }
/// unsafe fn allocate_alarm() -> Option<AlarmHandle> {
/// todo!()
/// }
/// fn set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
/// todo!()
/// }
/// fn set_alarm(alarm: AlarmHandle, timestamp: u64) {
/// todo!()
/// }
/// }
/// ```
//! # Example
//!
//! ```
//! use embassy::time::driver::{Driver, AlarmHandle};
//!
//! struct MyDriver{}; // not public!
//! embassy::time_driver_impl!(static DRIVER: MyDriver = MyDriver{});
//!
//! impl Driver for MyDriver {
//! fn now(&self) -> u64 {
//! todo!()
//! }
//! unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> {
//! todo!()
//! }
//! fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
//! todo!()
//! }
//! fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) {
//! todo!()
//! }
//! }
//! ```
/// Alarm handle, assigned by the driver.
#[derive(Clone, Copy)]
@ -76,22 +78,22 @@ impl AlarmHandle {
}
/// Time driver
pub trait Driver {
pub trait Driver: Send + Sync + 'static {
/// Return the current timestamp in ticks.
/// This is guaranteed to be monotonic, i.e. a call to now() will always return
/// a greater or equal value than earler calls.
fn now() -> u64;
fn now(&self) -> u64;
/// Try allocating an alarm handle. Returns None if no alarms left.
/// Initially the alarm has no callback set, and a null `ctx` pointer.
///
/// # Safety
/// It is UB to make the alarm fire before setting a callback.
unsafe fn allocate_alarm() -> Option<AlarmHandle>;
unsafe fn allocate_alarm(&self) -> Option<AlarmHandle>;
/// Sets the callback function to be called when the alarm triggers.
/// The callback may be called from any context (interrupt or thread mode).
fn set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ());
fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ());
/// Sets an alarm at the given timestamp. When the current timestamp reaches that
/// timestamp, the provided callback funcion will be called.
@ -99,7 +101,7 @@ pub trait Driver {
/// When callback is called, it is guaranteed that now() will return a value greater or equal than timestamp.
///
/// Only one alarm can be active at a time. This overwrites any previously-set alarm if any.
fn set_alarm(alarm: AlarmHandle, timestamp: u64);
fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64);
}
extern "Rust" {
@ -125,49 +127,34 @@ pub(crate) fn set_alarm(alarm: AlarmHandle, timestamp: u64) {
/// Set the time Driver implementation.
///
/// # Example
///
/// ```
/// struct MyDriver; // not public!
/// embassy::time_driver_impl!(MyDriver);
///
/// unsafe impl embassy::time::driver::Driver for MyDriver {
/// fn now() -> u64 {
/// todo!()
/// }
/// unsafe fn allocate_alarm() -> Option<AlarmHandle> {
/// todo!()
/// }
/// fn set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
/// todo!()
/// }
/// fn set_alarm(alarm: AlarmHandle, timestamp: u64) {
/// todo!()
/// }
/// }
/// ```
/// See the module documentation for an example.
#[macro_export]
macro_rules! time_driver_impl {
($t: ty) => {
(static $name:ident: $t: ty = $val:expr) => {
static $name: $t = $val;
#[no_mangle]
fn _embassy_time_now() -> u64 {
<$t as $crate::time::driver::Driver>::now()
<$t as $crate::time::driver::Driver>::now(&$name)
}
#[no_mangle]
unsafe fn _embassy_time_allocate_alarm() -> Option<AlarmHandle> {
<$t as $crate::time::driver::Driver>::allocate_alarm()
unsafe fn _embassy_time_allocate_alarm() -> Option<$crate::time::driver::AlarmHandle> {
<$t as $crate::time::driver::Driver>::allocate_alarm(&$name)
}
#[no_mangle]
fn _embassy_time_set_alarm_callback(
alarm: AlarmHandle,
alarm: $crate::time::driver::AlarmHandle,
callback: fn(*mut ()),
ctx: *mut (),
) {
<$t as $crate::time::driver::Driver>::set_alarm_callback(alarm, callback, ctx)
<$t as $crate::time::driver::Driver>::set_alarm_callback(&$name, alarm, callback, ctx)
}
#[no_mangle]
fn _embassy_time_set_alarm(alarm: AlarmHandle, timestamp: u64) {
<$t as $crate::time::driver::Driver>::set_alarm(alarm, timestamp)
fn _embassy_time_set_alarm(alarm: $crate::time::driver::AlarmHandle, timestamp: u64) {
<$t as $crate::time::driver::Driver>::set_alarm(&$name, alarm, timestamp)
}
};
}