Skip to main content

cranpose_core/
runtime.rs

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    /// The derived states among the cells, by slot.
73    derived: HashMap<u32, Weak<dyn DerivedRefresh>>,
74}
75
76/// A derived state that brings its value up to date before a read.
77pub(crate) trait DerivedRefresh {
78    /// Derives the value again when a state it read changed since the
79    /// recomposition that last derived it.
80    fn refresh(&self);
81}
82
83/// Sizes of a runtime's state arena, for leak and memory checks. A `_len` field counts entries and
84/// a `_cap` field the capacity reserved.
85#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
86pub struct StateArenaDebugStats {
87    /// State cells, both in use and free.
88    pub cells_len: usize,
89    /// Capacity reserved for the state cells.
90    pub cells_cap: usize,
91    /// State cells free for reuse.
92    pub free_len: usize,
93    /// Capacity reserved for the free list.
94    pub free_cap: usize,
95}
96
97/// Sizes of a runtime's queues and tables, for leak and memory checks. A `_len` field counts
98/// entries and a `_cap` field the capacity reserved.
99#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
100pub struct RuntimeDebugStats {
101    /// Node updates queued and not yet applied.
102    pub node_updates_len: usize,
103    /// Capacity reserved for the node updates.
104    pub node_updates_cap: usize,
105    /// Recompose scopes that were invalidated and not yet recomposed.
106    pub invalid_scopes_len: usize,
107    /// Entries in the queue of invalidated scopes.
108    pub scope_queue_len: usize,
109    /// Capacity reserved for the invalid scope queue.
110    pub scope_queue_cap: usize,
111    /// Frame callbacks waiting for the next frame.
112    pub frame_callbacks_len: usize,
113    /// Capacity reserved for the frame callbacks.
114    pub frame_callbacks_cap: usize,
115    /// UI-thread closures queued by `enqueue_ui_task` and not yet run.
116    pub local_tasks_len: usize,
117    /// Capacity reserved for the local tasks.
118    pub local_tasks_cap: usize,
119    /// UI continuations registered and waiting for a value.
120    pub ui_conts_len: usize,
121    /// Capacity reserved for the UI continuations.
122    pub ui_conts_cap: usize,
123    /// Futures from `spawn_ui` that have not finished.
124    pub tasks_len: usize,
125    /// Capacity reserved for the spawned tasks.
126    pub tasks_cap: usize,
127    /// States kept alive on behalf of the runtime itself, not of a Rust owner.
128    pub external_state_owners_len: usize,
129    /// Capacity reserved for the external state owners.
130    pub external_state_owners_cap: usize,
131    /// Messages posted from other threads that the UI thread has not run yet.
132    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    /// Makes `refresh` what brings the derived state `id` up to date before
266    /// each read, until the state is released.
267    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    /// Brings the derived state `id` up to date before a read; see
272    /// [`DerivedRefresh`].
273    #[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    /// Stops the state `id` from recomposing the live scope `scope_id`.
311    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/// Names one state cell of a runtime. A generation count makes the id of a freed cell stale.
349#[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/// Identifies a runtime within its thread.
374#[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/// Posts work from any thread to the UI thread of a runtime.
462#[derive(Clone)]
463pub struct UiDispatcher {
464    inner: UiDispatcherRef,
465}
466
467impl UiDispatcher {
468    fn new(inner: UiDispatcherRef) -> Self {
469        Self { inner }
470    }
471
472    /// Queues `task` to run on the UI thread the next time the runtime drains UI work. May be
473    /// called from any thread.
474    pub fn post(&self, task: impl FnOnce() + Send + 'static) {
475        self.inner.post(task);
476    }
477
478    /// Queues a call of the continuation registered under `id` with `value`, to run on the UI
479    /// thread. May be called from any thread.
480    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    /// Returns `true` if a posted message has not run yet.
488    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    /// The storage of the last pass's scope list, for the next pass.
500    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    /// Set by every task wake after the task's own flag, and cleared by the
513    /// poll that then looks at the tasks: a poll after no wake skips them.
514    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
533/// Names the next task that `spawn_ui` creates on this thread, for `live_ui_task_labels` and leak
534/// reports. A task with no label is called `unnamed`.
535pub 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        // An empty queue keeps its storage for the next invalidation.
615        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        // Cleared before the borrow: dropping a scope may run any code.
644        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        // Without a wake no task can make progress, and the order stays.
735        if !self.task_woken.load(Ordering::Acquire) {
736            return false;
737        }
738        // The tasks still pending move to the front of `order`, which goes
739        // back as the task order, after any task spawned while polling.
740        let mut order = std::mem::take(&mut *self.task_order.borrow_mut());
741        // An empty order is a poll nested in another, which holds the order:
742        // the wake flag stays for the polls after it.
743        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            // A message counts as pending from before it is sent until it
812            // ran, so none waits while none is pending.
813            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            // The tasks just polled may have queued work of their own, such
837            // as a state observer's change notice for a state they wrote. It
838            // belongs to this drain, or a frame that resumed an animation
839            // would draw the value it wrote only on the next frame. It does
840            // not count as progress: a task writing state on every poll must
841            // not keep the drain polling it.
842            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    /// A finished task's waker may still wake after the task is gone; with
874    /// no task left that wake is stale.
875    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    /// The frame time once the frame callback `id` has run. A waiting
943    /// task's entry takes `waker` when the one it holds would wake another
944    /// task.
945    #[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        // Callbacks write state in one snapshot. Waking a task writes none:
1015        // a frame of only waiting tasks opens no snapshot.
1016        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/// Owns the state cells, UI tasks, frame callbacks and invalidated scopes of one composition.
1061/// Clones share the same runtime.
1062#[derive(Clone)]
1063pub struct Runtime {
1064    inner: Rc<RuntimeInner>,
1065}
1066
1067impl Runtime {
1068    /// Creates a runtime whose UI thread is the current thread. `scheduler` is asked for a frame
1069    /// whenever the runtime has work.
1070    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    /// Returns a handle that reaches this runtime without keeping it alive.
1093    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    /// Returns `true` if node updates are queued or any recompose scope is invalid.
1103    pub fn has_updates(&self) -> bool {
1104        self.inner.has_updates()
1105    }
1106
1107    /// Returns `true` if the runtime has asked for a frame or has a task ready to run.
1108    pub fn needs_frame(&self) -> bool {
1109        *self.inner.needs_frame.borrow() || self.inner.has_runnable_tasks()
1110    }
1111
1112    /// Sets or clears the request for a frame.
1113    pub fn set_needs_frame(&self, value: bool) {
1114        *self.inner.needs_frame.borrow_mut() = value;
1115    }
1116
1117    /// Animation-clock time of the most recent frame-callback drain. This is
1118    /// the same clock `Animatable`s advance on (virtual under robot exact
1119    /// captures), so per-frame integrators derive dt from it instead of wall
1120    /// time.
1121    pub fn last_frame_time_nanos(&self) -> Option<u64> {
1122        self.inner.last_frame_time_nanos.get()
1123    }
1124
1125    /// Returns a clock for the frames of this runtime.
1126    #[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/// A scheduler that ignores frame requests, for compositions that the caller drives itself.
1150#[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/// A cheap handle to a [`Runtime`]. It does not keep the runtime alive, and its calls do nothing
1192/// once the runtime is dropped.
1193#[derive(Clone)]
1194pub struct RuntimeHandle {
1195    inner: Weak<RuntimeInner>,
1196    dispatcher: UiDispatcher,
1197    ui_thread_id: ThreadId,
1198    id: RuntimeId,
1199}
1200
1201/// What a recompose scope keeps of its runtime: one word, where a
1202/// [`RuntimeHandle`] takes four, on every scope of a composition. Each call
1203/// does nothing once the runtime is gone, as the handle's would.
1204pub(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
1244/// Controls a future spawned with `RuntimeHandle::spawn_ui`.
1245pub 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
1284/// Runs `build` with every state it creates owned by the caller, and returns
1285/// those states beside its value.
1286///
1287/// This is what makes the Jetpack Compose shape safe here:
1288///
1289/// ```rust,ignore
1290/// remember(|| Holder {
1291///     count: mutableStateOf(0),
1292/// })
1293/// ```
1294///
1295/// Kotlin leaves the state to the garbage collector, which frees it with the
1296/// object that holds it. Nothing collects here, so a state with no owner has
1297/// to be kept by the runtime for as long as the runtime lives, and a holder
1298/// built once per screen would pile up cells nobody can reach. Handing the
1299/// states to whoever is building the value puts them back on the object's
1300/// lifetime: the slot drops the value, the value drops the states.
1301pub(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    /// Returns the id of the runtime.
1329    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    /// Returns the number of state cells and, of those, the number free for reuse.
1399    pub fn state_arena_stats(&self) -> (usize, usize) {
1400        self.try_with_state_arena(StateArena::stats)
1401            .unwrap_or_default()
1402    }
1403
1404    /// Returns the sizes of the runtime's state arena, for leak and memory checks.
1405    pub fn state_arena_debug_stats(&self) -> StateArenaDebugStats {
1406        self.try_with_state_arena(StateArena::debug_stats)
1407            .unwrap_or_default()
1408    }
1409
1410    /// Returns the sizes of the runtime's queues and tables, for leak and memory checks.
1411    pub fn debug_stats(&self) -> RuntimeDebugStats {
1412        self.inner
1413            .upgrade()
1414            .map(|inner| inner.debug_stats())
1415            .unwrap_or_default()
1416    }
1417
1418    /// Returns the id and label of every spawned UI task that has not finished.
1419    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    /// Marks the runtime as needing a frame and asks its scheduler for one.
1434    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    /// Schedules work that must run on the runtime thread.
1447    ///
1448    /// The closure executes on the UI thread immediately when the runtime
1449    /// drains its local queue, so it may capture `Rc`/`RefCell` values. Calling
1450    /// this from any other thread is a logic error and will panic in debug
1451    /// builds via the inner assertion.
1452    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    /// Spawns `fut` on the UI thread, where `drain_ui` polls it whenever it is woken. Returns
1461    /// `None` if the runtime is gone.
1462    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    /// Cancels the spawned UI task `id` and drops its future.
1476    pub fn cancel_task(&self, id: u64) {
1477        if let Some(inner) = self.inner.upgrade() {
1478            inner.cancel_task(id);
1479        }
1480    }
1481
1482    /// Whether the runtime still holds the spawned task `id`.
1483    pub fn has_task(&self, id: u64) -> bool {
1484        self.inner.upgrade().is_some_and(|inner| inner.has_task(id))
1485    }
1486
1487    /// Enqueues work from any thread to run on the UI thread.
1488    ///
1489    /// The closure must be `Send` because it may cross threads before executing
1490    /// on the runtime thread. Use this when posting from background work.
1491    pub fn post_ui(&self, task: impl FnOnce() + Send + 'static) {
1492        self.dispatcher.post(task);
1493    }
1494
1495    /// Registers `f` to run once on the UI thread when `UiDispatcher::post_invoke` sends a `T` for
1496    /// the returned id. Returns `None` if the runtime is gone.
1497    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    /// Removes the continuation `id` without running it.
1502    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    /// Runs the work posted to the UI thread, including woken tasks, until none is left.
1509    pub fn drain_ui(&self) {
1510        if let Some(inner) = self.inner.upgrade() {
1511            inner.drain_ui();
1512        }
1513    }
1514
1515    /// Returns `true` if posted UI work has not run yet.
1516    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    /// Registers `callback` to run once on the next frame with the frame time in nanoseconds.
1524    /// Returns `None` if the runtime is gone.
1525    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    /// Registers `callback` to run once on the next frame, for a long-lived loop that registers
1538    /// again after every frame. It does not count in `has_transient_frame_callbacks`.
1539    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    /// Wakes the task of `waker` at the next frame, whose time it then reads
1552    /// with [`Self::frame_waker_time`].
1553    #[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    /// The frame time once the frame waker `id` has run; until then its
1565    /// entry wakes `waker`.
1566    #[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    /// Cancels the frame callback `id` if it has not run.
1574    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    /// Delivers callbacks registered before this display frame. Timestamps use
1581    /// one monotonic nanosecond epoch; duplicate or older timestamps are ignored.
1582    /// A callback registered during delivery waits for a newer frame timestamp.
1583    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    /// Animation-clock time of the most recent frame-callback drain (see
1590    /// [`Runtime::last_frame_time_nanos`]).
1591    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    /// Returns a clock for the frames of this runtime.
1598    #[cfg(any(feature = "internal", test))]
1599    pub fn frame_clock(&self) -> FrameClock {
1600        FrameClock::new(self.clone())
1601    }
1602
1603    /// Sets or clears the request for a frame.
1604    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    /// Returns `true` if node updates are queued or any recompose scope is invalid.
1618    pub fn has_updates(&self) -> bool {
1619        self.inner
1620            .upgrade()
1621            .is_some_and(|inner| inner.has_updates())
1622    }
1623
1624    /// The link a recompose scope keeps to this runtime.
1625    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    /// Gives back a scope list from [`Self::take_invalidated_scopes`], for
1642    /// the next pass to reuse its storage.
1643    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    /// Releases the retained state of the movable content with identity
1650    /// `id` at the composition's next opportunity. See
1651    /// [`crate::forget_movable`].
1652    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    /// An identity for a piece of movable content, unique within this
1667    /// runtime. Owned by the runtime instance rather than a process-wide
1668    /// counter, so two compositions in one process cannot collide and a
1669    /// test cannot be made to pass by the order it happens to run in.
1670    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    /// Returns `true` if a recompose scope has been invalidated and not yet recomposed.
1681    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    /// Returns `true` if any frame callback, transient or perpetual, is waiting.
1703    pub fn has_frame_callbacks(&self) -> bool {
1704        self.inner
1705            .upgrade()
1706            .is_some_and(|inner| inner.has_frame_callbacks())
1707    }
1708
1709    /// Returns `true` if a callback from `register_frame_callback` is waiting. Perpetual callbacks
1710    /// do not count.
1711    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    /// Panics in debug builds when called off the runtime's UI thread.
1718    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    /// Returns a dispatcher that posts work to the UI thread from any thread.
1727    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    /// Cancels the task if it has not finished.
1740    pub fn cancel(&self) {
1741        self.runtime.cancel_task(self.id);
1742    }
1743
1744    /// Whether the spawned future has finished or been cancelled.
1745    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    /// Runs with the frame time.
1758    Run(Box<dyn FnOnce(u64) + 'static>),
1759    /// Wakes a task awaiting the frame.
1760    #[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/// Sizes of the runtime registries of one thread, for leak and memory checks. A `_len` field counts
1823/// entries and a `_cap` field the capacity reserved.
1824#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
1825pub struct RuntimeThreadLocalDebugStats {
1826    /// Runtimes currently installed as active on this thread.
1827    pub active_runtimes_len: usize,
1828    /// Capacity reserved for the active runtimes.
1829    pub active_runtimes_cap: usize,
1830    /// Runtimes registered on this thread.
1831    pub registered_runtimes_len: usize,
1832    /// Capacity reserved for the registered runtimes.
1833    pub registered_runtimes_cap: usize,
1834    /// State releases waiting for the end of a teardown scope.
1835    pub deferred_state_releases_len: usize,
1836    /// Capacity reserved for the deferred state releases.
1837    pub deferred_state_releases_cap: usize,
1838}
1839
1840/// Gets the current runtime handle from thread-local storage.
1841///
1842/// Returns the most recently pushed active runtime, or the last known runtime.
1843/// Used by fling animation and other components that need access to the runtime.
1844pub 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
1902/// Returns the sizes of the runtime registries of the current thread, for leak and memory checks.
1903pub 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
1991/// Schedule a new frame render using the most recently active runtime handle.
1992pub 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
2003/// Schedule an in-place node update using the most recently active runtime.
2004pub 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;