riot_wrappers/ztimer/periodic.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
//! # [Periodic ZTimer API](https://doc.riot-os.org/ztimer_2periodic_8h.html)
use core::marker::PhantomPinned;
use core::mem::MaybeUninit;
use core::pin::Pin;
/// Return value of a periodic callback
#[derive(Copy, Clone, Debug)]
pub enum Behavior {
// The explicit values should make the into functions trivial
/// Invoke the callback on the next cycle
KeepGoing = riot_sys::ZTIMER_PERIODIC_KEEP_GOING as isize,
/// Stop invoking the callback
Abort = (riot_sys::ZTIMER_PERIODIC_KEEP_GOING as isize) ^ 1,
}
impl Into<riot_sys::libc::c_int> for Behavior {
fn into(self) -> riot_sys::libc::c_int {
match self {
Behavior::KeepGoing => riot_sys::ZTIMER_PERIODIC_KEEP_GOING as _,
// "any other value"
Behavior::Abort => (riot_sys::ZTIMER_PERIODIC_KEEP_GOING as riot_sys::libc::c_int) ^ 1,
}
}
}
impl Into<bool> for Behavior {
fn into(self) -> bool {
match self {
Behavior::KeepGoing => true,
Behavior::Abort => false,
}
}
}
/// Callback for a periodic timer
///
/// This is implemented as a trait (rather than Timer taking a callback directly) as to allow
/// interaction with the handler in a critical section in [Timer::alter].
pub trait Handler: Send {
fn trigger(&mut self) -> Behavior;
}
/// A periodic timer
///
/// This periodic timer is built on a [clock](super::Clock) and configured with a frequency and
/// tick handler.
///
/// It contains the handler and a `ztimer_periodic_t` C struct that then contains the actual timer
/// as well as a reference to the clock. Being self-referential by nature, it is mainly used in
/// pinned form. It can be started and stopped, and stops automatically when dropped.
pub struct Timer<H: Handler, const HZ: u32> {
// When pinned, this must note move.
timer: riot_sys::ztimer_periodic_t,
// When pinned, a reference to this is held in the timer, but the handler itself can be swapped
// around when the timer is not running or paused.
//
// (In a sense, we're treating this place in the struct like a bare_metal::Mutex, but as
// ZTimer's behavior of turning off all interrupts during execution is not made explicit
// anywhere by manifesting a CriticalSection, it's just done unsafely here right away -- and
// anyhow would need additional trickery to get a &mut out of it).
// FIXME: Should this be inside ... something (UnsafeCell is insufficient, as a &mut to it
// still implies exclusive access) that disallows assumptions on exclusivity?
handler: H,
// From the .start(), timer has an internal reference to the handler
_phantom: PhantomPinned,
}
impl<H: Handler, const HZ: u32> Timer<H, HZ> {
pub fn new(clock: super::Clock<HZ>, handler: H, ticks: super::Ticks<HZ>) -> Self {
let mut timer = MaybeUninit::uninit();
// Leaving the arg blank for the moment, to be set later when we have a Pin<&mut self>
//
// The type is self-referential (.timer.arg is the whole thing again), a property which is
// restored at start when pinned.
let timer = unsafe {
riot_sys::ztimer_periodic_init(
clock.0,
timer.as_mut_ptr(),
Some(Self::callback),
core::ptr::null_mut(),
ticks.0,
);
timer.assume_init()
};
Timer {
timer,
handler,
_phantom: PhantomPinned,
}
}
fn restore_internal_references(&mut self) {
self.timer.arg = &mut self.handler as *mut _ as *mut _;
self.timer.timer.arg = &mut self.timer as *mut _ as *mut _;
}
pub fn stop(&mut self) {
unsafe {
riot_sys::ztimer_periodic_stop(&mut self.timer);
}
}
extern "C" fn callback(arg: *mut riot_sys::libc::c_void) -> bool {
let handler = unsafe { &mut *(arg as *mut H) };
handler.trigger().into()
}
// Put on hold not only because I can't move the fields out due to the presence of a Drop
// implementation, but also because how would one get an owned self after the type was pinned?
// /// Stop the periodic timer, and return the handler that was in the timer
// pub fn to_parts(mut self) -> H {
// self.stop();
// return self.handler;
// }
/// Obtain a mutable reference to the handler.
///
/// This can be used, for example, to feed data into a handler that is sent out whenever the
/// timer triggers.
///
/// This is relatively invasive to the system as it creates a critical section (ie. possibly
/// delaying the execution of the next timer, or even other interrupts). In many cases, the
/// preferable way to send data to the timer is to use a lock-free data structure.
// This needs to take a &mut self to avoid nesting, otherwise two code paths could do nested
// .alter().
pub fn alter<R, F: FnOnce(&mut H) -> R>(self: &mut Pin<&mut Self>, f: F) -> R {
crate::interrupt::free(|_| {
// unsafe: Only accessing handler
let s = unsafe { Pin::into_inner_unchecked(self.as_mut()) };
f(&mut s.handler)
})
}
}
impl<H: Handler + 'static, const HZ: u32> Timer<H, HZ> {
#[doc(alias = "ztimer_periodic_start")]
/// Start the timer, calling the handler at every interval.
///
/// This requires a `Handler + 'static` because it relies on the timer's drop to stop the
/// process, and only a static handler can still safely be called if that drop never happens.
///
/// (For non-static handlers, a scoped version might be introduced later).
pub fn start(self: &mut Pin<&mut Self>) {
unsafe {
// unsafe: Nothing moved around with these references
let s = Pin::into_inner_unchecked(self.as_mut());
s.restore_internal_references();
// unsafe: C API
riot_sys::ztimer_periodic_start(&mut s.timer);
}
}
}
impl<H: Handler, const HZ: u32> Drop for Timer<H, HZ> {
fn drop(&mut self) {
self.stop();
// and then drop the fields
}
}