ferroui_base/threading/
i_dispatcher_impl.rs1use std::rc::Rc;
2use std::sync::Arc;
3
4use super::CancellationToken;
5use crate::utilities::HandlerList;
6
7pub struct DispatcherImplEvent<A = ()> {
13 handlers: HandlerList<dyn Fn(A)>,
14}
15
16impl<A> Default for DispatcherImplEvent<A> {
17 fn default() -> Self {
18 Self::new()
19 }
20}
21
22impl<A> DispatcherImplEvent<A> {
23 pub fn new() -> Self {
24 Self { handlers: HandlerList::new() }
25 }
26
27 pub fn add(&self, handler: Rc<dyn Fn(A)>) -> u64 {
29 self.handlers.add(handler)
30 }
31
32 pub fn remove(&self, token: u64) -> bool {
34 self.handlers.remove(token)
35 }
36
37 pub fn is_empty(&self) -> bool {
38 self.handlers.is_empty()
39 }
40}
41
42impl<A: Clone> DispatcherImplEvent<A> {
43 pub fn invoke(&self, arg: A) {
45 let snapshot = self.handlers.snapshot();
46 for (_, handler) in snapshot.iter() {
47 handler(arg.clone());
48 }
49 }
50}
51
52impl DispatcherImplEvent<()> {
53 pub fn raise(&self) {
55 self.invoke(());
56 }
57}
58
59pub trait IDispatcherSignal: Send + Sync {
62 fn signal(&self);
65}
66
67pub trait IPlatformThreadingSignal: Send + Sync {
71 fn signal(&self, priority: super::DispatcherPriority);
74}
75
76pub trait IDispatcherImpl {
91 fn current_thread_is_loop_thread(&self) -> bool;
92
93 fn signal(&self);
97
98 fn signal_handle(&self) -> Arc<dyn IDispatcherSignal>;
100
101 fn signaled(&self) -> &DispatcherImplEvent;
103
104 fn timer(&self) -> &DispatcherImplEvent;
107
108 fn now(&self) -> i64;
110
111 fn update_timer(&self, due_time_in_ms: Option<i64>);
113
114 fn as_pending_input(&self) -> Option<&dyn IDispatcherImplWithPendingInput> {
116 None
117 }
118
119 fn as_explicit_background_processing(&self) -> Option<&dyn IDispatcherImplWithExplicitBackgroundProcessing> {
122 None
123 }
124
125 fn as_controlled(&self) -> Option<&dyn IControlledDispatcherImpl> {
127 None
128 }
129}
130
131pub trait IDispatcherImplWithPendingInput: IDispatcherImpl {
132 fn can_query_pending_input(&self) -> bool;
134
135 fn has_pending_input(&self) -> bool;
137}
138
139pub trait IDispatcherImplWithExplicitBackgroundProcessing: IDispatcherImpl {
140 fn ready_for_background_processing(&self) -> &DispatcherImplEvent;
143
144 fn request_background_processing(&self);
145}
146
147pub trait IControlledDispatcherImpl: IDispatcherImplWithPendingInput {
148 fn run_loop(&self, token: CancellationToken);
150}
151
152pub(crate) struct DetachedDispatcherSignal;
154
155impl IDispatcherSignal for DetachedDispatcherSignal {
156 fn signal(&self) {}
157}
158
159pub(crate) struct DetachedDispatcherImpl {
163 signaled: DispatcherImplEvent,
164 timer: DispatcherImplEvent,
165}
166
167impl DetachedDispatcherImpl {
168 pub(crate) fn new() -> Self {
169 Self { signaled: DispatcherImplEvent::new(), timer: DispatcherImplEvent::new() }
170 }
171}
172
173impl IDispatcherImpl for DetachedDispatcherImpl {
174 fn current_thread_is_loop_thread(&self) -> bool {
175 true
176 }
177
178 fn signal(&self) {}
179
180 fn signal_handle(&self) -> Arc<dyn IDispatcherSignal> {
181 Arc::new(DetachedDispatcherSignal)
182 }
183
184 fn signaled(&self) -> &DispatcherImplEvent {
185 &self.signaled
186 }
187
188 fn timer(&self) -> &DispatcherImplEvent {
189 &self.timer
190 }
191
192 fn now(&self) -> i64 {
193 0
194 }
195
196 fn update_timer(&self, _due_time_in_ms: Option<i64>) {}
197}
198
199#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
200pub(crate) use legacy::LegacyDispatcherImpl;
201
202#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
203mod legacy {
204 use std::cell::RefCell;
205 use std::rc::Rc;
206 use std::sync::Arc;
207 use std::time::{Duration, Instant};
208
209 use super::{DispatcherImplEvent, IDispatcherImpl, IDispatcherSignal};
210 use super::IPlatformThreadingSignal;
211 use crate::platform::IPlatformThreadingInterface;
212 use crate::reactive::IDisposable;
213 use crate::threading::DispatcherPriority;
214
215 struct LegacySignal(Arc<dyn IPlatformThreadingSignal>);
216
217 impl IDispatcherSignal for LegacySignal {
218 fn signal(&self) {
219 self.0.signal(DispatcherPriority::SEND);
220 }
221 }
222
223 pub(crate) struct LegacyDispatcherImpl {
225 platform_threading: Rc<dyn IPlatformThreadingInterface>,
226 timer_handle: Rc<RefCell<Option<Rc<dyn IDisposable>>>>,
227 clock: Instant,
228 signaled: Rc<DispatcherImplEvent>,
229 timer: Rc<DispatcherImplEvent>,
230 }
231
232 impl LegacyDispatcherImpl {
233 pub(crate) fn new(platform_threading: Rc<dyn IPlatformThreadingInterface>) -> Self {
234 let signaled = Rc::new(DispatcherImplEvent::new());
235 let forward = signaled.clone();
236 platform_threading.signaled().add(Rc::new(move |_| forward.raise()));
237 Self {
238 platform_threading,
239 timer_handle: Rc::new(RefCell::new(None)),
240 clock: Instant::now(),
241 signaled,
242 timer: Rc::new(DispatcherImplEvent::new()),
243 }
244 }
245
246 fn elapsed_milliseconds(&self) -> i64 {
247 self.clock.elapsed().as_millis() as i64
248 }
249 }
250
251 fn dispose_timer(slot: &RefCell<Option<Rc<dyn IDisposable>>>) {
252 let handle = slot.borrow_mut().take();
253 if let Some(handle) = handle {
254 handle.dispose();
255 }
256 }
257
258 impl IDispatcherImpl for LegacyDispatcherImpl {
259 fn current_thread_is_loop_thread(&self) -> bool {
260 self.platform_threading.current_thread_is_loop_thread()
261 }
262
263 fn signal(&self) {
264 self.platform_threading.signal(DispatcherPriority::SEND);
265 }
266
267 fn signal_handle(&self) -> Arc<dyn IDispatcherSignal> {
268 Arc::new(LegacySignal(self.platform_threading.signal_handle()))
269 }
270
271 fn signaled(&self) -> &DispatcherImplEvent {
272 &self.signaled
273 }
274
275 fn timer(&self) -> &DispatcherImplEvent {
276 &self.timer
277 }
278
279 fn now(&self) -> i64 {
280 self.elapsed_milliseconds()
281 }
282
283 fn update_timer(&self, due_time_in_ms: Option<i64>) {
284 dispose_timer(&self.timer_handle);
285
286 if let Some(due_time) = due_time_in_ms {
287 let interval = (due_time - self.elapsed_milliseconds()).max(1);
288 let slot = self.timer_handle.clone();
289 let timer = self.timer.clone();
290 let handle = self.platform_threading.start_timer(
291 DispatcherPriority::SEND,
292 Duration::from_millis(interval as u64),
293 Rc::new(move || {
294 dispose_timer(&slot);
296 timer.raise();
297 }),
298 );
299 *self.timer_handle.borrow_mut() = Some(handle);
300 }
301 }
302 }
303}