ferroui_base/platform/
managed_dispatcher_impl.rs1#![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
15pub 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 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
54pub 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}