1use core::{ffi::c_void, ptr::NonNull};
19
20pub type TimerPtr = NonNull<()>;
22
23unsafe extern "Rust" {
24 fn esp_rtos_timer_create(
25 function: unsafe extern "C" fn(*mut c_void),
26 data: *mut c_void,
27 ) -> TimerPtr;
28 fn esp_rtos_timer_delete(timer: TimerPtr);
29
30 fn esp_rtos_timer_arm(timer: TimerPtr, timeout: u64, periodic: bool);
31 fn esp_rtos_timer_is_active(timer: TimerPtr) -> bool;
32 fn esp_rtos_timer_disarm(timer: TimerPtr);
33}
34
35pub trait TimerImplementation {
75 fn create(function: unsafe extern "C" fn(*mut c_void), data: *mut c_void) -> TimerPtr;
77
78 unsafe fn delete(timer: TimerPtr);
84
85 unsafe fn arm(timer: TimerPtr, timeout: u64, periodic: bool);
94
95 unsafe fn is_active(timer: TimerPtr) -> bool;
101
102 unsafe fn disarm(timer: TimerPtr);
108}
109
110#[macro_export]
111macro_rules! register_timer_implementation {
112 ($t: ty) => {
113 #[unsafe(no_mangle)]
114 #[inline]
115 fn esp_rtos_timer_create(
116 function: unsafe extern "C" fn(*mut ::core::ffi::c_void),
117 data: *mut ::core::ffi::c_void,
118 ) -> $crate::timer::TimerPtr {
119 <$t as $crate::timer::TimerImplementation>::create(function, data)
120 }
121
122 #[unsafe(no_mangle)]
123 #[inline]
124 fn esp_rtos_timer_delete(timer: $crate::timer::TimerPtr) {
125 unsafe { <$t as $crate::timer::TimerImplementation>::delete(timer) }
126 }
127
128 #[unsafe(no_mangle)]
129 #[inline]
130 fn esp_rtos_timer_arm(timer: $crate::timer::TimerPtr, timeout: u64, periodic: bool) {
131 unsafe { <$t as $crate::timer::TimerImplementation>::arm(timer, timeout, periodic) }
132 }
133
134 #[unsafe(no_mangle)]
135 #[inline]
136 fn esp_rtos_timer_is_active(timer: $crate::timer::TimerPtr) -> bool {
137 unsafe { <$t as $crate::timer::TimerImplementation>::is_active(timer) }
138 }
139
140 #[unsafe(no_mangle)]
141 #[inline]
142 fn esp_rtos_timer_disarm(timer: $crate::timer::TimerPtr) {
143 unsafe { <$t as $crate::timer::TimerImplementation>::disarm(timer) }
144 }
145 };
146}
147
148#[repr(transparent)]
152pub struct TimerHandle(TimerPtr);
153impl TimerHandle {
154 #[inline]
161 pub unsafe fn new(function: unsafe extern "C" fn(*mut c_void), data: *mut c_void) -> Self {
162 Self(unsafe { esp_rtos_timer_create(function, data) })
163 }
164
165 #[inline]
167 pub fn leak(self) -> TimerPtr {
168 let ptr = self.0;
169 core::mem::forget(self);
170 ptr
171 }
172
173 #[inline]
180 pub unsafe fn from_ptr(ptr: TimerPtr) -> Self {
181 Self(ptr)
182 }
183
184 #[inline]
192 pub unsafe fn ref_from_ptr(ptr: &TimerPtr) -> &Self {
193 unsafe { core::mem::transmute(ptr) }
194 }
195
196 #[inline]
201 pub fn arm(&self, timeout: u64, periodic: bool) {
202 unsafe { esp_rtos_timer_arm(self.0, timeout, periodic) }
203 }
204
205 #[inline]
207 pub fn is_active(&self) -> bool {
208 unsafe { esp_rtos_timer_is_active(self.0) }
209 }
210
211 #[inline]
213 pub fn disarm(&self) {
214 unsafe { esp_rtos_timer_disarm(self.0) }
215 }
216}
217
218impl Drop for TimerHandle {
219 #[inline]
220 fn drop(&mut self) {
221 unsafe { esp_rtos_timer_delete(self.0) };
222 }
223}
224
225#[cfg(feature = "ipc-implementations")]
226mod implementation {
227 use alloc::{boxed::Box, vec::Vec};
228 use core::{
229 cell::{RefCell, UnsafeCell},
230 ptr::NonNull,
231 sync::atomic::Ordering,
232 };
233
234 use esp_sync::NonReentrantMutex;
235 use portable_atomic::AtomicPtr;
236
237 use super::*;
238 use crate::semaphore::{SemaphoreHandle, SemaphoreKind, SemaphorePtr};
239
240 struct TimerQueueInner {
244 head: Option<NonNull<CompatTimer>>,
246 next_wakeup: u64,
247 semaphore: SemaphorePtr,
248 processing: bool,
249 scheduled_for_drop: Vec<TimerPtr>,
250 }
251
252 unsafe impl Send for TimerQueueInner {}
253
254 impl TimerQueueInner {
255 fn new() -> Self {
256 Self {
257 head: None,
258 next_wakeup: u64::MAX,
259 semaphore: SemaphoreHandle::new(SemaphoreKind::Counting { max: 1, initial: 0 })
260 .leak(),
261 processing: false,
262 scheduled_for_drop: Vec::new(),
263 }
264 }
265
266 fn enqueue(&mut self, timer: &CompatTimer) -> Option<SemaphorePtr> {
268 let head = self.head;
269 let props = timer.properties(self);
270 let due = props.next_due;
271
272 if !props.enqueued {
273 trace!("Enqueueing timer {:x}", timer as *const _ as usize);
274 props.enqueued = true;
275
276 props.next = head;
277 self.head = Some(NonNull::from(timer));
278 } else {
279 trace!("Already enqueued timer {:x}", timer as *const _ as usize);
280 }
281
282 if due < self.next_wakeup {
285 self.next_wakeup = due;
286 Some(self.semaphore)
287 } else {
288 None
289 }
290 }
291
292 fn dequeue(&mut self, timer: &CompatTimer) -> bool {
293 let mut current = self.head;
294 let mut prev: Option<NonNull<CompatTimer>> = None;
295
296 while let Some(current_timer) = current {
298 if core::ptr::eq(current_timer.as_ptr(), timer) {
299 let timer_props = timer.properties(self);
303 let next = timer_props.next.take();
304 timer_props.enqueued = false;
305
306 if let Some(mut p) = prev {
307 unsafe { p.as_mut().properties(self).next = next };
308 } else {
309 self.head = next;
310 }
311 return true;
312 }
313
314 prev = current;
315 current = unsafe { current_timer.as_ref().properties(self).next };
316 }
317
318 false
319 }
320 }
321
322 struct CompatTimerQueue {
323 inner: NonReentrantMutex<TimerQueueInner>,
324 }
325
326 unsafe impl Send for CompatTimerQueue {}
327
328 impl CompatTimerQueue {
329 fn ensure_initialized<'a>() -> &'a CompatTimerQueue {
331 static TIMER_QUEUE: AtomicPtr<CompatTimerQueue> = AtomicPtr::new(core::ptr::null_mut());
332
333 #[cold]
334 #[inline(never)]
335 fn initialize<'a>() -> &'a CompatTimerQueue {
336 trace!("Trying to initialize timer queue");
337 let boxed = Box::new(CompatTimerQueue {
338 inner: NonReentrantMutex::new(TimerQueueInner::new()),
339 });
340 let queue_ptr = NonNull::from(boxed.as_ref());
341
342 let mut forget = false;
343 let queue_ptr = loop {
344 match TIMER_QUEUE.compare_exchange(
345 core::ptr::null_mut(),
346 queue_ptr.as_ptr(),
347 Ordering::SeqCst,
348 Ordering::SeqCst,
349 ) {
350 Ok(_) => {
351 trace!("Successfully initialized timer queue");
353 forget = true;
354
355 let semaphore = boxed.with(|inner| inner.semaphore);
357 unsafe {
358 crate::task_create(
361 "timer",
362 timer_task,
363 semaphore.as_ptr().cast(),
364 2,
365 None,
366 8192,
367 );
368 }
369
370 break queue_ptr;
371 }
372 Err(queue) => {
373 if let Some(queue_ptr) = NonNull::new(queue) {
376 trace!("Timer queue already initialized");
377 break queue_ptr;
378 }
379 trace!("Retrying initialization");
380 }
381 }
382 };
383
384 if forget {
385 core::mem::forget(boxed);
386 }
387
388 unsafe { queue_ptr.as_ref() }
389 }
390
391 if let Some(queue) = NonNull::new(TIMER_QUEUE.load(Ordering::Acquire)) {
392 unsafe { queue.as_ref() }
393 } else {
394 initialize()
395 }
396 }
397
398 fn with_global<F, R>(f: F) -> R
402 where
403 F: FnOnce(&mut TimerQueueInner) -> R,
404 {
405 let queue = Self::ensure_initialized();
406 queue.with(f)
407 }
408
409 fn with<F, R>(&self, f: F) -> R
412 where
413 F: FnOnce(&mut TimerQueueInner) -> R,
414 {
415 self.inner.with(f)
416 }
417
418 fn process(&self, semaphore: &SemaphoreHandle) {
423 debug!("Processing timer queue");
424 let mut timers = self.with(|q| {
425 q.processing = true;
426 q.next_wakeup = u64::MAX;
427 q.head.take()
428 });
429
430 while let Some(current) = timers {
431 trace!("Checking timer: {:x}", current.addr());
432 let current_timer = unsafe { current.as_ref() };
433
434 let run_callback = self.with(|q| {
435 let props = current_timer.properties(q);
436
437 timers = props.next.take();
439 props.enqueued = false;
440
441 if !props.is_active {
442 trace!(
443 "Timer {:x} is inactive or dropped",
444 current_timer as *const _ as usize
445 );
446 return false;
447 }
448
449 if props.next_due > crate::now() {
450 trace!(
452 "Timer {:x} is not due yet",
453 current_timer as *const _ as usize
454 );
455 return false;
456 }
457
458 if props.periodic {
460 props.next_due += props.period;
461 }
462 props.is_active = props.periodic;
463 true
464 });
465
466 if run_callback {
467 debug!("Triggering timer: {:x}", current_timer as *const _ as usize);
468 (current_timer.callback.borrow_mut())();
469 }
470
471 self.with(|q| {
472 let props = current_timer.properties(q);
473
474 if props.is_active {
475 q.enqueue(current_timer);
476 } else {
477 trace!("Timer {:x} inactive", current_timer as *const _ as usize);
478 }
479 });
480 }
481
482 let next_wakeup = self.with(|q| {
483 while let Some(timer) = q.scheduled_for_drop.pop() {
484 trace!("Dropping timer {:x}", timer.as_ptr() as usize);
485 let timer = unsafe { Box::from_raw(timer.cast::<CompatTimer>().as_ptr()) };
486 q.dequeue(&timer);
487 core::mem::drop(timer);
488 }
489
490 q.processing = false;
491 q.next_wakeup
492 });
493
494 debug!("Timer queue next_wakeup: {}", next_wakeup);
495 semaphore.take_with_deadline(Some(next_wakeup));
496 }
497 }
498
499 struct TimerProperties {
500 is_active: bool,
501 next_due: u64,
502 period: u64,
503 periodic: bool,
504
505 enqueued: bool,
506 next: Option<NonNull<CompatTimer>>,
507 }
508
509 struct TimerQueueCell<T>(UnsafeCell<T>);
510
511 impl<T> TimerQueueCell<T> {
512 const fn new(inner: T) -> Self {
513 Self(UnsafeCell::new(inner))
514 }
515
516 fn get_mut<'a>(&'a self, _q: &'a mut TimerQueueInner) -> &'a mut T {
517 unsafe { &mut *self.0.get() }
518 }
519 }
520
521 pub struct CompatTimer {
528 callback: RefCell<Box<dyn FnMut() + Send>>,
529 timer_properties: TimerQueueCell<TimerProperties>,
531 }
532
533 impl CompatTimer {
534 pub fn new(callback: Box<dyn FnMut() + Send>) -> Self {
535 CompatTimer {
536 callback: RefCell::new(callback),
537 timer_properties: TimerQueueCell::new(TimerProperties {
538 is_active: false,
539 next_due: 0,
540 period: 0,
541 periodic: false,
542
543 enqueued: false,
544 next: None,
545 }),
546 }
547 }
548
549 unsafe fn from_ptr<'a>(ptr: TimerPtr) -> &'a Self {
550 unsafe { ptr.cast::<Self>().as_mut() }
551 }
552
553 fn arm(
554 &self,
555 q: &mut TimerQueueInner,
556 timeout: u64,
557 periodic: bool,
558 ) -> Option<SemaphorePtr> {
559 let next_due = crate::now() + timeout;
560
561 let props = self.properties(q);
562 props.is_active = true;
563 props.next_due = next_due;
564 props.period = timeout;
565 props.periodic = periodic;
566
567 q.enqueue(self)
568 }
569
570 fn is_active(&self, q: &mut TimerQueueInner) -> bool {
571 self.properties(q).is_active
572 }
573
574 fn disarm(&self, q: &mut TimerQueueInner) {
575 self.properties(q).is_active = false;
576
577 }
580
581 fn properties<'a>(&'a self, q: &'a mut TimerQueueInner) -> &'a mut TimerProperties {
582 self.timer_properties.get_mut(q)
583 }
584 }
585
586 impl TimerImplementation for CompatTimer {
587 fn create(func: unsafe extern "C" fn(*mut c_void), data: *mut c_void) -> TimerPtr {
588 struct CCallback {
590 func: unsafe extern "C" fn(*mut c_void),
591 data: *mut c_void,
592 }
593 unsafe impl Send for CCallback {}
594
595 impl CCallback {
596 unsafe fn call(&mut self) {
597 unsafe { (self.func)(self.data) }
598 }
599 }
600
601 let mut callback = CCallback { func, data };
602
603 let timer = Box::new(CompatTimer::new(Box::new(move || unsafe {
604 callback.call()
605 })));
606 NonNull::from(Box::leak(timer)).cast()
607 }
608
609 unsafe fn delete(timer: TimerPtr) {
610 let mut semaphore_to_give = None;
611 CompatTimerQueue::with_global(|q| {
612 q.scheduled_for_drop.push(timer);
615
616 if !q.processing && q.next_wakeup == u64::MAX {
619 q.next_wakeup = 0;
620
621 semaphore_to_give = Some(q.semaphore);
622 }
623
624 let timer = unsafe { CompatTimer::from_ptr(timer) };
625 timer.properties(q).is_active = false;
626 });
627
628 if let Some(semaphore_ptr) = semaphore_to_give {
629 let semaphore = unsafe { SemaphoreHandle::ref_from_ptr(&semaphore_ptr) };
630 semaphore.give();
631 }
632 }
633
634 unsafe fn arm(timer: TimerPtr, timeout: u64, periodic: bool) {
635 let timer = unsafe { CompatTimer::from_ptr(timer) };
636 if let Some(semaphore_ptr) =
637 CompatTimerQueue::with_global(|q| timer.arm(q, timeout, periodic))
638 {
639 let semaphore = unsafe { SemaphoreHandle::ref_from_ptr(&semaphore_ptr) };
640 semaphore.give();
641 }
642 }
643
644 unsafe fn is_active(timer: TimerPtr) -> bool {
645 let timer = unsafe { CompatTimer::from_ptr(timer) };
646 CompatTimerQueue::with_global(|q| timer.is_active(q))
647 }
648
649 unsafe fn disarm(timer: TimerPtr) {
650 let timer = unsafe { CompatTimer::from_ptr(timer) };
651 CompatTimerQueue::with_global(|q| timer.disarm(q))
652 }
653 }
654
655 pub(crate) extern "C" fn timer_task(semaphore_ptr: *mut c_void) {
660 let semaphore_ptr = NonNull::new(semaphore_ptr).unwrap().cast();
661 let semaphore = unsafe { SemaphoreHandle::ref_from_ptr(&semaphore_ptr) };
662
663 semaphore.take(None);
665
666 let queue = CompatTimerQueue::ensure_initialized();
667
668 loop {
669 queue.process(semaphore);
670 }
671 }
672}
673
674#[cfg(feature = "ipc-implementations")]
675pub use implementation::CompatTimer;