ferroui_base/threading/
dispatcher_timer.rs1use std::cell::{Cell, RefCell};
2use std::rc::{Rc, Weak};
3use std::sync::Arc;
4use std::time::Duration;
5
6use super::{Dispatcher, DispatcherOperation, DispatcherPriority};
7use crate::reactive::{Disposable, IDisposable};
8use crate::utilities::HandlerList;
9use crate::BoxedValue;
10
11crate::ferro_thread_local! {
12 static ACTIVE_TIMERS_COUNT: Cell<i32> = const { Cell::new(0) };
13}
14
15pub struct DispatcherTimer {
22 weak_self: Weak<DispatcherTimer>,
23 dispatcher: Arc<Dispatcher>,
24 priority: DispatcherPriority,
25 interval: Cell<Duration>,
26 operation: RefCell<Option<DispatcherOperation>>,
27 is_enabled: Cell<bool>,
28 due_time_in_ms: Cell<i64>,
30 tick: HandlerList<dyn Fn(&DispatcherTimer)>,
31 tag: RefCell<Option<BoxedValue>>,
32}
33
34impl DispatcherTimer {
35 #[allow(dead_code)]
37 pub(crate) fn active_timers_count() -> i32 {
38 ACTIVE_TIMERS_COUNT.with(Cell::get)
39 }
40
41 pub fn new() -> Rc<DispatcherTimer> {
44 Self::with_interval(Duration::ZERO, DispatcherPriority::BACKGROUND, &Dispatcher::current_dispatcher())
45 }
46
47 pub fn with_priority(priority: DispatcherPriority) -> Rc<DispatcherTimer> {
50 Self::with_interval(Duration::ZERO, priority, &Dispatcher::current_dispatcher())
51 }
52
53 pub fn with_priority_and_dispatcher(
56 priority: DispatcherPriority,
57 dispatcher: &Arc<Dispatcher>,
58 ) -> Rc<DispatcherTimer> {
59 Self::with_interval(Duration::ZERO, priority, dispatcher)
60 }
61
62 pub fn with_interval(
70 interval: Duration,
71 priority: DispatcherPriority,
72 dispatcher: &Arc<Dispatcher>,
73 ) -> Rc<DispatcherTimer> {
74 dispatcher.verify_access();
75
76 DispatcherPriority::validate(priority, "priority");
77 if priority == DispatcherPriority::INACTIVE {
78 panic!("Specified priority is not valid. (parameter 'priority')");
79 }
80
81 if interval.as_millis() > i32::MAX as u128 {
82 panic!("interval: TimeSpan period must be less than or equal to Int32.MaxValue.");
83 }
84
85 Rc::new_cyclic(|weak_self| DispatcherTimer {
86 weak_self: weak_self.clone(),
87 dispatcher: dispatcher.clone(),
88 priority,
89 interval: Cell::new(interval),
90 operation: RefCell::new(None),
91 is_enabled: Cell::new(false),
92 due_time_in_ms: Cell::new(0),
93 tick: HandlerList::new(),
94 tag: RefCell::new(None),
95 })
96 }
97
98 pub fn with_callback(
102 interval: Duration,
103 priority: DispatcherPriority,
104 callback: impl Fn(&DispatcherTimer) + 'static,
105 ) -> Rc<DispatcherTimer> {
106 Self::with_callback_and_dispatcher(interval, priority, &Dispatcher::current_dispatcher(), callback)
107 }
108
109 pub fn with_callback_and_dispatcher(
113 interval: Duration,
114 priority: DispatcherPriority,
115 dispatcher: &Arc<Dispatcher>,
116 callback: impl Fn(&DispatcherTimer) + 'static,
117 ) -> Rc<DispatcherTimer> {
118 let timer = Self::with_interval(interval, priority, dispatcher);
119 timer.tick.add(Rc::new(callback));
120 timer.start();
121 timer
122 }
123
124 pub fn dispatcher(&self) -> &Arc<Dispatcher> {
126 &self.dispatcher
127 }
128
129 pub fn is_enabled(&self) -> bool {
131 self.is_enabled.get()
132 }
133
134 pub fn set_is_enabled(&self, value: bool) {
136 if !value && self.is_enabled.get() {
137 self.stop();
138 } else if value && !self.is_enabled.get() {
139 self.start();
140 }
141 }
142
143 pub fn interval(&self) -> Duration {
145 self.interval.get()
146 }
147
148 pub fn set_interval(&self, value: Duration) {
150 self.dispatcher.verify_access();
151 let mut update_os_timer = false;
152
153 self.interval.set(value);
154
155 if self.is_enabled.get() {
156 self.due_time_in_ms.set(self.dispatcher.now() + value.as_millis() as i64);
157 update_os_timer = true;
158 }
159
160 if update_os_timer {
161 self.dispatcher.reschedule_timers();
162 }
163 }
164
165 pub fn start(&self) {
167 self.dispatcher.verify_access();
168 if !self.is_enabled.get() {
169 self.is_enabled.set(true);
170 ACTIVE_TIMERS_COUNT.with(|count| count.set(count.get() + 1));
171
172 self.restart();
173 }
174 }
175
176 pub fn stop(&self) {
178 self.dispatcher.verify_access();
179 let mut update_os_timer = false;
180
181 if self.is_enabled.get() {
182 self.is_enabled.set(false);
183 ACTIVE_TIMERS_COUNT.with(|count| count.set(count.get() - 1));
184 update_os_timer = true;
185
186 let operation = self.operation.borrow_mut().take();
188 if let Some(operation) = operation {
189 operation.abort();
190 }
191 }
192
193 if update_os_timer {
194 self.dispatcher.remove_timer(self);
195 }
196 }
197
198 pub fn run(
203 action: impl Fn() -> bool + 'static,
204 interval: Duration,
205 priority: DispatcherPriority,
206 ) -> Rc<dyn IDisposable> {
207 let timer = DispatcherTimer::with_priority(priority);
208 timer.set_interval(interval);
209
210 timer.tick.add(Rc::new(move |timer: &DispatcherTimer| {
211 if !action() {
212 timer.stop();
213 }
214 }));
215
216 timer.start();
217
218 Disposable::create(move || timer.stop())
219 }
220
221 pub fn run_once(action: impl Fn() + 'static, interval: Duration, priority: DispatcherPriority) -> Rc<dyn IDisposable> {
225 let interval = if interval != Duration::ZERO { interval } else { Duration::from_nanos(100) };
227
228 let timer = DispatcherTimer::with_priority(priority);
229 timer.set_interval(interval);
230
231 timer.tick.add(Rc::new(move |timer: &DispatcherTimer| {
232 action();
233 timer.stop();
234 }));
235
236 timer.start();
237
238 Disposable::create(move || timer.stop())
239 }
240
241 pub fn tick(&self, handler: impl Fn(&DispatcherTimer) + 'static) -> Rc<dyn IDisposable> {
244 let token = self.tick.add(Rc::new(handler));
245 let timer = self.weak_self.clone();
246 Disposable::create(move || {
247 if let Some(timer) = timer.upgrade() {
248 timer.tick.remove(token);
249 }
250 })
251 }
252
253 pub fn tag(&self) -> Option<BoxedValue> {
255 self.tag.borrow().clone()
256 }
257
258 pub fn set_tag(&self, value: Option<BoxedValue>) {
259 *self.tag.borrow_mut() = value;
260 }
261
262 fn restart(&self) {
263 if self.operation.borrow().is_some() {
264 return;
266 }
267 let Some(this) = self.weak_self.upgrade() else {
268 return;
269 };
270
271 let timer = this.clone();
273 let operation =
274 self.dispatcher.invoke_async_local_with_priority(move || timer.fire_tick(), DispatcherPriority::INACTIVE);
275 *self.operation.borrow_mut() = Some(operation);
276
277 self.due_time_in_ms.set(self.dispatcher.now() + self.interval.get().as_millis() as i64);
278
279 if self.interval.get().as_millis() == 0 && self.dispatcher.check_access() {
280 self.promote();
282 } else {
283 self.dispatcher.add_timer(&this);
284 }
285 }
286
287 pub(crate) fn promote(&self) {
289 let operation = self.operation.borrow().clone();
291 if let Some(operation) = operation {
292 operation.set_priority(self.priority);
293 }
294 }
295
296 pub(crate) fn due_time_in_ms(&self) -> i64 {
297 self.due_time_in_ms.get()
298 }
299
300 pub(crate) fn shift_due_time(&self, shift: i64) {
302 self.due_time_in_ms.set(self.due_time_in_ms.get().wrapping_add(shift));
303 }
304
305 fn fire_tick(&self) {
306 let operation = self.operation.borrow_mut().take();
308 drop(operation);
309
310 for (_, handler) in self.tick.snapshot().iter() {
313 handler(self);
314 }
315
316 if self.is_enabled.get() {
318 self.restart();
319 }
320 }
321}