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