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