1use std::{
2 any::Any,
3 cell::{Cell, RefCell},
4 collections::VecDeque,
5 future::Future,
6 pin::Pin,
7 rc::{Rc, Weak},
8 sync::{
9 Arc,
10 atomic::{AtomicBool, AtomicUsize, Ordering},
11 mpsc,
12 },
13 task::{Context, Poll, Waker},
14 thread::ThreadId,
15};
16
17#[cfg(any(feature = "internal", test))]
18use crate::frame_clock::FrameClock;
19use crate::{
20 Applier, Command, FrameCallbackId, Key, MutableStateInner, NodeError, RecomposeScope,
21 RecomposeScopeInner, ScopeId,
22 collections::map::HashMap,
23 platform::{RuntimeScheduler, SchedulerRef},
24 state::{MutationPolicy, NeverEqual},
25 thread_state,
26};
27
28#[derive(Clone, Copy, PartialEq, Eq)]
29pub(crate) enum FrameCallbackKind {
30 Transient,
31 Perpetual,
32}
33
34enum UiMessage {
35 Task(Box<dyn FnOnce() + Send + 'static>),
36 Invoke { id: u64, value: Box<dyn Any + Send> },
37}
38
39type UiContinuation = Box<dyn Fn(Box<dyn Any>) -> bool + 'static>;
40type UiContinuationMap = HashMap<u64, UiContinuation>;
41
42struct TypedStateCell<T: Clone + 'static> {
43 inner: MutableStateInner<T>,
44}
45
46trait ScopeWatchCell {
47 fn unregister_scope(&self, scope_id: ScopeId);
48 fn forget_scope(&self, scope_id: ScopeId);
49}
50
51impl<T: Clone + 'static> ScopeWatchCell for TypedStateCell<T> {
52 fn unregister_scope(&self, scope_id: ScopeId) {
53 self.inner.unregister_scope(scope_id);
54 }
55
56 fn forget_scope(&self, scope_id: ScopeId) {
57 self.inner.forget_scope(scope_id);
58 }
59}
60
61struct StateArenaSlot {
62 generation: u32,
63 cell: Option<Rc<dyn Any>>,
64 watcher_cell: Option<Rc<dyn ScopeWatchCell>>,
65 lease: Option<Weak<StateHandleLease>>,
66}
67
68#[derive(Default)]
69struct StateArenaInner {
70 cells: Vec<StateArenaSlot>,
71 free: Vec<u32>,
72 derived: HashMap<u32, Weak<dyn DerivedRefresh>>,
74}
75
76pub(crate) trait DerivedRefresh {
78 fn refresh(&self);
81}
82
83#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
86pub struct StateArenaDebugStats {
87 pub cells_len: usize,
89 pub cells_cap: usize,
91 pub free_len: usize,
93 pub free_cap: usize,
95}
96
97#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
100pub struct RuntimeDebugStats {
101 pub node_updates_len: usize,
103 pub node_updates_cap: usize,
105 pub invalid_scopes_len: usize,
107 pub scope_queue_len: usize,
109 pub scope_queue_cap: usize,
111 pub frame_callbacks_len: usize,
113 pub frame_callbacks_cap: usize,
115 pub local_tasks_len: usize,
117 pub local_tasks_cap: usize,
119 pub ui_conts_len: usize,
121 pub ui_conts_cap: usize,
123 pub tasks_len: usize,
125 pub tasks_cap: usize,
127 pub external_state_owners_len: usize,
129 pub external_state_owners_cap: usize,
131 pub ui_dispatcher_pending: usize,
133}
134
135#[derive(Default)]
136pub(crate) struct StateArena {
137 inner: RefCell<StateArenaInner>,
138}
139
140impl StateArena {
141 pub(crate) fn alloc<T: Clone + 'static>(&self, value: T, runtime: RuntimeHandle) -> StateId {
142 self.alloc_with_policy(value, runtime, Rc::new(NeverEqual))
143 }
144
145 pub(crate) fn alloc_with_policy<T: Clone + 'static>(
146 &self,
147 value: T,
148 runtime: RuntimeHandle,
149 policy: Rc<dyn MutationPolicy<T>>,
150 ) -> StateId {
151 let (slot, generation) = {
152 let mut inner = self.inner.borrow_mut();
153 loop {
154 let Some(slot) = inner.free.pop() else {
155 let slot = inner.cells.len() as u32;
156 inner.cells.push(StateArenaSlot {
157 generation: 0,
158 cell: None,
159 watcher_cell: None,
160 lease: None,
161 });
162 break (slot, 0);
163 };
164
165 let Some(entry) = inner.cells.get_mut(slot as usize) else {
166 continue;
167 };
168 if entry.cell.is_some() {
169 continue;
170 }
171
172 entry.watcher_cell = None;
173 entry.lease = None;
174 entry.generation = entry.generation.wrapping_add(1);
175 break (slot, entry.generation);
176 }
177 };
178 let id = StateId::new(slot, generation);
179 let inner = MutableStateInner::new_with_policy(value, runtime, policy);
180 inner.install_snapshot_observer(id);
181 let typed_cell = Rc::new(TypedStateCell { inner });
182 let cell: Rc<dyn Any> = typed_cell.clone();
183 let watcher_cell: Rc<dyn ScopeWatchCell> = typed_cell;
184 let mut arena = self.inner.borrow_mut();
185 let slot_entry = &mut arena.cells[slot as usize];
186 slot_entry.cell = Some(cell);
187 slot_entry.watcher_cell = Some(watcher_cell);
188 id
189 }
190
191 fn get_cell_opt(&self, id: StateId) -> Option<Rc<dyn Any>> {
192 self.inner
193 .borrow()
194 .cells
195 .get(id.slot_index())
196 .filter(|cell| cell.generation == id.generation())
197 .and_then(|cell| cell.cell.as_ref())
198 .cloned()
199 }
200
201 fn get_typed<T: Clone + 'static>(&self, id: StateId) -> Rc<TypedStateCell<T>> {
202 match self.get_cell_opt(id) {
203 None => panic!(
204 "state cell missing: slot={}, gen={}, expected={}",
205 id.slot(),
206 id.generation(),
207 std::any::type_name::<T>(),
208 ),
209 Some(cell) => Rc::downcast::<TypedStateCell<T>>(cell).unwrap_or_else(|_| {
210 panic!(
211 "state cell type mismatch: slot={}, gen={}, expected={}",
212 id.slot(),
213 id.generation(),
214 std::any::type_name::<T>(),
215 )
216 }),
217 }
218 }
219
220 fn get_typed_opt<T: Clone + 'static>(&self, id: StateId) -> Option<Rc<TypedStateCell<T>>> {
221 Rc::downcast::<TypedStateCell<T>>(self.get_cell_opt(id)?).ok()
222 }
223
224 pub(crate) fn with_typed<T: Clone + 'static, R>(
225 &self,
226 id: StateId,
227 f: impl FnOnce(&MutableStateInner<T>) -> R,
228 ) -> R {
229 let cell = self.get_typed::<T>(id);
230 f(&cell.inner)
231 }
232
233 pub(crate) fn with_typed_opt<T: Clone + 'static, R>(
234 &self,
235 id: StateId,
236 f: impl FnOnce(&MutableStateInner<T>) -> R,
237 ) -> Option<R> {
238 let cell = self.get_typed_opt::<T>(id)?;
239 Some(f(&cell.inner))
240 }
241
242 pub(crate) fn release(&self, id: StateId) {
243 let cell = {
244 let mut inner = self.inner.borrow_mut();
245 let Some(slot) = inner.cells.get_mut(id.slot_index()) else {
246 return;
247 };
248 if slot.generation != id.generation() {
249 return;
250 }
251 slot.lease = None;
252 slot.watcher_cell = None;
253 let cell = slot.cell.take();
254 if cell.is_some() {
255 inner.free.push(id.slot());
256 if !inner.derived.is_empty() {
257 inner.derived.remove(&id.slot());
258 }
259 }
260 cell
261 };
262 drop(cell);
263 }
264
265 pub(crate) fn register_derived(&self, id: StateId, refresh: Weak<dyn DerivedRefresh>) {
268 self.inner.borrow_mut().derived.insert(id.slot(), refresh);
269 }
270
271 #[cold]
274 pub(crate) fn refresh_derived(&self, id: StateId) {
275 let refresh = {
276 let inner = self.inner.borrow();
277 inner
278 .cells
279 .get(id.slot_index())
280 .filter(|cell| cell.generation == id.generation())
281 .and_then(|_| inner.derived.get(&id.slot()))
282 .and_then(Weak::upgrade)
283 };
284 if let Some(refresh) = refresh {
285 refresh.refresh();
286 }
287 }
288
289 pub(crate) fn stats(&self) -> (usize, usize) {
290 let inner = self.inner.borrow();
291 (inner.cells.len(), inner.free.len())
292 }
293
294 pub(crate) fn debug_stats(&self) -> StateArenaDebugStats {
295 let inner = self.inner.borrow();
296 StateArenaDebugStats {
297 cells_len: inner.cells.len(),
298 cells_cap: inner.cells.capacity(),
299 free_len: inner.free.len(),
300 free_cap: inner.free.capacity(),
301 }
302 }
303
304 pub(crate) fn unregister_scope(&self, id: StateId, scope_id: ScopeId) {
305 if let Some(watcher_cell) = self.watcher_cell(id) {
306 watcher_cell.unregister_scope(scope_id);
307 }
308 }
309
310 pub(crate) fn forget_scope(&self, id: StateId, scope_id: ScopeId) {
312 if let Some(watcher_cell) = self.watcher_cell(id) {
313 watcher_cell.forget_scope(scope_id);
314 }
315 }
316
317 fn watcher_cell(&self, id: StateId) -> Option<Rc<dyn ScopeWatchCell>> {
318 let inner = self.inner.borrow();
319 inner
320 .cells
321 .get(id.slot_index())
322 .filter(|slot| slot.generation == id.generation())
323 .and_then(|slot| slot.watcher_cell.as_ref())
324 .cloned()
325 }
326
327 pub(crate) fn register_lease(&self, id: StateId, lease: &Rc<StateHandleLease>) {
328 let mut inner = self.inner.borrow_mut();
329 let Some(slot) = inner.cells.get_mut(id.slot_index()) else {
330 return;
331 };
332 if slot.generation != id.generation() {
333 return;
334 }
335 slot.lease = Some(Rc::downgrade(lease));
336 }
337
338 pub(crate) fn retain_lease(&self, id: StateId) -> Option<Rc<StateHandleLease>> {
339 let inner = self.inner.borrow();
340 let slot = inner.cells.get(id.slot_index())?;
341 if slot.generation != id.generation() {
342 return None;
343 }
344 slot.lease.as_ref()?.upgrade()
345 }
346}
347
348#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
350pub struct StateId {
351 slot: u32,
352 generation: u32,
353}
354
355impl StateId {
356 const fn new(slot: u32, generation: u32) -> Self {
357 Self { slot, generation }
358 }
359
360 pub(crate) const fn slot(self) -> u32 {
361 self.slot
362 }
363
364 pub(crate) const fn slot_index(self) -> usize {
365 self.slot as usize
366 }
367
368 pub(crate) const fn generation(self) -> u32 {
369 self.generation
370 }
371}
372
373#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
375pub struct RuntimeId(u32);
376
377impl RuntimeId {
378 fn next() -> Self {
379 thread_state::runtimes(|state| {
380 let id = state.next_runtime_id.get();
381 state.next_runtime_id.set(id.wrapping_add(1));
382 Self(id)
383 })
384 }
385}
386
387struct UiDispatcherInner {
388 scheduler: SchedulerRef,
389 tx: mpsc::Sender<UiMessage>,
390 pending: AtomicUsize,
391}
392
393#[cfg(not(target_arch = "wasm32"))]
394type UiDispatcherRef = Arc<UiDispatcherInner>;
395
396#[cfg(target_arch = "wasm32")]
397type UiDispatcherRef = Rc<UiDispatcherInner>;
398
399impl UiDispatcherInner {
400 fn new(scheduler: SchedulerRef, tx: mpsc::Sender<UiMessage>) -> Self {
401 Self {
402 scheduler,
403 tx,
404 pending: AtomicUsize::new(0),
405 }
406 }
407
408 fn post(&self, task: impl FnOnce() + Send + 'static) {
409 self.pending.fetch_add(1, Ordering::SeqCst);
410 if self.tx.send(UiMessage::Task(Box::new(task))).is_ok() {
411 self.scheduler.schedule_frame();
412 } else {
413 self.pending.fetch_sub(1, Ordering::SeqCst);
414 }
415 }
416
417 fn post_invoke(&self, id: u64, value: Box<dyn Any + Send>) {
418 self.pending.fetch_add(1, Ordering::SeqCst);
419 if self.tx.send(UiMessage::Invoke { id, value }).is_ok() {
420 self.scheduler.schedule_frame();
421 } else {
422 self.pending.fetch_sub(1, Ordering::SeqCst);
423 }
424 }
425
426 fn has_pending(&self) -> bool {
427 self.pending.load(Ordering::SeqCst) > 0
428 }
429}
430
431struct PendingGuard<'a> {
432 counter: &'a AtomicUsize,
433}
434
435impl<'a> PendingGuard<'a> {
436 fn new(counter: &'a AtomicUsize) -> Self {
437 Self { counter }
438 }
439}
440
441impl Drop for PendingGuard<'_> {
442 fn drop(&mut self) {
443 let mut current = self.counter.load(Ordering::SeqCst);
444 loop {
445 if current == 0 {
446 return;
447 }
448 match self.counter.compare_exchange(
449 current,
450 current - 1,
451 Ordering::SeqCst,
452 Ordering::SeqCst,
453 ) {
454 Ok(_) => return,
455 Err(next) => current = next,
456 }
457 }
458 }
459}
460
461#[derive(Clone)]
463pub struct UiDispatcher {
464 inner: UiDispatcherRef,
465}
466
467impl UiDispatcher {
468 fn new(inner: UiDispatcherRef) -> Self {
469 Self { inner }
470 }
471
472 pub fn post(&self, task: impl FnOnce() + Send + 'static) {
475 self.inner.post(task);
476 }
477
478 pub fn post_invoke<T>(&self, id: u64, value: T)
481 where
482 T: Send + 'static,
483 {
484 self.inner.post_invoke(id, Box::new(value));
485 }
486
487 pub fn has_pending(&self) -> bool {
489 self.inner.has_pending()
490 }
491}
492
493struct RuntimeInner {
494 scheduler: SchedulerRef,
495 needs_frame: RefCell<bool>,
496 node_updates: RefCell<Vec<Command>>,
497 invalid_scope_count: Cell<usize>,
498 scope_queue: RefCell<Vec<Weak<RecomposeScopeInner>>>,
499 spare_scopes: RefCell<Vec<RecomposeScope>>,
501 frame_callbacks: RefCell<VecDeque<FrameCallbackEntry>>,
502 next_frame_callback_id: Cell<u64>,
503 last_frame_time_nanos: Cell<Option<u64>>,
504 ui_dispatcher: UiDispatcherRef,
505 ui_rx: RefCell<mpsc::Receiver<UiMessage>>,
506 local_tasks: RefCell<VecDeque<Box<dyn FnOnce() + 'static>>>,
507 ui_conts: RefCell<UiContinuationMap>,
508 next_cont_id: Cell<u64>,
509 ui_thread_id: ThreadId,
510 tasks: RefCell<HashMap<u64, TaskEntry>>,
511 task_order: RefCell<Vec<u64>>,
512 task_woken: Arc<AtomicBool>,
515 next_task_id: Cell<u64>,
516 state_arena: StateArena,
517 external_state_owners: RefCell<HashMap<StateId, Rc<StateHandleLease>>>,
518 live_recompose_scope_count: Cell<usize>,
519 forgotten_movables: RefCell<Vec<Key>>,
520 next_movable_content_id: Cell<u64>,
521 runtime_id: RuntimeId,
522 #[cfg(target_os = "android")]
523 rescued: RefCell<Option<Rc<thread_state::RescuedThreadState>>>,
524}
525
526struct TaskEntry {
527 label: String,
528 future: Option<Pin<Box<dyn Future<Output = ()> + 'static>>>,
529 runnable: Arc<AtomicBool>,
530 waker: Waker,
531}
532
533pub fn label_next_ui_task(label: impl Into<String>) {
536 thread_state::watch_thread_exit();
537 let label = label.into();
538 thread_state::runtimes(|state| *state.next_task_label.borrow_mut() = Some(label));
539}
540
541impl RuntimeInner {
542 fn new(scheduler: SchedulerRef) -> Self {
543 let (tx, rx) = mpsc::channel();
544 let dispatcher = UiDispatcherRef::new(UiDispatcherInner::new(scheduler.clone(), tx));
545 Self {
546 scheduler,
547 needs_frame: RefCell::new(false),
548 node_updates: RefCell::new(Vec::new()),
549 invalid_scope_count: Cell::new(0),
550 scope_queue: RefCell::new(Vec::new()),
551 spare_scopes: RefCell::new(Vec::new()),
552 frame_callbacks: RefCell::new(VecDeque::new()),
553 next_frame_callback_id: Cell::new(1),
554 last_frame_time_nanos: Cell::new(None),
555 ui_dispatcher: dispatcher,
556 ui_rx: RefCell::new(rx),
557 local_tasks: RefCell::new(VecDeque::new()),
558 ui_conts: RefCell::new(UiContinuationMap::default()),
559 next_cont_id: Cell::new(1),
560 ui_thread_id: std::thread::current().id(),
561 tasks: RefCell::new(HashMap::default()),
562 task_order: RefCell::new(Vec::new()),
563 task_woken: Arc::new(AtomicBool::new(false)),
564 next_task_id: Cell::new(1),
565 state_arena: StateArena::default(),
566 external_state_owners: RefCell::new(HashMap::default()),
567 live_recompose_scope_count: Cell::new(0),
568 forgotten_movables: RefCell::new(Vec::new()),
569 next_movable_content_id: Cell::new(1),
570 runtime_id: RuntimeId::next(),
571 #[cfg(target_os = "android")]
572 rescued: RefCell::new(None),
573 }
574 }
575
576 fn schedule(&self) {
577 *self.needs_frame.borrow_mut() = true;
578 self.scheduler.schedule_frame();
579 }
580
581 fn enqueue_update(&self, command: Command) {
582 self.node_updates.borrow_mut().push(command);
583 self.schedule();
584 }
585
586 fn take_updates(&self) -> Vec<Command> {
587 self.node_updates.borrow_mut().drain(..).collect::<Vec<_>>()
588 }
589
590 fn has_updates(&self) -> bool {
591 !self.node_updates.borrow().is_empty() || self.has_invalid_scopes()
592 }
593
594 fn register_invalid_scope(&self, scope: Weak<RecomposeScopeInner>) {
595 self.invalid_scope_count
596 .set(self.invalid_scope_count.get() + 1);
597 self.scope_queue.borrow_mut().push(scope);
598 self.schedule();
599 }
600
601 fn requeue_invalid_scope(&self, scope: &RecomposeScope) {
602 if scope.is_enqueued() {
603 self.scope_queue.borrow_mut().push(scope.downgrade());
604 self.schedule();
605 }
606 }
607
608 fn mark_scope_recomposed(&self) {
609 self.invalid_scope_count
610 .set(self.invalid_scope_count.get().saturating_sub(1));
611 }
612
613 fn take_invalidated_scopes(&self) -> Option<Vec<RecomposeScope>> {
614 let mut pending = {
616 let mut queue = self.scope_queue.borrow_mut();
617 if queue.is_empty() {
618 return None;
619 }
620 std::mem::take(&mut *queue)
621 };
622 let mut scopes = std::mem::take(&mut *self.spare_scopes.borrow_mut());
623 scopes.extend(
624 pending
625 .iter()
626 .filter_map(RecomposeScope::upgrade)
627 .filter(RecomposeScope::is_enqueued),
628 );
629 pending.clear();
630 let mut queue = self.scope_queue.borrow_mut();
631 if queue.is_empty() {
632 std::mem::swap(&mut *queue, &mut pending);
633 }
634 drop(queue);
635 if scopes.is_empty() {
636 self.recycle_scopes(scopes);
637 return None;
638 }
639 Some(scopes)
640 }
641
642 fn recycle_scopes(&self, mut scopes: Vec<RecomposeScope>) {
643 scopes.clear();
645 let mut spare = self.spare_scopes.borrow_mut();
646 if spare.capacity() < scopes.capacity() {
647 *spare = scopes;
648 }
649 }
650
651 fn has_invalid_scopes(&self) -> bool {
652 self.invalid_scope_count.get() != 0
653 }
654
655 fn queued_invalid_scope_ids(&self) -> Vec<ScopeId> {
656 let mut ids: Vec<ScopeId> = self
657 .scope_queue
658 .borrow()
659 .iter()
660 .filter_map(RecomposeScope::upgrade)
661 .filter(RecomposeScope::is_enqueued)
662 .map(|scope| scope.id())
663 .collect();
664 ids.sort_unstable();
665 ids.dedup();
666 ids
667 }
668
669 fn increment_live_recompose_scope_count(&self) {
670 self.live_recompose_scope_count
671 .set(self.live_recompose_scope_count.get().saturating_add(1));
672 }
673
674 fn decrement_live_recompose_scope_count(&self) {
675 self.live_recompose_scope_count
676 .set(self.live_recompose_scope_count.get().saturating_sub(1));
677 }
678
679 fn live_recompose_scope_count(&self) -> usize {
680 self.live_recompose_scope_count.get()
681 }
682
683 fn has_frame_callbacks(&self) -> bool {
684 !self.frame_callbacks.borrow().is_empty()
685 }
686
687 fn has_transient_frame_callbacks(&self) -> bool {
688 self.frame_callbacks
689 .borrow()
690 .iter()
691 .any(|entry| entry.kind == FrameCallbackKind::Transient)
692 }
693
694 fn enqueue_ui_task(&self, task: Box<dyn FnOnce() + 'static>) {
695 self.local_tasks.borrow_mut().push_back(task);
696 self.schedule();
697 }
698
699 fn spawn_ui_task(&self, future: Pin<Box<dyn Future<Output = ()> + 'static>>) -> u64 {
700 let id = self.next_task_id.get();
701 self.next_task_id.set(id + 1);
702 let label = thread_state::runtimes(|state| state.next_task_label.borrow_mut().take())
703 .unwrap_or_else(|| "unnamed".to_string());
704 let runnable = Arc::new(AtomicBool::new(true));
705 self.task_woken.store(true, Ordering::Release);
706 let waker = RuntimeTaskWaker::new(self, Arc::clone(&runnable)).into_waker();
707 self.tasks.borrow_mut().insert(
708 id,
709 TaskEntry {
710 label,
711 future: Some(future),
712 runnable,
713 waker,
714 },
715 );
716 self.task_order.borrow_mut().push(id);
717 self.schedule();
718 id
719 }
720
721 fn cancel_task(&self, id: u64) {
722 let task = self.tasks.borrow_mut().remove(&id);
723 self.task_order.borrow_mut().retain(|queued| *queued != id);
724 drop(task);
725 }
726
727 fn has_task(&self, id: u64) -> bool {
728 self.tasks
729 .try_borrow()
730 .map_or(true, |tasks| tasks.contains_key(&id))
731 }
732
733 fn poll_async_tasks(&self) -> bool {
734 if !self.task_woken.load(Ordering::Acquire) {
736 return false;
737 }
738 let mut order = std::mem::take(&mut *self.task_order.borrow_mut());
741 if order.is_empty() || !self.task_woken.swap(false, Ordering::AcqRel) {
744 let mut task_order = self.task_order.borrow_mut();
745 if task_order.is_empty() {
746 *task_order = order;
747 } else {
748 task_order.extend(order);
749 }
750 return false;
751 }
752 let mut pending = 0;
753 let mut made_progress = false;
754 for index in 0..order.len() {
755 let id = order[index];
756 let task = {
757 let mut tasks = self.tasks.borrow_mut();
758 let Some(entry) = tasks.get_mut(&id) else {
759 continue;
760 };
761 if entry.runnable.swap(false, Ordering::AcqRel) {
762 entry
763 .future
764 .take()
765 .map(|future| (future, entry.waker.clone()))
766 } else {
767 None
768 }
769 };
770 let Some((mut future, waker)) = task else {
771 order[pending] = id;
772 pending += 1;
773 continue;
774 };
775 let mut cx = Context::from_waker(&waker);
776 match future.as_mut().poll(&mut cx) {
777 Poll::Ready(()) => {
778 self.cancel_task(id);
779 made_progress = true;
780 }
781 Poll::Pending => {
782 let mut tasks = self.tasks.borrow_mut();
783 if let Some(entry) = tasks.get_mut(&id) {
784 entry.future = Some(future);
785 order[pending] = id;
786 pending += 1;
787 } else {
788 drop(tasks);
789 drop(future);
790 }
791 }
792 }
793 }
794 order.truncate(pending);
795 order.retain(|id| self.has_task(*id));
796 let mut task_order = self.task_order.borrow_mut();
797 if task_order.is_empty() {
798 *task_order = order;
799 } else {
800 task_order.extend(order);
801 }
802 made_progress
803 }
804
805 fn drain_ui(&self) {
806 #[cfg(target_os = "emscripten")]
807 crate::concurrency::fire_due_alarms();
808 loop {
809 let mut executed = false;
810
811 if self.ui_dispatcher.has_pending() {
814 let rx = &mut *self.ui_rx.borrow_mut();
815 for message in rx.try_iter() {
816 executed = true;
817 let _guard = PendingGuard::new(&self.ui_dispatcher.pending);
818 match message {
819 UiMessage::Task(task) => {
820 task();
821 }
822 UiMessage::Invoke { id, value } => {
823 self.invoke_ui_cont(id, value);
824 }
825 }
826 }
827 }
828
829 if self.run_local_tasks() {
830 executed = true;
831 }
832
833 if self.poll_async_tasks() {
834 executed = true;
835 }
836 self.run_local_tasks();
843
844 if !executed {
845 break;
846 }
847 }
848
849 self.clear_needs_frame_if_idle();
850 }
851
852 fn run_local_tasks(&self) -> bool {
853 let mut ran = false;
854 loop {
855 let task = self.local_tasks.borrow_mut().pop_front();
856 let Some(task) = task else {
857 return ran;
858 };
859 ran = true;
860 task();
861 }
862 }
863
864 fn has_pending_ui(&self) -> bool {
865 let local_pending = self
866 .local_tasks
867 .try_borrow()
868 .map_or(true, |tasks| !tasks.is_empty());
869
870 local_pending || self.ui_dispatcher.has_pending() || self.has_runnable_tasks()
871 }
872
873 fn has_runnable_tasks(&self) -> bool {
876 self.task_woken.load(Ordering::Acquire)
877 && self
878 .tasks
879 .try_borrow()
880 .map_or(true, |tasks| !tasks.is_empty())
881 }
882
883 fn register_ui_cont<T: 'static>(&self, f: impl FnOnce(T) + 'static) -> u64 {
884 debug_assert_eq!(
885 std::thread::current().id(),
886 self.ui_thread_id,
887 "UI continuation registered off the runtime thread",
888 );
889 let id = self.next_cont_id.get();
890 self.next_cont_id.set(id + 1);
891 let callback = RefCell::new(Some(f));
892 self.ui_conts.borrow_mut().insert(
893 id,
894 Box::new(move |value: Box<dyn Any>| {
895 let Ok(value) = value.downcast::<T>() else {
896 return false;
897 };
898 let Some(slot) = callback.borrow_mut().take() else {
899 return true;
900 };
901 slot(*value);
902 true
903 }),
904 );
905 id
906 }
907
908 fn invoke_ui_cont(&self, id: u64, value: Box<dyn Any + Send>) {
909 debug_assert_eq!(
910 std::thread::current().id(),
911 self.ui_thread_id,
912 "UI continuation invoked off the runtime thread",
913 );
914 let callback = { self.ui_conts.borrow_mut().remove(&id) };
915 if let Some(callback) = callback {
916 let value: Box<dyn Any> = value;
917 if !callback(value) {
918 self.ui_conts.borrow_mut().insert(id, callback);
919 }
920 }
921 }
922
923 fn cancel_ui_cont(&self, id: u64) {
924 let continuation = self.ui_conts.borrow_mut().remove(&id);
925 drop(continuation);
926 }
927
928 fn register_frame_callback(
929 &self,
930 kind: FrameCallbackKind,
931 callback: FrameCallback,
932 ) -> FrameCallbackId {
933 let id = self.next_frame_callback_id.get();
934 self.next_frame_callback_id.set(id + 1);
935 self.frame_callbacks
936 .borrow_mut()
937 .push_back(FrameCallbackEntry { id, kind, callback });
938 self.schedule();
939 id
940 }
941
942 #[cfg(any(feature = "internal", test))]
946 fn frame_waker_time(&self, id: FrameCallbackId, waker: &Waker) -> Option<u64> {
947 let mut callbacks = self.frame_callbacks.borrow_mut();
948 let Some(entry) = callbacks.iter_mut().find(|entry| entry.id == id) else {
949 return self.last_frame_time_nanos.get();
950 };
951 if let FrameCallback::Wake(held) = &mut entry.callback
952 && !held.will_wake(waker)
953 {
954 held.clone_from(waker);
955 }
956 None
957 }
958
959 fn cancel_frame_callback(&self, id: FrameCallbackId) {
960 let removed = {
961 let mut callbacks = self.frame_callbacks.borrow_mut();
962 callbacks
963 .iter()
964 .position(|entry| entry.id == id)
965 .and_then(|index| callbacks.remove(index))
966 };
967 drop(removed);
968 self.clear_needs_frame_if_idle();
969 }
970
971 fn clear_needs_frame_if_idle(&self) {
972 if !self.has_invalid_scopes()
973 && !self.has_updates()
974 && !self.has_frame_callbacks()
975 && !self.has_pending_ui()
976 {
977 *self.needs_frame.borrow_mut() = false;
978 }
979 }
980
981 fn drain_frame_callbacks(&self, frame_time_nanos: u64) {
982 if self
983 .last_frame_time_nanos
984 .get()
985 .is_some_and(|previous| frame_time_nanos <= previous)
986 {
987 return;
988 }
989 self.last_frame_time_nanos.set(Some(frame_time_nanos));
990 let next_frame_id = self.next_frame_callback_id.get();
991 let drain = || {
992 loop {
993 let entry = {
994 let mut callbacks = self.frame_callbacks.borrow_mut();
995 if callbacks
996 .front()
997 .is_some_and(|entry| entry.id < next_frame_id)
998 {
999 callbacks.pop_front()
1000 } else {
1001 None
1002 }
1003 };
1004 let Some(entry) = entry else {
1005 break;
1006 };
1007 match entry.callback {
1008 FrameCallback::Run(callback) => callback(frame_time_nanos),
1009 #[cfg(any(feature = "internal", test))]
1010 FrameCallback::Wake(waker) => waker.wake(),
1011 }
1012 }
1013 };
1014 let runs_callbacks = self
1017 .frame_callbacks
1018 .borrow()
1019 .iter()
1020 .any(|entry| matches!(entry.callback, FrameCallback::Run(_)));
1021 if runs_callbacks {
1022 let _ = crate::run_in_mutable_snapshot(drain);
1023 } else {
1024 drain();
1025 }
1026
1027 self.clear_needs_frame_if_idle();
1028 }
1029
1030 fn debug_stats(&self) -> RuntimeDebugStats {
1031 let node_updates = self.node_updates.borrow();
1032 let scope_queue = self.scope_queue.borrow();
1033 let frame_callbacks = self.frame_callbacks.borrow();
1034 let local_tasks = self.local_tasks.borrow();
1035 let ui_conts = self.ui_conts.borrow();
1036 let tasks = self.tasks.borrow();
1037 let external_state_owners = self.external_state_owners.borrow();
1038
1039 RuntimeDebugStats {
1040 node_updates_len: node_updates.len(),
1041 node_updates_cap: node_updates.capacity(),
1042 invalid_scopes_len: self.invalid_scope_count.get(),
1043 scope_queue_len: scope_queue.len(),
1044 scope_queue_cap: scope_queue.capacity(),
1045 frame_callbacks_len: frame_callbacks.len(),
1046 frame_callbacks_cap: frame_callbacks.capacity(),
1047 local_tasks_len: local_tasks.len(),
1048 local_tasks_cap: local_tasks.capacity(),
1049 ui_conts_len: ui_conts.len(),
1050 ui_conts_cap: ui_conts.capacity(),
1051 tasks_len: tasks.len(),
1052 tasks_cap: tasks.capacity(),
1053 external_state_owners_len: external_state_owners.len(),
1054 external_state_owners_cap: external_state_owners.capacity(),
1055 ui_dispatcher_pending: self.ui_dispatcher.pending.load(Ordering::SeqCst),
1056 }
1057 }
1058}
1059
1060#[derive(Clone)]
1063pub struct Runtime {
1064 inner: Rc<RuntimeInner>,
1065}
1066
1067impl Runtime {
1068 pub fn new(scheduler: SchedulerRef) -> Self {
1071 thread_state::watch_thread_exit();
1072 let inner = Rc::new(RuntimeInner::new(scheduler));
1073 let runtime = Self { inner };
1074 let handle = runtime.handle();
1075 thread_state::runtimes(|state| {
1076 state
1077 .registered_runtimes
1078 .borrow_mut()
1079 .insert(handle.id(), handle.clone());
1080 *state.last_runtime.borrow_mut() = Some(handle);
1081 });
1082 runtime
1083 }
1084
1085 #[cfg(target_os = "android")]
1086 pub(crate) fn restore_thread_state(&self) {
1087 if let Some(rescued) = self.inner.rescued.take() {
1088 thread_state::restore(&rescued);
1089 }
1090 }
1091
1092 pub fn handle(&self) -> RuntimeHandle {
1094 RuntimeHandle {
1095 inner: Rc::downgrade(&self.inner),
1096 dispatcher: UiDispatcher::new(self.inner.ui_dispatcher.clone()),
1097 ui_thread_id: self.inner.ui_thread_id,
1098 id: self.inner.runtime_id,
1099 }
1100 }
1101
1102 pub fn has_updates(&self) -> bool {
1104 self.inner.has_updates()
1105 }
1106
1107 pub fn needs_frame(&self) -> bool {
1109 *self.inner.needs_frame.borrow() || self.inner.has_runnable_tasks()
1110 }
1111
1112 pub fn set_needs_frame(&self, value: bool) {
1114 *self.inner.needs_frame.borrow_mut() = value;
1115 }
1116
1117 pub fn last_frame_time_nanos(&self) -> Option<u64> {
1122 self.inner.last_frame_time_nanos.get()
1123 }
1124
1125 #[cfg(any(feature = "internal", test))]
1127 pub fn frame_clock(&self) -> FrameClock {
1128 FrameClock::new(self.handle())
1129 }
1130}
1131
1132impl Drop for Runtime {
1133 fn drop(&mut self) {
1134 if Rc::strong_count(&self.inner) != 1 {
1135 return;
1136 }
1137 let id = self.inner.runtime_id;
1138 thread_state::try_runtimes(|state| {
1139 state.registered_runtimes.borrow_mut().remove(&id);
1140 let mut last = state.last_runtime.borrow_mut();
1141 if last.as_ref().is_some_and(|handle| handle.id() == id) {
1142 *last = None;
1143 }
1144 });
1145 thread_state::release_when_idle();
1146 }
1147}
1148
1149#[derive(Default)]
1151pub struct DefaultScheduler;
1152
1153impl RuntimeScheduler for DefaultScheduler {
1154 fn schedule_frame(&self) {}
1155}
1156
1157#[cfg(test)]
1158#[derive(Default)]
1159pub(crate) struct TestScheduler;
1160
1161#[cfg(test)]
1162impl RuntimeScheduler for TestScheduler {
1163 fn schedule_frame(&self) {}
1164}
1165
1166#[cfg(test)]
1167pub(crate) struct TestRuntime {
1168 runtime: Runtime,
1169}
1170
1171#[cfg(test)]
1172impl Default for TestRuntime {
1173 fn default() -> Self {
1174 Self::new()
1175 }
1176}
1177
1178#[cfg(test)]
1179impl TestRuntime {
1180 pub(crate) fn new() -> Self {
1181 Self {
1182 runtime: Runtime::new(Arc::new(TestScheduler)),
1183 }
1184 }
1185
1186 pub(crate) fn handle(&self) -> RuntimeHandle {
1187 self.runtime.handle()
1188 }
1189}
1190
1191#[derive(Clone)]
1194pub struct RuntimeHandle {
1195 inner: Weak<RuntimeInner>,
1196 dispatcher: UiDispatcher,
1197 ui_thread_id: ThreadId,
1198 id: RuntimeId,
1199}
1200
1201pub(crate) struct ScopeRuntime(Weak<RuntimeInner>);
1205
1206impl ScopeRuntime {
1207 pub(crate) fn mark_scope_recomposed(&self) {
1208 if let Some(inner) = self.0.upgrade() {
1209 inner.mark_scope_recomposed();
1210 }
1211 }
1212
1213 pub(crate) fn register_invalid_scope(&self, scope: Weak<RecomposeScopeInner>) {
1214 if let Some(inner) = self.0.upgrade() {
1215 inner.register_invalid_scope(scope);
1216 }
1217 }
1218
1219 pub(crate) fn unregister_state_scope(&self, id: StateId, scope_id: ScopeId) {
1220 if let Some(inner) = self.0.upgrade() {
1221 inner.state_arena.unregister_scope(id, scope_id);
1222 }
1223 }
1224
1225 pub(crate) fn forget_state_scope(&self, id: StateId, scope_id: ScopeId) {
1226 if let Some(inner) = self.0.upgrade() {
1227 inner.state_arena.forget_scope(id, scope_id);
1228 }
1229 }
1230
1231 pub(crate) fn increment_live_recompose_scope_count(&self) {
1232 if let Some(inner) = self.0.upgrade() {
1233 inner.increment_live_recompose_scope_count();
1234 }
1235 }
1236
1237 pub(crate) fn decrement_live_recompose_scope_count(&self) {
1238 if let Some(inner) = self.0.upgrade() {
1239 inner.decrement_live_recompose_scope_count();
1240 }
1241 }
1242}
1243
1244pub struct TaskHandle {
1246 id: u64,
1247 runtime: RuntimeHandle,
1248}
1249
1250pub(crate) struct DeferredStateRelease {
1251 runtime: Weak<RuntimeInner>,
1252 id: StateId,
1253}
1254
1255pub(crate) struct StateHandleLease {
1256 id: StateId,
1257 runtime: RuntimeHandle,
1258}
1259
1260impl StateHandleLease {
1261 pub(crate) fn id(&self) -> StateId {
1262 self.id
1263 }
1264
1265 pub(crate) fn runtime_id(&self) -> RuntimeId {
1266 self.runtime.id()
1267 }
1268}
1269
1270impl Drop for StateHandleLease {
1271 fn drop(&mut self) {
1272 defer_state_release(&self.runtime, self.id);
1273 }
1274}
1275
1276struct StateOwnerFrame;
1277
1278impl Drop for StateOwnerFrame {
1279 fn drop(&mut self) {
1280 thread_state::runtimes(|state| state.state_owners.borrow_mut().pop());
1281 }
1282}
1283
1284pub(crate) fn collecting_states<T>(build: impl FnOnce() -> T) -> (T, Vec<Rc<StateHandleLease>>) {
1302 thread_state::runtimes(|state| state.state_owners.borrow_mut().push(Vec::new()));
1303 let frame = StateOwnerFrame;
1304 let value = build();
1305 let states = thread_state::runtimes(|state| {
1306 state
1307 .state_owners
1308 .borrow_mut()
1309 .last_mut()
1310 .map(std::mem::take)
1311 .unwrap_or_default()
1312 });
1313 drop(frame);
1314 (value, states)
1315}
1316
1317fn hand_to_current_owner(lease: &Rc<StateHandleLease>) -> bool {
1318 thread_state::runtimes(|state| match state.state_owners.borrow_mut().last_mut() {
1319 Some(owner) => {
1320 owner.push(Rc::clone(lease));
1321 true
1322 }
1323 None => false,
1324 })
1325}
1326
1327impl RuntimeHandle {
1328 pub fn id(&self) -> RuntimeId {
1330 self.id
1331 }
1332
1333 pub(crate) fn alloc_state<T: Clone + 'static>(&self, value: T) -> Rc<StateHandleLease> {
1334 let id = self.with_state_arena(|arena| arena.alloc(value, self.clone()));
1335 let lease = Rc::new(StateHandleLease {
1336 id,
1337 runtime: self.clone(),
1338 });
1339 self.with_state_arena(|arena| arena.register_lease(id, &lease));
1340 lease
1341 }
1342
1343 pub(crate) fn alloc_state_with_policy<T: Clone + 'static>(
1344 &self,
1345 value: T,
1346 policy: Rc<dyn MutationPolicy<T>>,
1347 ) -> Rc<StateHandleLease> {
1348 let id =
1349 self.with_state_arena(|arena| arena.alloc_with_policy(value, self.clone(), policy));
1350 let lease = Rc::new(StateHandleLease {
1351 id,
1352 runtime: self.clone(),
1353 });
1354 self.with_state_arena(|arena| arena.register_lease(id, &lease));
1355 lease
1356 }
1357
1358 pub(crate) fn alloc_persistent_state<T: Clone + 'static>(
1359 &self,
1360 value: T,
1361 ) -> crate::MutableState<T> {
1362 self.hand_out(self.alloc_state(value))
1363 }
1364
1365 pub(crate) fn alloc_persistent_state_with_policy<T: Clone + 'static>(
1366 &self,
1367 value: T,
1368 policy: Rc<dyn MutationPolicy<T>>,
1369 ) -> crate::MutableState<T> {
1370 self.hand_out(self.alloc_state_with_policy(value, policy))
1371 }
1372
1373 fn hand_out<T: Clone + 'static>(&self, lease: Rc<StateHandleLease>) -> crate::MutableState<T> {
1374 if !hand_to_current_owner(&lease)
1375 && let Some(inner) = self.inner.upgrade()
1376 {
1377 inner
1378 .external_state_owners
1379 .borrow_mut()
1380 .insert(lease.id(), Rc::clone(&lease));
1381 }
1382 crate::MutableState::from_lease(&lease)
1383 }
1384
1385 pub(crate) fn retain_state_lease(&self, id: StateId) -> Option<Rc<StateHandleLease>> {
1386 self.with_state_arena(|arena| arena.retain_lease(id))
1387 }
1388
1389 pub(crate) fn with_state_arena<R>(&self, f: impl FnOnce(&StateArena) -> R) -> R {
1390 self.try_with_state_arena(f)
1391 .unwrap_or_else(|| panic!("runtime dropped"))
1392 }
1393
1394 pub(crate) fn try_with_state_arena<R>(&self, f: impl FnOnce(&StateArena) -> R) -> Option<R> {
1395 self.inner.upgrade().map(|inner| f(&inner.state_arena))
1396 }
1397
1398 pub fn state_arena_stats(&self) -> (usize, usize) {
1400 self.try_with_state_arena(StateArena::stats)
1401 .unwrap_or_default()
1402 }
1403
1404 pub fn state_arena_debug_stats(&self) -> StateArenaDebugStats {
1406 self.try_with_state_arena(StateArena::debug_stats)
1407 .unwrap_or_default()
1408 }
1409
1410 pub fn debug_stats(&self) -> RuntimeDebugStats {
1412 self.inner
1413 .upgrade()
1414 .map(|inner| inner.debug_stats())
1415 .unwrap_or_default()
1416 }
1417
1418 pub fn live_ui_task_labels(&self) -> Vec<(u64, String)> {
1420 self.inner
1421 .upgrade()
1422 .map(|inner| {
1423 inner
1424 .tasks
1425 .borrow()
1426 .iter()
1427 .map(|(id, entry)| (*id, entry.label.clone()))
1428 .collect()
1429 })
1430 .unwrap_or_default()
1431 }
1432
1433 pub fn schedule(&self) {
1435 if let Some(inner) = self.inner.upgrade() {
1436 inner.schedule();
1437 }
1438 }
1439
1440 pub(crate) fn enqueue_node_update(&self, command: Command) {
1441 if let Some(inner) = self.inner.upgrade() {
1442 inner.enqueue_update(command);
1443 }
1444 }
1445
1446 pub fn enqueue_ui_task(&self, task: Box<dyn FnOnce() + 'static>) {
1453 if let Some(inner) = self.inner.upgrade() {
1454 inner.enqueue_ui_task(task);
1455 } else {
1456 task();
1457 }
1458 }
1459
1460 pub fn spawn_ui<F>(&self, fut: F) -> Option<TaskHandle>
1463 where
1464 F: Future<Output = ()> + 'static,
1465 {
1466 self.inner.upgrade().map(|inner| {
1467 let id = inner.spawn_ui_task(Box::pin(fut));
1468 TaskHandle {
1469 id,
1470 runtime: self.clone(),
1471 }
1472 })
1473 }
1474
1475 pub fn cancel_task(&self, id: u64) {
1477 if let Some(inner) = self.inner.upgrade() {
1478 inner.cancel_task(id);
1479 }
1480 }
1481
1482 pub fn has_task(&self, id: u64) -> bool {
1484 self.inner.upgrade().is_some_and(|inner| inner.has_task(id))
1485 }
1486
1487 pub fn post_ui(&self, task: impl FnOnce() + Send + 'static) {
1492 self.dispatcher.post(task);
1493 }
1494
1495 pub fn register_ui_cont<T: 'static>(&self, f: impl FnOnce(T) + 'static) -> Option<u64> {
1498 self.inner.upgrade().map(|inner| inner.register_ui_cont(f))
1499 }
1500
1501 pub fn cancel_ui_cont(&self, id: u64) {
1503 if let Some(inner) = self.inner.upgrade() {
1504 inner.cancel_ui_cont(id);
1505 }
1506 }
1507
1508 pub fn drain_ui(&self) {
1510 if let Some(inner) = self.inner.upgrade() {
1511 inner.drain_ui();
1512 }
1513 }
1514
1515 pub fn has_pending_ui(&self) -> bool {
1517 self.inner.upgrade().map_or_else(
1518 || self.dispatcher.has_pending(),
1519 |inner| inner.has_pending_ui(),
1520 )
1521 }
1522
1523 pub fn register_frame_callback(
1526 &self,
1527 callback: impl FnOnce(u64) + 'static,
1528 ) -> Option<FrameCallbackId> {
1529 self.inner.upgrade().map(|inner| {
1530 inner.register_frame_callback(
1531 FrameCallbackKind::Transient,
1532 FrameCallback::Run(Box::new(callback)),
1533 )
1534 })
1535 }
1536
1537 pub fn register_perpetual_frame_callback(
1540 &self,
1541 callback: impl FnOnce(u64) + 'static,
1542 ) -> Option<FrameCallbackId> {
1543 self.inner.upgrade().map(|inner| {
1544 inner.register_frame_callback(
1545 FrameCallbackKind::Perpetual,
1546 FrameCallback::Run(Box::new(callback)),
1547 )
1548 })
1549 }
1550
1551 #[cfg(any(feature = "internal", test))]
1554 pub(crate) fn register_frame_waker(
1555 &self,
1556 kind: FrameCallbackKind,
1557 waker: Waker,
1558 ) -> Option<FrameCallbackId> {
1559 self.inner
1560 .upgrade()
1561 .map(|inner| inner.register_frame_callback(kind, FrameCallback::Wake(waker)))
1562 }
1563
1564 #[cfg(any(feature = "internal", test))]
1567 pub(crate) fn frame_waker_time(&self, id: FrameCallbackId, waker: &Waker) -> Option<u64> {
1568 self.inner
1569 .upgrade()
1570 .and_then(|inner| inner.frame_waker_time(id, waker))
1571 }
1572
1573 pub fn cancel_frame_callback(&self, id: FrameCallbackId) {
1575 if let Some(inner) = self.inner.upgrade() {
1576 inner.cancel_frame_callback(id);
1577 }
1578 }
1579
1580 pub fn drain_frame_callbacks(&self, frame_time_nanos: u64) {
1584 if let Some(inner) = self.inner.upgrade() {
1585 inner.drain_frame_callbacks(frame_time_nanos);
1586 }
1587 }
1588
1589 pub fn last_frame_time_nanos(&self) -> Option<u64> {
1592 self.inner
1593 .upgrade()
1594 .and_then(|inner| inner.last_frame_time_nanos.get())
1595 }
1596
1597 #[cfg(any(feature = "internal", test))]
1599 pub fn frame_clock(&self) -> FrameClock {
1600 FrameClock::new(self.clone())
1601 }
1602
1603 pub fn set_needs_frame(&self, value: bool) {
1605 if let Some(inner) = self.inner.upgrade() {
1606 *inner.needs_frame.borrow_mut() = value;
1607 }
1608 }
1609
1610 pub(crate) fn take_updates(&self) -> Vec<Command> {
1611 self.inner
1612 .upgrade()
1613 .map(|inner| inner.take_updates())
1614 .unwrap_or_default()
1615 }
1616
1617 pub fn has_updates(&self) -> bool {
1619 self.inner
1620 .upgrade()
1621 .is_some_and(|inner| inner.has_updates())
1622 }
1623
1624 pub(crate) fn scope_runtime(&self) -> ScopeRuntime {
1626 ScopeRuntime(Weak::clone(&self.inner))
1627 }
1628
1629 pub(crate) fn requeue_invalid_scope(&self, scope: &RecomposeScope) {
1630 if let Some(inner) = self.inner.upgrade() {
1631 inner.requeue_invalid_scope(scope);
1632 }
1633 }
1634
1635 pub(crate) fn take_invalidated_scopes(&self) -> Option<Vec<RecomposeScope>> {
1636 self.inner
1637 .upgrade()
1638 .and_then(|inner| inner.take_invalidated_scopes())
1639 }
1640
1641 pub(crate) fn recycle_scopes(&self, scopes: Vec<RecomposeScope>) {
1644 if let Some(inner) = self.inner.upgrade() {
1645 inner.recycle_scopes(scopes);
1646 }
1647 }
1648
1649 pub fn forget_movable(&self, id: Key) {
1653 if let Some(inner) = self.inner.upgrade() {
1654 inner.forgotten_movables.borrow_mut().push(id);
1655 inner.schedule();
1656 }
1657 }
1658
1659 pub(crate) fn take_forgotten_movables(&self) -> Vec<Key> {
1660 self.inner
1661 .upgrade()
1662 .map(|inner| std::mem::take(&mut *inner.forgotten_movables.borrow_mut()))
1663 .unwrap_or_default()
1664 }
1665
1666 pub(crate) fn next_movable_content_id(&self) -> Key {
1671 let Some(inner) = self.inner.upgrade() else {
1672 log::error!("movable content asked a runtime that is gone for an identity");
1673 return 0;
1674 };
1675 let id = inner.next_movable_content_id.get();
1676 inner.next_movable_content_id.set(id.wrapping_add(1).max(1));
1677 id
1678 }
1679
1680 pub fn has_invalid_scopes(&self) -> bool {
1682 self.inner
1683 .upgrade()
1684 .is_some_and(|inner| inner.has_invalid_scopes())
1685 }
1686
1687 fn live_recompose_scope_count(&self) -> usize {
1688 self.inner
1689 .upgrade()
1690 .map(|inner| inner.live_recompose_scope_count())
1691 .unwrap_or_default()
1692 }
1693
1694 #[doc(hidden)]
1695 pub fn debug_invalid_scope_ids(&self) -> Vec<usize> {
1696 self.inner
1697 .upgrade()
1698 .map(|inner| inner.queued_invalid_scope_ids())
1699 .unwrap_or_default()
1700 }
1701
1702 pub fn has_frame_callbacks(&self) -> bool {
1704 self.inner
1705 .upgrade()
1706 .is_some_and(|inner| inner.has_frame_callbacks())
1707 }
1708
1709 pub fn has_transient_frame_callbacks(&self) -> bool {
1712 self.inner
1713 .upgrade()
1714 .is_some_and(|inner| inner.has_transient_frame_callbacks())
1715 }
1716
1717 pub fn assert_ui_thread(&self) {
1719 debug_assert_eq!(
1720 std::thread::current().id(),
1721 self.ui_thread_id,
1722 "state mutated off the runtime's UI thread"
1723 );
1724 }
1725
1726 pub fn dispatcher(&self) -> UiDispatcher {
1728 self.dispatcher.clone()
1729 }
1730
1731 #[doc(hidden)]
1732 pub fn with_deferred_state_releases<R>(&self, f: impl FnOnce() -> R) -> R {
1733 let _scope = enter_state_teardown_scope();
1734 f()
1735 }
1736}
1737
1738impl TaskHandle {
1739 pub fn cancel(&self) {
1741 self.runtime.cancel_task(self.id);
1742 }
1743
1744 pub fn is_finished(&self) -> bool {
1746 !self.runtime.has_task(self.id)
1747 }
1748}
1749
1750pub(crate) struct FrameCallbackEntry {
1751 id: FrameCallbackId,
1752 kind: FrameCallbackKind,
1753 callback: FrameCallback,
1754}
1755
1756enum FrameCallback {
1757 Run(Box<dyn FnOnce(u64) + 'static>),
1759 #[cfg(any(feature = "internal", test))]
1761 Wake(Waker),
1762}
1763
1764#[cfg(not(target_arch = "wasm32"))]
1765struct RuntimeTaskWaker {
1766 scheduler: SchedulerRef,
1767 runnable: Arc<AtomicBool>,
1768 woken: Arc<AtomicBool>,
1769}
1770
1771#[cfg(target_arch = "wasm32")]
1772struct RuntimeTaskWaker {
1773 runtime_id: RuntimeId,
1774 runnable: Arc<AtomicBool>,
1775 woken: Arc<AtomicBool>,
1776}
1777
1778impl RuntimeTaskWaker {
1779 #[cfg(not(target_arch = "wasm32"))]
1780 fn new(inner: &RuntimeInner, runnable: Arc<AtomicBool>) -> Self {
1781 let scheduler = inner.scheduler.clone();
1782 Self {
1783 scheduler,
1784 runnable,
1785 woken: Arc::clone(&inner.task_woken),
1786 }
1787 }
1788
1789 #[cfg(target_arch = "wasm32")]
1790 fn new(inner: &RuntimeInner, runnable: Arc<AtomicBool>) -> Self {
1791 let runtime_id = inner.runtime_id;
1792 Self {
1793 runtime_id,
1794 runnable,
1795 woken: Arc::clone(&inner.task_woken),
1796 }
1797 }
1798
1799 fn into_waker(self) -> Waker {
1800 futures_task::waker(Arc::new(self))
1801 }
1802}
1803
1804impl futures_task::ArcWake for RuntimeTaskWaker {
1805 #[cfg(not(target_arch = "wasm32"))]
1806 fn wake_by_ref(arc_self: &Arc<Self>) {
1807 arc_self.runnable.store(true, Ordering::Release);
1808 arc_self.woken.store(true, Ordering::Release);
1809 arc_self.scheduler.schedule_frame();
1810 }
1811
1812 #[cfg(target_arch = "wasm32")]
1813 fn wake_by_ref(arc_self: &Arc<Self>) {
1814 arc_self.runnable.store(true, Ordering::Release);
1815 arc_self.woken.store(true, Ordering::Release);
1816 if let Some(handle) = runtime_handle_by_id(arc_self.runtime_id) {
1817 handle.schedule();
1818 }
1819 }
1820}
1821
1822#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
1825pub struct RuntimeThreadLocalDebugStats {
1826 pub active_runtimes_len: usize,
1828 pub active_runtimes_cap: usize,
1830 pub registered_runtimes_len: usize,
1832 pub registered_runtimes_cap: usize,
1834 pub deferred_state_releases_len: usize,
1836 pub deferred_state_releases_cap: usize,
1838}
1839
1840pub fn current_runtime_handle() -> Option<RuntimeHandle> {
1845 thread_state::try_runtimes(|state| {
1846 if let Some(handle) = state.active_runtimes.borrow().last() {
1847 return Some(handle.clone());
1848 }
1849 state.last_runtime.borrow().clone()
1850 })
1851 .flatten()
1852}
1853
1854pub(crate) fn runtime_handle_by_id(id: RuntimeId) -> Option<RuntimeHandle> {
1855 thread_state::try_runtimes(|state| state.registered_runtimes.borrow().get(&id).cloned())
1856 .flatten()
1857}
1858
1859#[cfg(target_os = "android")]
1860pub(crate) fn share_rescue<'a>(
1861 handles: impl Iterator<Item = &'a RuntimeHandle>,
1862) -> Option<Rc<thread_state::RescuedThreadState>> {
1863 let live: Vec<Rc<RuntimeInner>> = handles
1864 .filter_map(|handle| handle.inner.upgrade())
1865 .collect();
1866 let shared = live
1867 .iter()
1868 .find_map(|inner| inner.rescued.borrow().clone())
1869 .or_else(|| (!live.is_empty()).then(Rc::default))?;
1870 for inner in &live {
1871 *inner.rescued.borrow_mut() = Some(Rc::clone(&shared));
1872 }
1873 Some(shared)
1874}
1875
1876pub(crate) fn with_state_arena_by_id<R>(
1877 id: RuntimeId,
1878 f: impl FnOnce(&StateArena) -> R,
1879) -> Option<R> {
1880 let inner = thread_state::try_runtimes(|state| {
1881 state
1882 .registered_runtimes
1883 .borrow()
1884 .get(&id)
1885 .and_then(|handle| handle.inner.upgrade())
1886 })
1887 .flatten()?;
1888 Some(f(&inner.state_arena))
1889}
1890
1891pub(crate) fn live_recompose_scope_count() -> usize {
1892 thread_state::runtimes(|state| {
1893 state
1894 .registered_runtimes
1895 .borrow()
1896 .values()
1897 .map(RuntimeHandle::live_recompose_scope_count)
1898 .sum()
1899 })
1900}
1901
1902pub fn debug_runtime_thread_local_stats() -> RuntimeThreadLocalDebugStats {
1904 thread_state::runtimes(|state| {
1905 let active = state.active_runtimes.borrow();
1906 let registered = state.registered_runtimes.borrow();
1907 let deferred = state.deferred_state_releases.borrow();
1908 RuntimeThreadLocalDebugStats {
1909 active_runtimes_len: active.len(),
1910 active_runtimes_cap: active.capacity(),
1911 registered_runtimes_len: registered.len(),
1912 registered_runtimes_cap: registered.capacity(),
1913 deferred_state_releases_len: deferred.len(),
1914 deferred_state_releases_cap: deferred.capacity(),
1915 }
1916 })
1917}
1918
1919fn defer_state_release(runtime: &RuntimeHandle, id: StateId) {
1920 let deferred = thread_state::try_runtimes(|state| {
1921 if state.state_teardown_depth.get() == 0 {
1922 return false;
1923 }
1924 state
1925 .deferred_state_releases
1926 .borrow_mut()
1927 .push(DeferredStateRelease {
1928 runtime: Weak::clone(&runtime.inner),
1929 id,
1930 });
1931 true
1932 });
1933 if deferred != Some(true) {
1934 release_state(&runtime.inner, id);
1935 }
1936}
1937
1938fn release_state(runtime: &Weak<RuntimeInner>, id: StateId) {
1939 if let Some(inner) = runtime.upgrade() {
1940 inner.state_arena.release(id);
1941 }
1942}
1943
1944pub(crate) struct StateTeardownScope;
1945
1946pub(crate) fn enter_state_teardown_scope() -> StateTeardownScope {
1947 thread_state::try_runtimes(|state| {
1948 let depth = &state.state_teardown_depth;
1949 depth.set(depth.get() + 1);
1950 });
1951 StateTeardownScope
1952}
1953
1954impl Drop for StateTeardownScope {
1955 fn drop(&mut self) {
1956 let finished = thread_state::try_runtimes(|state| {
1957 let depth = &state.state_teardown_depth;
1958 let next = depth.get().saturating_sub(1);
1959 depth.set(next);
1960 if next == 0 {
1961 let mut releases = state.deferred_state_releases.borrow_mut();
1962 while let Some(deferred) = releases.pop() {
1963 release_state(&deferred.runtime, deferred.id);
1964 }
1965 }
1966 next == 0
1967 });
1968 if finished == Some(true) {
1969 thread_state::release_when_idle();
1970 }
1971 }
1972}
1973
1974pub(crate) fn push_active_runtime(handle: &RuntimeHandle) {
1975 thread_state::runtimes(|state| {
1976 state
1977 .registered_runtimes
1978 .borrow_mut()
1979 .insert(handle.id(), handle.clone());
1980 state.active_runtimes.borrow_mut().push(handle.clone());
1981 *state.last_runtime.borrow_mut() = Some(handle.clone());
1982 });
1983}
1984
1985pub(crate) fn pop_active_runtime() {
1986 thread_state::runtimes(|state| {
1987 state.active_runtimes.borrow_mut().pop();
1988 });
1989}
1990
1991pub fn schedule_frame() {
1993 if let Some(handle) = current_runtime_handle() {
1994 handle.schedule();
1995 return;
1996 }
1997 log::debug!(
1998 target: "cranpose::runtime",
1999 "ignoring frame request without an active runtime",
2000 );
2001}
2002
2003pub fn schedule_node_update(
2005 update: impl FnOnce(&mut dyn Applier) -> Result<(), NodeError> + 'static,
2006) {
2007 if let Some(handle) = current_runtime_handle() {
2008 handle.enqueue_node_update(Command::callback(update));
2009 } else {
2010 drop(update);
2011 log::debug!(
2012 target: "cranpose::runtime",
2013 "ignoring node update request without an active runtime",
2014 );
2015 }
2016}
2017
2018#[cfg(test)]
2019#[path = "tests/runtime_tests.rs"]
2020mod tests;