1use crate::ops::runtime::timer::{TimerId, TimerOps};
2use std::{cell::RefCell, future::Future, rc::Rc, thread::LocalKey, time::Duration};
3
4#[derive(Clone, Copy, Debug, Eq, PartialEq)]
15pub struct ApiTimerHandle(TimerId);
16
17pub type TimerSlot = LocalKey<RefCell<Option<ApiTimerHandle>>>;
23
24pub fn set_lifecycle_timer(
27 delay: Duration,
28 label: impl Into<String>,
29 task: impl Future<Output = ()> + 'static,
30) -> ApiTimerHandle {
31 ApiTimerHandle(TimerOps::set(delay, label, task))
32}
33
34pub fn set_guarded(
37 slot: &'static TimerSlot,
38 delay: Duration,
39 label: impl Into<String>,
40 task: impl Future<Output = ()> + 'static,
41) -> bool {
42 slot.with_borrow_mut(|entry| {
43 if entry.is_some() {
44 return false;
45 }
46
47 let id = TimerOps::set(delay, label, task);
48 *entry = Some(ApiTimerHandle(id));
49 true
50 })
51}
52
53pub fn set_interval<F, Fut>(interval: Duration, label: impl Into<String>, task: F) -> ApiTimerHandle
55where
56 F: FnMut() -> Fut + 'static,
57 Fut: Future<Output = ()> + 'static,
58{
59 ApiTimerHandle(TimerOps::set_interval(interval, label, task))
60}
61
62pub fn set_guarded_interval<FInit, InitFut, FTick, TickFut>(
64 slot: &'static TimerSlot,
65 init_delay: Duration,
66 init_label: impl Into<String>,
67 init_task: FInit,
68 interval: Duration,
69 interval_label: impl Into<String>,
70 tick_task: FTick,
71) -> bool
72where
73 FInit: FnOnce() -> InitFut + 'static,
74 InitFut: Future<Output = ()> + 'static,
75 FTick: FnMut() -> TickFut + 'static,
76 TickFut: Future<Output = ()> + 'static,
77{
78 let init_label = init_label.into();
79 let interval_label = interval_label.into();
80
81 slot.with_borrow_mut(|entry| {
82 if entry.is_some() {
83 return false;
84 }
85
86 let init_id_cell = Rc::new(RefCell::new(None));
87 let init_id_cell_task = Rc::clone(&init_id_cell);
88
89 let init_id = TimerOps::set(init_delay, init_label, async move {
90 init_task().await;
91
92 let init_id = init_id_cell_task.borrow();
93 let Some(init_id) = init_id.as_ref() else {
94 return;
95 };
96
97 let still_armed = slot.with_borrow(|slot_val| slot_val.as_ref() == Some(init_id));
98 if !still_armed {
99 return;
100 }
101
102 let interval_id = TimerOps::set_interval(interval, interval_label, tick_task);
103 let interval_handle = ApiTimerHandle(interval_id);
104
105 slot.with_borrow_mut(|slot_val| {
108 let old = slot_val.replace(interval_handle);
109 if let Some(old_id) = old
110 && old_id != interval_handle
111 {
112 TimerOps::clear(old_id.0);
113 }
114 });
115 });
116
117 let init_handle = ApiTimerHandle(init_id);
118 *init_id_cell.borrow_mut() = Some(init_handle);
119 *entry = Some(init_handle);
120 true
121 })
122}
123
124pub fn clear_lifecycle_timer(handle: ApiTimerHandle) {
126 TimerOps::clear(handle.0);
127}