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