ferroui_native/
dispatcher_impl.rs1use crate::interop::*;
4use ferroui_base::threading::{
5 CancellationToken, CancellationTokenSource, DispatcherImplEvent, IControlledDispatcherImpl, IDispatcherImpl,
6 IDispatcherImplWithExplicitBackgroundProcessing, IDispatcherImplWithPendingInput, IDispatcherSignal,
7};
8use ferroui_microcom::ComPtr;
9use std::any::Any;
10use std::cell::{Cell, RefCell};
11use std::panic::resume_unwind;
12use std::rc::{Rc, Weak};
13use std::sync::{Arc, Mutex, PoisonError};
14use std::thread::{self, ThreadId};
15use std::time::Instant;
16
17ferroui_base::ferro_thread_local! {
18 static CURRENT: RefCell<Weak<DispatcherImpl>> = const { RefCell::new(Weak::new()) };
21}
22
23struct NativeSignal(ComPtr<IFrnPlatformThreadingInterface>);
25
26unsafe impl Send for NativeSignal {}
31unsafe impl Sync for NativeSignal {}
33
34impl IDispatcherSignal for NativeSignal {
35 fn signal(&self) {
36 self.0.signal();
37 }
38}
39
40struct LoopCancellation(Mutex<LoopCancellationState>);
43
44struct LoopCancellationState {
45 exited: bool,
46 cancel: Option<ComPtr<IFrnLoopCancellation>>,
47}
48
49unsafe impl Send for LoopCancellation {}
53unsafe impl Sync for LoopCancellation {}
55
56impl LoopCancellation {
57 fn cancel(&self) {
58 let state = self.0.lock().unwrap_or_else(PoisonError::into_inner);
59 if !state.exited {
60 if let Some(cancel) = &state.cancel {
61 cancel.cancel();
62 }
63 }
64 }
65
66 fn exit(&self) -> Option<ComPtr<IFrnLoopCancellation>> {
69 let mut state = self.0.lock().unwrap_or_else(PoisonError::into_inner);
70 state.exited = true;
71 state.cancel.take()
72 }
73}
74
75struct RunLoopFrame {
77 exception: RefCell<Option<Box<dyn Any + Send>>>,
78 cancellation_token_source: CancellationTokenSource,
79}
80
81pub struct DispatcherImpl {
83 native: ComPtr<IFrnPlatformThreadingInterface>,
84 signal: Arc<NativeSignal>,
85 loop_thread: Cell<Option<ThreadId>>,
86 clock: Instant,
87 managed_frames: RefCell<Vec<Rc<RunLoopFrame>>>,
88 signaled: DispatcherImplEvent,
89 timer: DispatcherImplEvent,
90 ready_for_background_processing: DispatcherImplEvent,
91}
92
93struct Events {
95 parent: Weak<DispatcherImpl>,
96}
97
98impl IFrnPlatformThreadingInterfaceEventsImpl for Events {
99 fn signaled(&self) {
100 crate::callback_base::guard((), || {
101 if let Some(parent) = self.parent.upgrade() {
102 parent.signaled.raise();
103 }
104 })
105 }
106
107 fn timer(&self) {
108 crate::callback_base::guard((), || {
109 if let Some(parent) = self.parent.upgrade() {
110 parent.timer.raise();
111 }
112 })
113 }
114
115 fn ready_for_background_processing(&self) {
116 crate::callback_base::guard((), || {
117 if let Some(parent) = self.parent.upgrade() {
118 parent.ready_for_background_processing.raise();
119 }
120 })
121 }
122}
123
124impl DispatcherImpl {
125 pub fn new(native: ComPtr<IFrnPlatformThreadingInterface>) -> Rc<DispatcherImpl> {
128 let this = Rc::new(DispatcherImpl {
129 signal: Arc::new(NativeSignal(native.clone())),
130 native,
131 loop_thread: Cell::new(None),
132 clock: Instant::now(),
133 managed_frames: RefCell::new(Vec::new()),
134 signaled: DispatcherImplEvent::new(),
135 timer: DispatcherImplEvent::new(),
136 ready_for_background_processing: DispatcherImplEvent::new(),
137 });
138 let events = IFrnPlatformThreadingInterfaceEvents::from_impl(Events { parent: Rc::downgrade(&this) });
139 this.native.set_events(Some(&events));
140 CURRENT.with(|current| *current.borrow_mut() = Rc::downgrade(&this));
141 this
142 }
143
144 pub(crate) fn current() -> Option<Rc<DispatcherImpl>> {
146 CURRENT.try_with(|current| current.borrow().upgrade()).ok().flatten()
147 }
148
149 fn timer_interval(due_time_in_ms: Option<i64>, now: i64) -> i32 {
153 match due_time_in_ms {
154 None => -1,
155 Some(due_time) => (due_time - now).max(1).min((i32::MAX - 10) as i64) as i32,
156 }
157 }
158
159 pub fn propagate_callback_exception(&self, capture: Box<dyn Any + Send>) {
162 let frame = self.managed_frames.borrow().last().cloned();
163 let Some(frame) = frame else {
164 debug_assert!(false, "We should never get here");
165 return;
166 };
167
168 *frame.exception.borrow_mut() = Some(capture);
169 frame.cancellation_token_source.cancel();
170 }
171}
172
173impl IDispatcherImpl for DispatcherImpl {
174 fn current_thread_is_loop_thread(&self) -> bool {
175 if let Some(loop_thread) = self.loop_thread.get() {
176 return thread::current().id() == loop_thread;
177 }
178 if !self.native.get_current_thread_is_loop_thread() {
179 return false;
180 }
181 self.loop_thread.set(Some(thread::current().id()));
182 true
183 }
184
185 fn signal(&self) {
186 self.native.signal();
187 }
188
189 fn signal_handle(&self) -> Arc<dyn IDispatcherSignal> {
190 self.signal.clone()
191 }
192
193 fn signaled(&self) -> &DispatcherImplEvent {
194 &self.signaled
195 }
196
197 fn timer(&self) -> &DispatcherImplEvent {
198 &self.timer
199 }
200
201 fn now(&self) -> i64 {
202 self.clock.elapsed().as_millis() as i64
203 }
204
205 fn update_timer(&self, due_time_in_ms: Option<i64>) {
206 self.native.update_timer(Self::timer_interval(due_time_in_ms, self.now()));
207 }
208
209 fn as_pending_input(&self) -> Option<&dyn IDispatcherImplWithPendingInput> {
210 Some(self)
211 }
212
213 fn as_explicit_background_processing(&self) -> Option<&dyn IDispatcherImplWithExplicitBackgroundProcessing> {
214 Some(self)
215 }
216
217 fn as_controlled(&self) -> Option<&dyn IControlledDispatcherImpl> {
218 Some(self)
219 }
220}
221
222impl IDispatcherImplWithPendingInput for DispatcherImpl {
223 fn can_query_pending_input(&self) -> bool {
224 false
225 }
226
227 fn has_pending_input(&self) -> bool {
228 false
229 }
230}
231
232impl IDispatcherImplWithExplicitBackgroundProcessing for DispatcherImpl {
233 fn ready_for_background_processing(&self) -> &DispatcherImplEvent {
234 &self.ready_for_background_processing
235 }
236
237 fn request_background_processing(&self) {
238 self.native.request_background_processing();
239 }
240}
241
242impl IControlledDispatcherImpl for DispatcherImpl {
243 fn run_loop(&self, token: CancellationToken) {
244 if token.is_cancellation_requested() {
245 return;
246 }
247
248 let frame = Rc::new(RunLoopFrame {
251 exception: RefCell::new(None),
252 cancellation_token_source: CancellationTokenSource::new(),
253 });
254 let linked = frame.cancellation_token_source.clone();
255 let link_registration = token.register(move || linked.cancel());
256
257 let cancel = self.native.create_loop_cancellation();
258 let shared = Arc::new(LoopCancellation(Mutex::new(LoopCancellationState { exited: false, cancel: cancel.clone() })));
259 let on_cancel = shared.clone();
260 let cancel_registration = frame.cancellation_token_source.token().register(move || on_cancel.cancel());
261
262 self.managed_frames.borrow_mut().push(frame.clone());
263 self.native.run_loop(cancel.as_deref());
264
265 drop(shared.exit());
267 self.managed_frames.borrow_mut().pop();
268 cancel_registration.dispose();
269 link_registration.dispose();
270 let exception = frame.exception.borrow_mut().take();
271 if let Some(exception) = exception {
272 resume_unwind(exception);
273 }
274 }
275}
276
277#[cfg(test)]
278mod tests {
279 use super::*;
280
281 #[test]
282 fn timer_interval_matches_the_reference_clamping() {
283 assert_eq!(DispatcherImpl::timer_interval(None, 100), -1);
284 assert_eq!(DispatcherImpl::timer_interval(Some(150), 100), 50);
285 assert_eq!(DispatcherImpl::timer_interval(Some(100), 100), 1);
287 assert_eq!(DispatcherImpl::timer_interval(Some(10), 100), 1);
288 assert_eq!(DispatcherImpl::timer_interval(Some(i64::MAX), 0), i32::MAX - 10);
289 }
290}