Skip to main content

ferroui_base/platform/
managed_dispatcher_impl.rs

1// The managed implementation needs a monotonic clock and a blocking wait,
2// neither of which exists on bare WebAssembly.
3#![cfg(not(target_family = "wasm"))]
4
5use std::rc::Rc;
6use std::sync::{Arc, Condvar, Mutex, MutexGuard, PoisonError};
7use std::thread::{self, ThreadId};
8use std::time::{Duration, Instant};
9
10use crate::threading::{
11    CancellationToken, DispatcherImplEvent, IControlledDispatcherImpl, IDispatcherImpl,
12    IDispatcherImplWithExplicitBackgroundProcessing, IDispatcherImplWithPendingInput, IDispatcherSignal,
13};
14
15/// Supplies input events to a [`ManagedDispatcherImpl`] run loop.
16pub trait IManagedDispatcherInputProvider {
17    fn has_input(&self) -> bool;
18    fn dispatch_next_input_event(&self);
19}
20
21#[derive(Default)]
22struct State {
23    signaled: bool,
24    next_timer: Option<Duration>,
25    background_processing_requested: bool,
26    /// The auto-reset wake-up event.
27    wakeup: bool,
28}
29
30struct Shared {
31    state: Mutex<State>,
32    wakeup: Condvar,
33}
34
35impl Shared {
36    fn lock(&self) -> MutexGuard<'_, State> {
37        self.state.lock().unwrap_or_else(PoisonError::into_inner)
38    }
39
40    fn set_wakeup(&self, state: &mut State) {
41        state.wakeup = true;
42        self.wakeup.notify_all();
43    }
44}
45
46impl IDispatcherSignal for Shared {
47    fn signal(&self) {
48        let mut state = self.lock();
49        state.signaled = true;
50        self.set_wakeup(&mut state);
51    }
52}
53
54/// A dispatcher implementation with a run loop built on standard library
55/// primitives. It is what a dispatcher uses until the platform installs its
56/// own implementation, and what headless platforms can use directly.
57///
58/// The wake-up state is shared with other threads; everything else belongs
59/// to the loop thread.
60pub struct ManagedDispatcherImpl {
61    input_provider: Option<Rc<dyn IManagedDispatcherInputProvider>>,
62    shared: Arc<Shared>,
63    clock: Instant,
64    loop_thread: ThreadId,
65    signaled: DispatcherImplEvent,
66    timer: DispatcherImplEvent,
67    ready_for_background_processing: DispatcherImplEvent,
68}
69
70impl ManagedDispatcherImpl {
71    pub fn new(input_provider: Option<Rc<dyn IManagedDispatcherInputProvider>>) -> Self {
72        Self {
73            input_provider,
74            shared: Arc::new(Shared { state: Mutex::new(State::default()), wakeup: Condvar::new() }),
75            clock: Instant::now(),
76            loop_thread: thread::current().id(),
77            signaled: DispatcherImplEvent::new(),
78            timer: DispatcherImplEvent::new(),
79            ready_for_background_processing: DispatcherImplEvent::new(),
80        }
81    }
82}
83
84impl IDispatcherImpl for ManagedDispatcherImpl {
85    fn current_thread_is_loop_thread(&self) -> bool {
86        self.loop_thread == thread::current().id()
87    }
88
89    fn signal(&self) {
90        IDispatcherSignal::signal(&*self.shared);
91    }
92
93    fn signal_handle(&self) -> Arc<dyn IDispatcherSignal> {
94        self.shared.clone()
95    }
96
97    fn signaled(&self) -> &DispatcherImplEvent {
98        &self.signaled
99    }
100
101    fn timer(&self) -> &DispatcherImplEvent {
102        &self.timer
103    }
104
105    fn now(&self) -> i64 {
106        self.clock.elapsed().as_millis() as i64
107    }
108
109    fn update_timer(&self, due_time_in_ms: Option<i64>) {
110        let mut state = self.shared.lock();
111        state.next_timer = due_time_in_ms.map(|due| Duration::from_millis(due.max(0) as u64));
112        if !self.current_thread_is_loop_thread() {
113            self.shared.set_wakeup(&mut state);
114        }
115    }
116
117    fn as_pending_input(&self) -> Option<&dyn IDispatcherImplWithPendingInput> {
118        Some(self)
119    }
120
121    fn as_explicit_background_processing(&self) -> Option<&dyn IDispatcherImplWithExplicitBackgroundProcessing> {
122        Some(self)
123    }
124
125    fn as_controlled(&self) -> Option<&dyn IControlledDispatcherImpl> {
126        Some(self)
127    }
128}
129
130impl IDispatcherImplWithPendingInput for ManagedDispatcherImpl {
131    fn can_query_pending_input(&self) -> bool {
132        self.input_provider.is_some()
133    }
134
135    fn has_pending_input(&self) -> bool {
136        self.input_provider.as_ref().is_some_and(|provider| provider.has_input())
137    }
138}
139
140impl IDispatcherImplWithExplicitBackgroundProcessing for ManagedDispatcherImpl {
141    fn ready_for_background_processing(&self) -> &DispatcherImplEvent {
142        &self.ready_for_background_processing
143    }
144
145    fn request_background_processing(&self) {
146        let mut state = self.shared.lock();
147        state.background_processing_requested = true;
148        self.shared.set_wakeup(&mut state);
149    }
150}
151
152impl IControlledDispatcherImpl for ManagedDispatcherImpl {
153    fn run_loop(&self, token: CancellationToken) {
154        let registration = token.can_be_canceled().then(|| {
155            let shared = self.shared.clone();
156            token.register(move || {
157                let mut state = shared.lock();
158                shared.set_wakeup(&mut state);
159            })
160        });
161
162        while !token.is_cancellation_requested() {
163            let signaled = std::mem::take(&mut self.shared.lock().signaled);
164
165            if signaled {
166                self.signaled.raise();
167                continue;
168            }
169
170            let fire_timer = {
171                let mut state = self.shared.lock();
172                if state.next_timer.is_some_and(|next| next < self.clock.elapsed()) {
173                    state.next_timer = None;
174                    true
175                } else {
176                    false
177                }
178            };
179
180            if fire_timer {
181                self.timer.raise();
182                continue;
183            }
184
185            if let Some(provider) = &self.input_provider {
186                if provider.has_input() {
187                    provider.dispatch_next_input_event();
188                    continue;
189                }
190            }
191
192            let trigger_background_processing =
193                std::mem::take(&mut self.shared.lock().background_processing_requested);
194
195            if trigger_background_processing {
196                self.ready_for_background_processing.raise();
197                continue;
198            }
199
200            let mut state = self.shared.lock();
201            match state.next_timer {
202                Some(next_timer) => {
203                    let wait_for = next_timer.saturating_sub(self.clock.elapsed());
204                    if wait_for.as_millis() < 1 {
205                        continue;
206                    }
207                    if !state.wakeup {
208                        state = self
209                            .shared
210                            .wakeup
211                            .wait_timeout(state, wait_for)
212                            .unwrap_or_else(PoisonError::into_inner)
213                            .0;
214                    }
215                }
216                None => {
217                    while !state.wakeup {
218                        state = self.shared.wakeup.wait(state).unwrap_or_else(PoisonError::into_inner);
219                    }
220                }
221            }
222            state.wakeup = false;
223        }
224
225        if let Some(registration) = registration {
226            registration.dispose();
227        }
228    }
229}