ferroui_base/threading/
dispatcher_timers.rs1use std::rc::Rc;
2
3use super::dispatcher::DispatcherState;
4use super::{Dispatcher, DispatcherPriority, DispatcherTimer};
5
6impl Dispatcher {
7 pub fn now(&self) -> i64 {
11 self.verify_access();
12 self.local_impl().map_or(0, |impl_| impl_.now())
13 }
14
15 pub(crate) fn update_os_timer(&self) {
16 self.update_os_timer_locked(&mut self.lock());
17 }
18
19 pub(crate) fn update_os_timer_locked(&self, st: &mut DispatcherState) {
20 self.verify_access();
21 let next_due_time = match (st.due_time_for_timers, st.due_time_for_background_processing) {
22 (Some(timers), Some(background)) => Some(timers.min(background)),
23 (timers, background) => timers.or(background),
24 };
25 if st.os_timer_set_to == next_due_time {
26 return;
27 }
28
29 st.os_timer_set_to = next_due_time;
30 if let Some(impl_) = self.local_impl() {
31 impl_.update_timer(next_due_time);
32 }
33 }
34
35 pub(crate) fn reschedule_timers(&self) {
36 if !self.check_access() {
37 let dispatcher = self.to_arc();
38 self.post(move || dispatcher.reschedule_timers(), DispatcherPriority::SEND);
39 return;
40 }
41
42 let local = self.try_local();
43 let mut st = self.lock();
44 if !st.has_shutdown_finished {
45 let old_due_time_found = st.due_time_found;
46 let old_due_time_in_ticks = st.due_time_in_ms;
47 st.due_time_found = false;
48 st.due_time_in_ms = 0;
49
50 if let Some(local) = &local {
51 for timer in local.timers.borrow().iter() {
53 if !st.due_time_found || timer.due_time_in_ms().wrapping_sub(st.due_time_in_ms) < 0 {
54 st.due_time_found = true;
55 st.due_time_in_ms = timer.due_time_in_ms();
56 }
57 }
58 }
59
60 if st.due_time_found {
61 if st.due_time_for_timers.is_none()
62 || !old_due_time_found
63 || old_due_time_in_ticks != st.due_time_in_ms
64 {
65 st.due_time_for_timers = Some(st.due_time_in_ms);
66 self.update_os_timer_locked(&mut st);
67 }
68 } else if old_due_time_found {
69 st.due_time_for_timers = None;
70 self.update_os_timer_locked(&mut st);
71 }
72 }
73 }
74
75 pub(crate) fn add_timer(&self, timer: &Rc<DispatcherTimer>) {
76 if let Some(local) = self.try_local() {
77 if !self.lock().has_shutdown_finished {
78 local.timers.borrow_mut().push(timer.clone());
79 local.timers_version.set(local.timers_version.get() + 1);
80 }
81 }
82
83 self.reschedule_timers();
84 }
85
86 pub(crate) fn remove_timer(&self, timer: &DispatcherTimer) {
87 if let Some(local) = self.try_local() {
88 if !self.lock().has_shutdown_finished {
89 let removed = {
90 let mut timers = local.timers.borrow_mut();
91 timers.iter().position(|t| std::ptr::eq(Rc::as_ptr(t), timer)).map(|index| timers.remove(index))
92 };
93 local.timers_version.set(local.timers_version.get() + 1);
94 drop(removed);
95 }
96 }
97
98 self.reschedule_timers();
99 }
100
101 pub(crate) fn on_os_timer(&self) {
102 let need_to_promote_timers;
103 let need_to_process_queue;
104 {
105 let Some(impl_) = self.local_impl() else {
106 return;
107 };
108 let mut st = self.lock();
109 impl_.update_timer(None);
110 st.os_timer_set_to = None;
111 let now = impl_.now();
112 need_to_promote_timers = st.due_time_for_timers.is_some_and(|due| due <= now);
113 if need_to_promote_timers {
114 st.due_time_for_timers = None;
115 }
116 need_to_process_queue = st.due_time_for_background_processing.is_some_and(|due| due <= now);
117 if need_to_process_queue {
118 st.due_time_for_background_processing = None;
119 }
120 }
121
122 if need_to_promote_timers {
123 self.promote_timers();
124 }
125 if need_to_process_queue {
126 self.execute_jobs_core(false);
127 }
128 self.update_os_timer();
129 }
130
131 pub(crate) fn promote_timers(&self) {
132 let Some(local) = self.try_local() else {
133 return;
134 };
135 if local.timers.borrow().is_empty() {
136 let st = self.lock();
138 if !st.due_time_found {
139 return;
140 }
141 }
142
143 let current_time_in_ticks = self.now();
144
145 let (has_due_timers, mut timers_version) = {
146 let st = self.lock();
147 let due = !st.has_shutdown_finished
148 && st.due_time_found
149 && st.due_time_in_ms.wrapping_sub(current_time_in_ticks) <= 0;
150 (due, local.timers_version.get())
151 };
152
153 if has_due_timers {
154 let mut i_timer = 0usize;
155
156 loop {
157 let timer = {
158 let mut timers = local.timers.borrow_mut();
159 let mut timer = None;
160
161 if timers_version != local.timers_version.get() {
164 timers_version = local.timers_version.get();
165 i_timer = 0;
166 }
167
168 while i_timer < timers.len() {
169 if timers[i_timer].due_time_in_ms().wrapping_sub(current_time_in_ticks) <= 0 {
171 timer = Some(timers.remove(i_timer));
174 break;
175 } else {
176 i_timer += 1;
177 }
178 }
179 timer
180 };
181
182 match timer {
184 Some(timer) => timer.promote(),
185 None => break,
186 }
187 }
188 }
189
190 self.reschedule_timers();
192 }
193
194 #[allow(dead_code)]
195 pub(crate) fn snapshot_timers_for_unit_tests() -> Vec<Rc<DispatcherTimer>> {
196 Dispatcher::ui_thread().try_local().map(|local| local.timers.borrow().clone()).unwrap_or_default()
197 }
198}
199
200impl Dispatcher {
203 #[doc(hidden)]
205 pub fn timers_for_unit_tests() -> Vec<Rc<DispatcherTimer>> {
206 Self::snapshot_timers_for_unit_tests()
207 }
208
209 #[doc(hidden)]
211 pub fn force_fire_timer_for_unit_tests(timer: &Rc<DispatcherTimer>) {
212 timer.promote();
213 timer.dispatcher().remove_timer(timer);
214 timer.dispatcher().run_jobs(None);
215 }
216}