1use crate::collections::map::HashMap;
2use crate::collections::map::HashSet;
3use crate::MutableStateInner;
4use std::any::Any;
5use std::cell::{Cell, Ref, RefCell};
6use std::collections::{HashMap as StdHashMap, VecDeque};
7use std::future::Future;
8use std::pin::Pin;
9use std::rc::{Rc, Weak};
10use std::sync::atomic::{AtomicU32, AtomicUsize, Ordering};
11use std::sync::{mpsc, Arc};
12use std::task::{Context, Poll, Waker};
13use std::thread::ThreadId;
14use std::thread_local;
15
16use crate::frame_clock::FrameClock;
17use crate::platform::RuntimeScheduler;
18use crate::{Applier, Command, FrameCallbackId, NodeError, RecomposeScopeInner, ScopeId};
19
20enum UiMessage {
21 Task(Box<dyn FnOnce() + Send + 'static>),
22 Invoke { id: u64, value: Box<dyn Any + Send> },
23}
24
25type UiContinuation = Box<dyn Fn(Box<dyn Any>) + 'static>;
26type UiContinuationMap = HashMap<u64, UiContinuation>;
27
28trait AnyStateCell {
29 fn as_any(&self) -> &dyn Any;
30}
31
32struct TypedStateCell<T: Clone + 'static> {
33 inner: MutableStateInner<T>,
34}
35
36impl<T: Clone + 'static> AnyStateCell for TypedStateCell<T> {
37 fn as_any(&self) -> &dyn Any {
38 &self.inner
39 }
40}
41
42#[allow(dead_code)]
43struct RawStateCell<T: 'static> {
44 value: T,
45}
46
47impl<T: 'static> AnyStateCell for RawStateCell<T> {
48 fn as_any(&self) -> &dyn Any {
49 &self.value
50 }
51}
52
53#[derive(Default)]
54pub(crate) struct StateArena {
55 cells: RefCell<Vec<Option<Box<dyn AnyStateCell>>>>,
56}
57
58impl StateArena {
59 pub(crate) fn alloc<T: Clone + 'static>(&self, value: T, runtime: RuntimeHandle) -> StateId {
60 let mut cells = self.cells.borrow_mut();
61 let id = StateId(cells.len() as u32);
62 let inner = MutableStateInner::new(value, runtime.clone());
63 inner.install_snapshot_observer(id);
64 let cell: Box<dyn AnyStateCell> = Box::new(TypedStateCell { inner });
65 cells.push(Some(cell));
66 id
67 }
68
69 #[allow(dead_code)]
70 pub(crate) fn alloc_raw<T: 'static>(&self, value: T) -> StateId {
71 let mut cells = self.cells.borrow_mut();
72 let id = StateId(cells.len() as u32);
73 let cell: Box<dyn AnyStateCell> = Box::new(RawStateCell { value });
74 cells.push(Some(cell));
75 id
76 }
77
78 fn get_cell(&self, id: StateId) -> Ref<'_, Box<dyn AnyStateCell>> {
79 Ref::map(self.cells.borrow(), |cells| {
80 cells
81 .get(id.0 as usize)
82 .and_then(|cell| cell.as_ref())
83 .expect("state cell missing")
84 })
85 }
86
87 pub(crate) fn get_typed<T: Clone + 'static>(
88 &self,
89 id: StateId,
90 ) -> Ref<'_, MutableStateInner<T>> {
91 Ref::map(self.get_cell(id), |cell| {
92 cell.as_any()
93 .downcast_ref::<MutableStateInner<T>>()
94 .expect("state cell type mismatch")
95 })
96 }
97
98 #[allow(dead_code)]
99 pub(crate) fn get_raw<T: 'static>(&self, id: StateId) -> Ref<'_, T> {
100 Ref::map(self.get_cell(id), |cell| {
101 cell.as_any()
102 .downcast_ref::<T>()
103 .expect("raw state cell type mismatch")
104 })
105 }
106
107 pub(crate) fn get_typed_opt<T: Clone + 'static>(
108 &self,
109 id: StateId,
110 ) -> Option<Ref<'_, MutableStateInner<T>>> {
111 Ref::filter_map(self.get_cell(id), |cell| {
112 cell.as_any().downcast_ref::<MutableStateInner<T>>()
113 })
114 .ok()
115 }
116}
117
118thread_local! {
119 #[allow(clippy::missing_const_for_thread_local)]
120 static RUNTIME_HANDLES: RefCell<StdHashMap<RuntimeId, RuntimeHandle>> =
121 RefCell::new(StdHashMap::new());
122}
123
124fn register_runtime_handle(handle: &RuntimeHandle) {
125 RUNTIME_HANDLES.with(|registry| {
126 registry.borrow_mut().insert(handle.id, handle.clone());
127 });
128 LAST_RUNTIME.with(|slot| *slot.borrow_mut() = Some(handle.clone()));
131}
132
133pub(crate) fn runtime_handle_for(id: RuntimeId) -> Option<RuntimeHandle> {
134 RUNTIME_HANDLES.with(|registry| registry.borrow().get(&id).cloned())
135}
136
137#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
138pub struct StateId(pub(crate) u32);
139
140#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
141pub struct RuntimeId(u32);
142
143impl RuntimeId {
144 fn next() -> Self {
145 static NEXT_RUNTIME_ID: AtomicU32 = AtomicU32::new(1);
146 Self(NEXT_RUNTIME_ID.fetch_add(1, Ordering::Relaxed))
147 }
148}
149
150struct UiDispatcherInner {
151 scheduler: Arc<dyn RuntimeScheduler>,
152 tx: mpsc::Sender<UiMessage>,
153 pending: AtomicUsize,
154}
155
156impl UiDispatcherInner {
157 fn new(scheduler: Arc<dyn RuntimeScheduler>, tx: mpsc::Sender<UiMessage>) -> Self {
158 Self {
159 scheduler,
160 tx,
161 pending: AtomicUsize::new(0),
162 }
163 }
164
165 fn post(&self, task: impl FnOnce() + Send + 'static) {
166 self.pending.fetch_add(1, Ordering::SeqCst);
167 let _ = self.tx.send(UiMessage::Task(Box::new(task)));
168 self.scheduler.schedule_frame();
169 }
170
171 fn post_invoke(&self, id: u64, value: Box<dyn Any + Send>) {
172 self.pending.fetch_add(1, Ordering::SeqCst);
173 let _ = self.tx.send(UiMessage::Invoke { id, value });
174 self.scheduler.schedule_frame();
175 }
176
177 fn has_pending(&self) -> bool {
178 self.pending.load(Ordering::SeqCst) > 0
179 }
180}
181
182struct PendingGuard<'a> {
183 counter: &'a AtomicUsize,
184}
185
186impl<'a> PendingGuard<'a> {
187 fn new(counter: &'a AtomicUsize) -> Self {
188 Self { counter }
189 }
190}
191
192impl<'a> Drop for PendingGuard<'a> {
193 fn drop(&mut self) {
194 let previous = self.counter.fetch_sub(1, Ordering::SeqCst);
195 debug_assert!(previous > 0, "UI dispatcher pending count underflowed");
196 }
197}
198
199#[derive(Clone)]
200pub struct UiDispatcher {
201 inner: Arc<UiDispatcherInner>,
202}
203
204impl UiDispatcher {
205 fn new(inner: Arc<UiDispatcherInner>) -> Self {
206 Self { inner }
207 }
208
209 pub fn post(&self, task: impl FnOnce() + Send + 'static) {
210 self.inner.post(task);
211 }
212
213 pub fn post_invoke<T>(&self, id: u64, value: T)
214 where
215 T: Send + 'static,
216 {
217 self.inner.post_invoke(id, Box::new(value));
218 }
219
220 pub fn has_pending(&self) -> bool {
221 self.inner.has_pending()
222 }
223}
224
225struct RuntimeInner {
226 scheduler: Arc<dyn RuntimeScheduler>,
227 needs_frame: RefCell<bool>,
228 node_updates: RefCell<Vec<Command>>, invalid_scopes: RefCell<HashSet<ScopeId>>, scope_queue: RefCell<Vec<(ScopeId, Weak<RecomposeScopeInner>)>>, frame_callbacks: RefCell<VecDeque<FrameCallbackEntry>>, next_frame_callback_id: Cell<u64>,
233 ui_dispatcher: Arc<UiDispatcherInner>,
234 ui_rx: RefCell<mpsc::Receiver<UiMessage>>,
235 local_tasks: RefCell<VecDeque<Box<dyn FnOnce() + 'static>>>,
236 ui_conts: RefCell<UiContinuationMap>,
237 next_cont_id: Cell<u64>,
238 ui_thread_id: ThreadId,
239 tasks: RefCell<Vec<TaskEntry>>, next_task_id: Cell<u64>,
241 task_waker: RefCell<Option<Waker>>,
242 state_arena: StateArena,
243 runtime_id: RuntimeId,
244}
245
246struct TaskEntry {
247 id: u64,
248 future: Pin<Box<dyn Future<Output = ()> + 'static>>,
249}
250
251impl RuntimeInner {
252 fn new(scheduler: Arc<dyn RuntimeScheduler>) -> Self {
253 let (tx, rx) = mpsc::channel();
254 let dispatcher = Arc::new(UiDispatcherInner::new(scheduler.clone(), tx));
255 Self {
256 scheduler,
257 needs_frame: RefCell::new(false),
258 node_updates: RefCell::new(Vec::new()),
259 invalid_scopes: RefCell::new(HashSet::default()),
260 scope_queue: RefCell::new(Vec::new()),
261 frame_callbacks: RefCell::new(VecDeque::new()),
262 next_frame_callback_id: Cell::new(1),
263 ui_dispatcher: dispatcher,
264 ui_rx: RefCell::new(rx),
265 local_tasks: RefCell::new(VecDeque::new()),
266 ui_conts: RefCell::new(UiContinuationMap::default()),
267 next_cont_id: Cell::new(1),
268 ui_thread_id: std::thread::current().id(),
269 tasks: RefCell::new(Vec::new()),
270 next_task_id: Cell::new(1),
271 task_waker: RefCell::new(None),
272 state_arena: StateArena::default(),
273 runtime_id: RuntimeId::next(),
274 }
275 }
276
277 fn init_task_waker(this: &Rc<Self>) {
278 let weak = Rc::downgrade(this);
279 let waker = RuntimeTaskWaker::new(weak).into_waker();
280 *this.task_waker.borrow_mut() = Some(waker);
281 }
282
283 fn schedule(&self) {
284 *self.needs_frame.borrow_mut() = true;
285 self.scheduler.schedule_frame();
286 }
287
288 fn enqueue_update(&self, command: Command) {
289 self.node_updates.borrow_mut().push(command);
290 self.schedule(); }
292
293 fn take_updates(&self) -> Vec<Command> {
294 let updates = self.node_updates.borrow_mut().drain(..).collect::<Vec<_>>();
296 updates
297 }
298
299 fn has_updates(&self) -> bool {
300 !self.node_updates.borrow().is_empty() || self.has_invalid_scopes()
301 }
302
303 fn register_invalid_scope(&self, id: ScopeId, scope: Weak<RecomposeScopeInner>) {
304 let mut invalid = self.invalid_scopes.borrow_mut();
305 if invalid.insert(id) {
306 self.scope_queue.borrow_mut().push((id, scope));
307 self.schedule();
308 }
309 }
310
311 fn mark_scope_recomposed(&self, id: ScopeId) {
312 self.invalid_scopes.borrow_mut().remove(&id);
313 }
314
315 fn take_invalidated_scopes(&self) -> Vec<(ScopeId, Weak<RecomposeScopeInner>)> {
316 let mut queue = self.scope_queue.borrow_mut();
318 if queue.is_empty() {
319 return Vec::new();
320 }
321 let pending: Vec<_> = queue.drain(..).collect();
322 drop(queue);
323 let invalid = self.invalid_scopes.borrow();
324 pending
325 .into_iter()
326 .filter(|(id, _)| invalid.contains(id))
327 .collect()
328 }
329
330 fn has_invalid_scopes(&self) -> bool {
331 !self.invalid_scopes.borrow().is_empty()
332 }
333
334 fn has_frame_callbacks(&self) -> bool {
335 !self.frame_callbacks.borrow().is_empty()
336 }
337
338 fn enqueue_ui_task(&self, task: Box<dyn FnOnce() + 'static>) {
343 self.local_tasks.borrow_mut().push_back(task);
344 self.schedule();
345 }
346
347 fn spawn_ui_task(&self, future: Pin<Box<dyn Future<Output = ()> + 'static>>) -> u64 {
348 let id = self.next_task_id.get();
349 self.next_task_id.set(id + 1);
350 self.tasks.borrow_mut().push(TaskEntry { id, future });
351 self.schedule();
352 id
353 }
354
355 fn cancel_task(&self, id: u64) {
356 let mut tasks = self.tasks.borrow_mut();
357 if tasks.iter().any(|entry| entry.id == id) {
358 tasks.retain(|entry| entry.id != id);
359 }
360 }
361
362 fn poll_async_tasks(&self) -> bool {
363 let waker = match self.task_waker.borrow().as_ref() {
364 Some(waker) => waker.clone(),
365 None => return false,
366 };
367 let mut cx = Context::from_waker(&waker);
368 let mut tasks_ref = self.tasks.borrow_mut();
369 let tasks = std::mem::take(&mut *tasks_ref);
370 drop(tasks_ref);
371 let mut pending = Vec::with_capacity(tasks.len());
372 let mut made_progress = false;
373 for mut entry in tasks.into_iter() {
374 match entry.future.as_mut().poll(&mut cx) {
375 Poll::Ready(()) => {
376 made_progress = true;
377 }
378 Poll::Pending => {
379 pending.push(entry);
380 }
381 }
382 }
383 if !pending.is_empty() {
384 self.tasks.borrow_mut().extend(pending);
385 }
386 made_progress
387 }
388
389 fn drain_ui(&self) {
390 loop {
391 let mut executed = false;
392
393 {
394 let rx = &mut *self.ui_rx.borrow_mut();
395 for message in rx.try_iter() {
396 executed = true;
397 let _guard = PendingGuard::new(&self.ui_dispatcher.pending);
398 match message {
399 UiMessage::Task(task) => {
400 task();
401 }
402 UiMessage::Invoke { id, value } => {
403 self.invoke_ui_cont(id, value);
404 }
405 }
406 }
407 }
408
409 loop {
410 let task = {
411 let mut local = self.local_tasks.borrow_mut();
412 local.pop_front()
413 };
414
415 match task {
416 Some(task) => {
417 executed = true;
418 task();
419 }
420 None => break,
421 }
422 }
423
424 if self.poll_async_tasks() {
425 executed = true;
426 }
427
428 if !executed {
429 break;
430 }
431 }
432 }
433
434 fn has_pending_ui(&self) -> bool {
435 let local_pending = self
436 .local_tasks
437 .try_borrow()
438 .map(|tasks| !tasks.is_empty())
439 .unwrap_or(true);
440
441 let async_pending = self
442 .tasks
443 .try_borrow()
444 .map(|tasks| !tasks.is_empty())
445 .unwrap_or(true);
446
447 local_pending || self.ui_dispatcher.has_pending() || async_pending
448 }
449
450 fn register_ui_cont<T: 'static>(&self, f: impl FnOnce(T) + 'static) -> u64 {
451 debug_assert_eq!(
452 std::thread::current().id(),
453 self.ui_thread_id,
454 "UI continuation registered off the runtime thread",
455 );
456 let id = self.next_cont_id.get();
457 self.next_cont_id.set(id + 1);
458 let callback = RefCell::new(Some(f));
459 self.ui_conts.borrow_mut().insert(
460 id,
461 Box::new(move |value: Box<dyn Any>| {
462 let slot = callback
463 .borrow_mut()
464 .take()
465 .expect("UI continuation invoked more than once");
466 let value = value
467 .downcast::<T>()
468 .expect("UI continuation type mismatch");
469 slot(*value);
470 }),
471 );
472 id
473 }
474
475 fn invoke_ui_cont(&self, id: u64, value: Box<dyn Any + Send>) {
476 debug_assert_eq!(
477 std::thread::current().id(),
478 self.ui_thread_id,
479 "UI continuation invoked off the runtime thread",
480 );
481 if let Some(callback) = self.ui_conts.borrow_mut().remove(&id) {
482 let value: Box<dyn Any> = value;
483 callback(value);
484 }
485 }
486
487 fn cancel_ui_cont(&self, id: u64) {
488 self.ui_conts.borrow_mut().remove(&id);
489 }
490
491 fn register_frame_callback(&self, callback: Box<dyn FnOnce(u64) + 'static>) -> FrameCallbackId {
492 let id = self.next_frame_callback_id.get();
493 self.next_frame_callback_id.set(id + 1);
494 self.frame_callbacks
495 .borrow_mut()
496 .push_back(FrameCallbackEntry {
497 id,
498 callback: Some(callback),
499 });
500 self.schedule();
501 id
502 }
503
504 fn cancel_frame_callback(&self, id: FrameCallbackId) {
505 let mut callbacks = self.frame_callbacks.borrow_mut();
506 if let Some(index) = callbacks.iter().position(|entry| entry.id == id) {
507 callbacks.remove(index);
508 }
509 let callbacks_empty = callbacks.is_empty();
510 drop(callbacks);
511 let local_pending = self
512 .local_tasks
513 .try_borrow()
514 .map(|tasks| !tasks.is_empty())
515 .unwrap_or(true);
516 let async_pending = self
517 .tasks
518 .try_borrow()
519 .map(|tasks| !tasks.is_empty())
520 .unwrap_or(true);
521 if !self.has_invalid_scopes()
522 && !self.has_updates()
523 && callbacks_empty
524 && !local_pending
525 && !self.ui_dispatcher.has_pending()
526 && !async_pending
527 {
528 *self.needs_frame.borrow_mut() = false;
529 }
530 }
531
532 fn drain_frame_callbacks(&self, frame_time_nanos: u64) {
533 let mut callbacks = self.frame_callbacks.borrow_mut();
534 let mut pending: Vec<Box<dyn FnOnce(u64) + 'static>> = Vec::with_capacity(callbacks.len());
535 while let Some(mut entry) = callbacks.pop_front() {
536 if let Some(callback) = entry.callback.take() {
537 pending.push(callback);
538 }
539 }
540 drop(callbacks);
541
542 if !pending.is_empty() {
547 let _ = crate::run_in_mutable_snapshot(|| {
548 for callback in pending {
549 callback(frame_time_nanos);
550 }
551 });
552 }
553
554 if !self.has_invalid_scopes()
555 && !self.has_updates()
556 && !self.has_frame_callbacks()
557 && !self.has_pending_ui()
558 {
559 *self.needs_frame.borrow_mut() = false;
560 }
561 }
562}
563
564#[derive(Clone)]
565pub struct Runtime {
566 inner: Rc<RuntimeInner>, }
568
569impl Runtime {
570 pub fn new(scheduler: Arc<dyn RuntimeScheduler>) -> Self {
571 let inner = Rc::new(RuntimeInner::new(scheduler));
572 RuntimeInner::init_task_waker(&inner);
573 let runtime = Self { inner };
574 register_runtime_handle(&runtime.handle());
575 runtime
576 }
577
578 pub fn handle(&self) -> RuntimeHandle {
579 RuntimeHandle {
580 inner: Rc::downgrade(&self.inner),
581 dispatcher: UiDispatcher::new(self.inner.ui_dispatcher.clone()),
582 ui_thread_id: self.inner.ui_thread_id,
583 id: self.inner.runtime_id,
584 }
585 }
586
587 pub fn has_updates(&self) -> bool {
588 self.inner.has_updates()
589 }
590
591 pub fn needs_frame(&self) -> bool {
592 *self.inner.needs_frame.borrow() || self.inner.ui_dispatcher.has_pending()
593 }
594
595 pub fn set_needs_frame(&self, value: bool) {
596 *self.inner.needs_frame.borrow_mut() = value;
597 }
598
599 pub fn frame_clock(&self) -> FrameClock {
600 FrameClock::new(self.handle())
601 }
602}
603
604#[derive(Default)]
605pub struct DefaultScheduler;
606
607impl RuntimeScheduler for DefaultScheduler {
608 fn schedule_frame(&self) {}
609}
610
611#[cfg(test)]
612#[derive(Default)]
613pub struct TestScheduler;
614
615#[cfg(test)]
616impl RuntimeScheduler for TestScheduler {
617 fn schedule_frame(&self) {}
618}
619
620#[cfg(test)]
621pub struct TestRuntime {
622 runtime: Runtime,
623}
624
625#[cfg(test)]
626impl Default for TestRuntime {
627 fn default() -> Self {
628 Self::new()
629 }
630}
631
632#[cfg(test)]
633impl TestRuntime {
634 pub fn new() -> Self {
635 Self {
636 runtime: Runtime::new(Arc::new(TestScheduler)),
637 }
638 }
639
640 pub fn handle(&self) -> RuntimeHandle {
641 self.runtime.handle()
642 }
643}
644
645#[derive(Clone)]
646pub struct RuntimeHandle {
647 inner: Weak<RuntimeInner>,
648 dispatcher: UiDispatcher,
649 ui_thread_id: ThreadId,
650 id: RuntimeId,
651}
652
653pub struct TaskHandle {
654 id: u64,
655 runtime: RuntimeHandle,
656}
657
658impl RuntimeHandle {
659 pub fn id(&self) -> RuntimeId {
660 self.id
661 }
662
663 pub(crate) fn alloc_state<T: Clone + 'static>(&self, value: T) -> StateId {
664 self.with_state_arena(|arena| arena.alloc(value, self.clone()))
665 }
666
667 pub(crate) fn with_state_arena<R>(&self, f: impl FnOnce(&StateArena) -> R) -> R {
668 self.inner
669 .upgrade()
670 .map(|inner| f(&inner.state_arena))
671 .unwrap_or_else(|| panic!("runtime dropped"))
672 }
673
674 #[allow(dead_code)]
675 pub(crate) fn alloc_value<T: 'static>(&self, value: T) -> StateId {
676 self.with_state_arena(|arena| arena.alloc_raw(value))
677 }
678
679 #[allow(dead_code)]
680 pub(crate) fn with_value<T: 'static, R>(&self, id: StateId, f: impl FnOnce(&T) -> R) -> R {
681 self.with_state_arena(|arena| {
682 let value = arena.get_raw::<T>(id);
683 f(&value)
684 })
685 }
686
687 pub fn schedule(&self) {
688 if let Some(inner) = self.inner.upgrade() {
689 inner.schedule();
690 }
691 }
692
693 pub fn enqueue_node_update(&self, command: Command) {
694 if let Some(inner) = self.inner.upgrade() {
695 inner.enqueue_update(command);
696 }
697 }
698
699 pub fn enqueue_ui_task(&self, task: Box<dyn FnOnce() + 'static>) {
706 if let Some(inner) = self.inner.upgrade() {
707 inner.enqueue_ui_task(task);
708 } else {
709 task();
710 }
711 }
712
713 pub fn spawn_ui<F>(&self, fut: F) -> Option<TaskHandle>
714 where
715 F: Future<Output = ()> + 'static,
716 {
717 self.inner.upgrade().map(|inner| {
718 let id = inner.spawn_ui_task(Box::pin(fut));
719 TaskHandle {
720 id,
721 runtime: self.clone(),
722 }
723 })
724 }
725
726 pub fn cancel_task(&self, id: u64) {
727 if let Some(inner) = self.inner.upgrade() {
728 inner.cancel_task(id);
729 }
730 }
731
732 pub fn post_ui(&self, task: impl FnOnce() + Send + 'static) {
737 self.dispatcher.post(task);
738 }
739
740 pub fn register_ui_cont<T: 'static>(&self, f: impl FnOnce(T) + 'static) -> Option<u64> {
741 self.inner.upgrade().map(|inner| inner.register_ui_cont(f))
742 }
743
744 pub fn cancel_ui_cont(&self, id: u64) {
745 if let Some(inner) = self.inner.upgrade() {
746 inner.cancel_ui_cont(id);
747 }
748 }
749
750 pub fn drain_ui(&self) {
751 if let Some(inner) = self.inner.upgrade() {
752 inner.drain_ui();
753 }
754 }
755
756 pub fn has_pending_ui(&self) -> bool {
757 self.inner
758 .upgrade()
759 .map(|inner| inner.has_pending_ui())
760 .unwrap_or_else(|| self.dispatcher.has_pending())
761 }
762
763 pub fn register_frame_callback(
764 &self,
765 callback: impl FnOnce(u64) + 'static,
766 ) -> Option<FrameCallbackId> {
767 self.inner
768 .upgrade()
769 .map(|inner| inner.register_frame_callback(Box::new(callback)))
770 }
771
772 pub fn cancel_frame_callback(&self, id: FrameCallbackId) {
773 if let Some(inner) = self.inner.upgrade() {
774 inner.cancel_frame_callback(id);
775 }
776 }
777
778 pub fn drain_frame_callbacks(&self, frame_time_nanos: u64) {
779 if let Some(inner) = self.inner.upgrade() {
780 inner.drain_frame_callbacks(frame_time_nanos);
781 }
782 }
783
784 pub fn frame_clock(&self) -> FrameClock {
785 FrameClock::new(self.clone())
786 }
787
788 pub fn set_needs_frame(&self, value: bool) {
789 if let Some(inner) = self.inner.upgrade() {
790 *inner.needs_frame.borrow_mut() = value;
791 }
792 }
793
794 pub(crate) fn take_updates(&self) -> Vec<Command> {
795 self.inner
797 .upgrade()
798 .map(|inner| inner.take_updates())
799 .unwrap_or_default()
800 }
801
802 pub fn has_updates(&self) -> bool {
803 self.inner
804 .upgrade()
805 .map(|inner| inner.has_updates())
806 .unwrap_or(false)
807 }
808
809 pub(crate) fn register_invalid_scope(&self, id: ScopeId, scope: Weak<RecomposeScopeInner>) {
810 if let Some(inner) = self.inner.upgrade() {
811 inner.register_invalid_scope(id, scope);
812 }
813 }
814
815 pub(crate) fn mark_scope_recomposed(&self, id: ScopeId) {
816 if let Some(inner) = self.inner.upgrade() {
817 inner.mark_scope_recomposed(id);
818 }
819 }
820
821 pub(crate) fn take_invalidated_scopes(&self) -> Vec<(ScopeId, Weak<RecomposeScopeInner>)> {
822 self.inner
824 .upgrade()
825 .map(|inner| inner.take_invalidated_scopes())
826 .unwrap_or_default()
827 }
828
829 pub fn has_invalid_scopes(&self) -> bool {
830 self.inner
831 .upgrade()
832 .map(|inner| inner.has_invalid_scopes())
833 .unwrap_or(false)
834 }
835
836 pub fn has_frame_callbacks(&self) -> bool {
837 self.inner
838 .upgrade()
839 .map(|inner| inner.has_frame_callbacks())
840 .unwrap_or(false)
841 }
842
843 pub fn assert_ui_thread(&self) {
844 debug_assert_eq!(
845 std::thread::current().id(),
846 self.ui_thread_id,
847 "state mutated off the runtime's UI thread"
848 );
849 }
850
851 pub fn dispatcher(&self) -> UiDispatcher {
852 self.dispatcher.clone()
853 }
854}
855
856impl TaskHandle {
857 pub fn cancel(self) {
858 self.runtime.cancel_task(self.id);
859 }
860}
861
862pub(crate) struct FrameCallbackEntry {
863 id: FrameCallbackId,
864 callback: Option<Box<dyn FnOnce(u64) + 'static>>,
865}
866
867struct RuntimeTaskWaker {
868 scheduler: Arc<dyn RuntimeScheduler>,
869}
870
871impl RuntimeTaskWaker {
872 fn new(inner: Weak<RuntimeInner>) -> Self {
873 let scheduler = inner
876 .upgrade()
877 .map(|rc| rc.scheduler.clone())
878 .expect("RuntimeInner dropped before waker created");
879 Self { scheduler }
880 }
881
882 fn into_waker(self) -> Waker {
883 futures_task::waker(Arc::new(self))
884 }
885}
886
887impl futures_task::ArcWake for RuntimeTaskWaker {
888 fn wake_by_ref(arc_self: &Arc<Self>) {
889 arc_self.scheduler.schedule_frame();
890 }
891}
892
893thread_local! {
894 static ACTIVE_RUNTIMES: RefCell<Vec<RuntimeHandle>> = const { RefCell::new(Vec::new()) }; static LAST_RUNTIME: RefCell<Option<RuntimeHandle>> = const { RefCell::new(None) };
896}
897
898pub fn current_runtime_handle() -> Option<RuntimeHandle> {
903 if let Some(handle) = ACTIVE_RUNTIMES.with(|stack| stack.borrow().last().cloned()) {
904 return Some(handle);
905 }
906 LAST_RUNTIME.with(|slot| slot.borrow().clone())
907}
908
909pub(crate) fn push_active_runtime(handle: &RuntimeHandle) {
910 ACTIVE_RUNTIMES.with(|stack| stack.borrow_mut().push(handle.clone()));
911 LAST_RUNTIME.with(|slot| *slot.borrow_mut() = Some(handle.clone()));
912}
913
914pub(crate) fn pop_active_runtime() {
915 ACTIVE_RUNTIMES.with(|stack| {
916 stack.borrow_mut().pop();
917 });
918}
919
920pub fn schedule_frame() {
922 if let Some(handle) = current_runtime_handle() {
923 handle.schedule();
924 return;
925 }
926 panic!("no runtime available to schedule frame");
927}
928
929pub fn schedule_node_update(
931 update: impl FnOnce(&mut dyn Applier) -> Result<(), NodeError> + 'static,
932) {
933 let handle = current_runtime_handle().expect("no runtime available to schedule node update");
934 let mut update_opt = Some(update);
935 handle.enqueue_node_update(Box::new(move |applier: &mut dyn Applier| {
936 if let Some(update) = update_opt.take() {
937 return update(applier);
938 }
939 Ok(())
940 }));
941}