Skip to main content

ferroui_base/threading/
i_dispatcher_impl.rs

1use std::rc::Rc;
2use std::sync::Arc;
3
4use super::CancellationToken;
5use crate::utilities::HandlerList;
6
7/// A multicast callback list used for the events exposed by dispatcher
8/// platform implementations. Like the implementations themselves it belongs
9/// to the loop thread.
10///
11/// Raising the event does not allocate.
12pub 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    /// Subscribes `handler` and returns the token that removes it.
28    pub fn add(&self, handler: Rc<dyn Fn(A)>) -> u64 {
29        self.handlers.add(handler)
30    }
31
32    /// Removes the handler registered under `token`.
33    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    /// Invokes every subscribed handler with `arg`.
44    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    /// Invokes every subscribed handler.
54    pub fn raise(&self) {
55        self.invoke(());
56    }
57}
58
59/// The one part of a dispatcher platform implementation that is used from
60/// other threads: waking the loop thread up.
61pub trait IDispatcherSignal: Send + Sync {
62    /// Asynchronously triggers the [`IDispatcherImpl::signaled`] callback on
63    /// the loop thread. Must not call back into the dispatcher.
64    fn signal(&self);
65}
66
67/// The one part of an
68/// [`IPlatformThreadingInterface`](crate::platform::IPlatformThreadingInterface)
69/// that is used from other threads: waking the loop thread up.
70pub trait IPlatformThreadingSignal: Send + Sync {
71    /// Does what `IPlatformThreadingInterface::signal` does, from any
72    /// thread.
73    fn signal(&self, priority: super::DispatcherPriority);
74}
75
76/// The platform side of a [`Dispatcher`](super::Dispatcher).
77///
78/// An implementation belongs to its loop thread, like the rest of the object
79/// model: it is held as `Rc<dyn IDispatcherImpl>`, every member is called on
80/// the loop thread only, and it is free to hold handles that cannot leave the
81/// thread. The single cross-thread entry point is the wake-up, which is
82/// factored out into [`IDispatcherSignal`]: the dispatcher asks for it once
83/// with [`signal_handle`](Self::signal_handle) and uses that handle when work
84/// is queued from another thread.
85///
86/// Implementations must not call back into the dispatcher synchronously from
87/// `signal`, `update_timer`, `now` or
88/// [`request_background_processing`](IDispatcherImplWithExplicitBackgroundProcessing::request_background_processing):
89/// the dispatcher calls them while holding its instance lock.
90pub trait IDispatcherImpl {
91    fn current_thread_is_loop_thread(&self) -> bool;
92
93    /// Asynchronously triggers the [`signaled`](Self::signaled) callback.
94    /// Called on the loop thread; other threads go through
95    /// [`signal_handle`](Self::signal_handle).
96    fn signal(&self);
97
98    /// The thread-safe handle that does what [`signal`](Self::signal) does.
99    fn signal_handle(&self) -> Arc<dyn IDispatcherSignal>;
100
101    /// Raised on the loop thread in response to a signal.
102    fn signaled(&self) -> &DispatcherImplEvent;
103
104    /// Raised on the loop thread when the timer set by
105    /// [`update_timer`](Self::update_timer) is due.
106    fn timer(&self) -> &DispatcherImplEvent;
107
108    /// Monotonic time in milliseconds.
109    fn now(&self) -> i64;
110
111    /// Sets (or clears) the time at which [`timer`](Self::timer) is raised.
112    fn update_timer(&self, due_time_in_ms: Option<i64>);
113
114    /// Returns `self` when the implementation can report pending input.
115    fn as_pending_input(&self) -> Option<&dyn IDispatcherImplWithPendingInput> {
116        None
117    }
118
119    /// Returns `self` when the implementation supports explicit background
120    /// processing requests.
121    fn as_explicit_background_processing(&self) -> Option<&dyn IDispatcherImplWithExplicitBackgroundProcessing> {
122        None
123    }
124
125    /// Returns `self` when the implementation can run the event loop.
126    fn as_controlled(&self) -> Option<&dyn IControlledDispatcherImpl> {
127        None
128    }
129}
130
131pub trait IDispatcherImplWithPendingInput: IDispatcherImpl {
132    /// Checks if the dispatcher implementation can query pending input.
133    fn can_query_pending_input(&self) -> bool;
134
135    /// Checks if there is pending user input.
136    fn has_pending_input(&self) -> bool;
137}
138
139pub trait IDispatcherImplWithExplicitBackgroundProcessing: IDispatcherImpl {
140    /// Raised on the loop thread once input has been handled after a call to
141    /// [`request_background_processing`](Self::request_background_processing).
142    fn ready_for_background_processing(&self) -> &DispatcherImplEvent;
143
144    fn request_background_processing(&self);
145}
146
147pub trait IControlledDispatcherImpl: IDispatcherImplWithPendingInput {
148    /// Runs the event loop until `token` is canceled.
149    fn run_loop(&self, token: CancellationToken);
150}
151
152/// A signal that wakes nobody up.
153pub(crate) struct DetachedDispatcherSignal;
154
155impl IDispatcherSignal for DetachedDispatcherSignal {
156    fn signal(&self) {}
157}
158
159/// Stand-in used while a dispatcher has no usable platform implementation:
160/// after shutdown in unit tests, and before the platform is initialized on
161/// targets where the managed implementation is not available.
162pub(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    /// Adapts an [`IPlatformThreadingInterface`] to [`IDispatcherImpl`].
224    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                        // OnTick
295                        dispose_timer(&slot);
296                        timer.raise();
297                    }),
298                );
299                *self.timer_handle.borrow_mut() = Some(handle);
300            }
301        }
302    }
303}