1use std::{collections::VecDeque, sync::Arc};
8
9use parking_lot::Mutex;
10
11type UiWork = Box<dyn FnOnce() + Send + 'static>;
12type Wake = Arc<dyn Fn() + Send + Sync>;
13
14struct QueueState {
15 open: bool,
16 pending: VecDeque<UiWork>,
17 wake: Option<Wake>,
18}
19
20struct DispatchQueue(Mutex<QueueState>);
21
22impl Default for DispatchQueue {
23 fn default() -> Self {
24 Self(Mutex::new(QueueState {
25 open: crate::RuntimeSession::current().is_some_and(|session| session.accepts_work()),
26 pending: VecDeque::new(),
27 wake: None,
28 }))
29 }
30}
31
32#[derive(Clone)]
34pub struct UiDispatcher {
35 owner: crate::RuntimeSessionId,
36 queue: Arc<DispatchQueue>,
37 liveness: Option<Arc<dyn Fn() -> bool + Send + Sync>>,
38}
39
40impl UiDispatcher {
41 pub fn dispatch(&self, work: impl FnOnce() + Send + 'static) -> bool {
45 if !self.is_alive() {
46 return false;
47 }
48 let liveness = self.liveness.clone();
49 let work = move || {
50 if liveness.as_ref().is_none_or(|alive| alive()) {
51 work();
52 }
53 };
54 let wake = {
55 let mut state = self.queue.0.lock();
56 if !state.open {
57 return false;
58 }
59 state.pending.push_back(Box::new(work));
60 state.wake.clone()
61 };
62 if let Some(wake) = wake {
63 wake();
64 }
65 true
66 }
67
68 pub fn is_alive(&self) -> bool {
70 let open = self.queue.0.lock().open;
71 open && self.liveness.as_ref().is_none_or(|alive| alive())
72 }
73
74 pub(crate) fn scoped(mut self, alive: impl Fn() -> bool + Send + Sync + 'static) -> Self {
75 self.liveness = Some(Arc::new(alive));
76 self
77 }
78
79 pub fn session_id(&self) -> crate::RuntimeSessionId {
81 self.owner
82 }
83}
84
85pub(crate) fn current() -> UiDispatcher {
86 let session =
87 crate::RuntimeSession::current().expect("a UI dispatcher requires an active session");
88 UiDispatcher {
89 owner: session.identity(),
90 queue: crate::runtime_resource::<DispatchQueue>(),
91 liveness: None,
92 }
93}
94
95pub(crate) fn set_waker(wake: Option<Wake>) {
96 let queue = crate::runtime_resource::<DispatchQueue>();
97 let (previous, pending) = {
98 let mut state = queue.0.lock();
99 let previous = std::mem::replace(&mut state.wake, wake.clone());
100 (previous, !state.pending.is_empty())
101 };
102 drop(previous);
103 if pending && let Some(wake) = wake {
104 wake();
105 }
106}
107
108pub(crate) fn has_pending() -> bool {
109 crate::RuntimeSession::current().is_some()
110 && !crate::runtime_resource::<DispatchQueue>()
111 .0
112 .lock()
113 .pending
114 .is_empty()
115}
116
117pub(crate) fn flush() {
118 let queue = crate::runtime_resource::<DispatchQueue>();
119 let work = std::mem::take(&mut queue.0.lock().pending);
120 for work in work {
121 work();
122 }
123}
124
125pub(crate) fn close() {
126 let queue = crate::runtime_resource::<DispatchQueue>();
127 let removed = {
128 let mut state = queue.0.lock();
129 state.open = false;
130 (std::mem::take(&mut state.pending), state.wake.take())
131 };
132 drop(removed);
133}