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}
49
50impl<T: Clone + 'static> ScopeWatchCell for TypedStateCell<T> {
51 fn unregister_scope(&self, scope_id: ScopeId) {
52 self.inner.unregister_scope(scope_id);
53 }
54}
55
56struct StateArenaSlot {
57 generation: u32,
58 cell: Option<Rc<dyn Any>>,
59 watcher_cell: Option<Rc<dyn ScopeWatchCell>>,
60 lease: Option<Weak<StateHandleLease>>,
61}
62
63#[derive(Default)]
64struct StateArenaInner {
65 cells: Vec<StateArenaSlot>,
66 free: Vec<u32>,
67}
68
69#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
70pub struct StateArenaDebugStats {
71 pub cells_len: usize,
72 pub cells_cap: usize,
73 pub free_len: usize,
74 pub free_cap: usize,
75}
76
77#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
78pub struct RuntimeDebugStats {
79 pub node_updates_len: usize,
80 pub node_updates_cap: usize,
81 pub invalid_scopes_len: usize,
82 pub scope_queue_len: usize,
83 pub scope_queue_cap: usize,
84 pub frame_callbacks_len: usize,
85 pub frame_callbacks_cap: usize,
86 pub local_tasks_len: usize,
87 pub local_tasks_cap: usize,
88 pub ui_conts_len: usize,
89 pub ui_conts_cap: usize,
90 pub tasks_len: usize,
91 pub tasks_cap: usize,
92 pub external_state_owners_len: usize,
93 pub external_state_owners_cap: usize,
94 pub ui_dispatcher_pending: usize,
95}
96
97#[derive(Default)]
98pub(crate) struct StateArena {
99 inner: RefCell<StateArenaInner>,
100}
101
102impl StateArena {
103 pub(crate) fn alloc<T: Clone + 'static>(&self, value: T, runtime: RuntimeHandle) -> StateId {
104 self.alloc_with_policy(value, runtime, Rc::new(NeverEqual))
105 }
106
107 pub(crate) fn alloc_with_policy<T: Clone + 'static>(
108 &self,
109 value: T,
110 runtime: RuntimeHandle,
111 policy: Rc<dyn MutationPolicy<T>>,
112 ) -> StateId {
113 let (slot, generation) = {
114 let mut inner = self.inner.borrow_mut();
115 loop {
116 let Some(slot) = inner.free.pop() else {
117 let slot = inner.cells.len() as u32;
118 inner.cells.push(StateArenaSlot {
119 generation: 0,
120 cell: None,
121 watcher_cell: None,
122 lease: None,
123 });
124 break (slot, 0);
125 };
126
127 let Some(entry) = inner.cells.get_mut(slot as usize) else {
128 continue;
129 };
130 if entry.cell.is_some() {
131 continue;
132 }
133
134 entry.watcher_cell = None;
135 entry.lease = None;
136 entry.generation = entry.generation.wrapping_add(1);
137 break (slot, entry.generation);
138 }
139 };
140 let id = StateId::new(slot, generation);
141 let inner = MutableStateInner::new_with_policy(value, runtime, policy);
142 inner.install_snapshot_observer(id);
143 let typed_cell = Rc::new(TypedStateCell { inner });
144 let cell: Rc<dyn Any> = typed_cell.clone();
145 let watcher_cell: Rc<dyn ScopeWatchCell> = typed_cell;
146 let mut arena = self.inner.borrow_mut();
147 let slot_entry = &mut arena.cells[slot as usize];
148 slot_entry.cell = Some(cell);
149 slot_entry.watcher_cell = Some(watcher_cell);
150 id
151 }
152
153 fn get_cell_opt(&self, id: StateId) -> Option<Rc<dyn Any>> {
154 self.inner
155 .borrow()
156 .cells
157 .get(id.slot_index())
158 .filter(|cell| cell.generation == id.generation())
159 .and_then(|cell| cell.cell.as_ref())
160 .cloned()
161 }
162
163 fn get_typed<T: Clone + 'static>(&self, id: StateId) -> Rc<TypedStateCell<T>> {
164 match self.get_cell_opt(id) {
165 None => panic!(
166 "state cell missing: slot={}, gen={}, expected={}",
167 id.slot(),
168 id.generation(),
169 std::any::type_name::<T>(),
170 ),
171 Some(cell) => Rc::downcast::<TypedStateCell<T>>(cell).unwrap_or_else(|_| {
172 panic!(
173 "state cell type mismatch: slot={}, gen={}, expected={}",
174 id.slot(),
175 id.generation(),
176 std::any::type_name::<T>(),
177 )
178 }),
179 }
180 }
181
182 fn get_typed_opt<T: Clone + 'static>(&self, id: StateId) -> Option<Rc<TypedStateCell<T>>> {
183 Rc::downcast::<TypedStateCell<T>>(self.get_cell_opt(id)?).ok()
184 }
185
186 pub(crate) fn with_typed<T: Clone + 'static, R>(
187 &self,
188 id: StateId,
189 f: impl FnOnce(&MutableStateInner<T>) -> R,
190 ) -> R {
191 let cell = self.get_typed::<T>(id);
192 f(&cell.inner)
193 }
194
195 pub(crate) fn with_typed_opt<T: Clone + 'static, R>(
196 &self,
197 id: StateId,
198 f: impl FnOnce(&MutableStateInner<T>) -> R,
199 ) -> Option<R> {
200 let cell = self.get_typed_opt::<T>(id)?;
201 Some(f(&cell.inner))
202 }
203
204 pub(crate) fn release(&self, id: StateId) {
205 let cell = {
206 let mut inner = self.inner.borrow_mut();
207 let Some(slot) = inner.cells.get_mut(id.slot_index()) else {
208 return;
209 };
210 if slot.generation != id.generation() {
211 return;
212 }
213 slot.lease = None;
214 slot.watcher_cell = None;
215 let cell = slot.cell.take();
216 if cell.is_some() {
217 inner.free.push(id.slot());
218 }
219 cell
220 };
221 drop(cell);
222 }
223
224 pub(crate) fn stats(&self) -> (usize, usize) {
225 let inner = self.inner.borrow();
226 (inner.cells.len(), inner.free.len())
227 }
228
229 pub(crate) fn debug_stats(&self) -> StateArenaDebugStats {
230 let inner = self.inner.borrow();
231 StateArenaDebugStats {
232 cells_len: inner.cells.len(),
233 cells_cap: inner.cells.capacity(),
234 free_len: inner.free.len(),
235 free_cap: inner.free.capacity(),
236 }
237 }
238
239 pub(crate) fn unregister_scope(&self, id: StateId, scope_id: ScopeId) {
240 let watcher_cell = {
241 let inner = self.inner.borrow();
242 inner
243 .cells
244 .get(id.slot_index())
245 .filter(|slot| slot.generation == id.generation())
246 .and_then(|slot| slot.watcher_cell.as_ref())
247 .cloned()
248 };
249 if let Some(watcher_cell) = watcher_cell {
250 watcher_cell.unregister_scope(scope_id);
251 }
252 }
253
254 pub(crate) fn register_lease(&self, id: StateId, lease: &Rc<StateHandleLease>) {
255 let mut inner = self.inner.borrow_mut();
256 let Some(slot) = inner.cells.get_mut(id.slot_index()) else {
257 return;
258 };
259 if slot.generation != id.generation() {
260 return;
261 }
262 slot.lease = Some(Rc::downgrade(lease));
263 }
264
265 pub(crate) fn retain_lease(&self, id: StateId) -> Option<Rc<StateHandleLease>> {
266 let inner = self.inner.borrow();
267 let slot = inner.cells.get(id.slot_index())?;
268 if slot.generation != id.generation() {
269 return None;
270 }
271 slot.lease.as_ref()?.upgrade()
272 }
273}
274
275#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
276pub struct StateId {
277 slot: u32,
278 generation: u32,
279}
280
281impl StateId {
282 const fn new(slot: u32, generation: u32) -> Self {
283 Self { slot, generation }
284 }
285
286 pub(crate) const fn slot(self) -> u32 {
287 self.slot
288 }
289
290 pub(crate) const fn slot_index(self) -> usize {
291 self.slot as usize
292 }
293
294 pub(crate) const fn generation(self) -> u32 {
295 self.generation
296 }
297}
298
299#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
300pub struct RuntimeId(u32);
301
302impl RuntimeId {
303 fn next() -> Self {
304 thread_state::runtimes(|state| {
305 let id = state.next_runtime_id.get();
306 state.next_runtime_id.set(id.wrapping_add(1));
307 Self(id)
308 })
309 }
310}
311
312struct UiDispatcherInner {
313 scheduler: SchedulerRef,
314 tx: mpsc::Sender<UiMessage>,
315 pending: AtomicUsize,
316}
317
318#[cfg(not(target_arch = "wasm32"))]
319type UiDispatcherRef = Arc<UiDispatcherInner>;
320
321#[cfg(target_arch = "wasm32")]
322type UiDispatcherRef = Rc<UiDispatcherInner>;
323
324impl UiDispatcherInner {
325 fn new(scheduler: SchedulerRef, tx: mpsc::Sender<UiMessage>) -> Self {
326 Self {
327 scheduler,
328 tx,
329 pending: AtomicUsize::new(0),
330 }
331 }
332
333 fn post(&self, task: impl FnOnce() + Send + 'static) {
334 self.pending.fetch_add(1, Ordering::SeqCst);
335 if self.tx.send(UiMessage::Task(Box::new(task))).is_ok() {
336 self.scheduler.schedule_frame();
337 } else {
338 self.pending.fetch_sub(1, Ordering::SeqCst);
339 }
340 }
341
342 fn post_invoke(&self, id: u64, value: Box<dyn Any + Send>) {
343 self.pending.fetch_add(1, Ordering::SeqCst);
344 if self.tx.send(UiMessage::Invoke { id, value }).is_ok() {
345 self.scheduler.schedule_frame();
346 } else {
347 self.pending.fetch_sub(1, Ordering::SeqCst);
348 }
349 }
350
351 fn has_pending(&self) -> bool {
352 self.pending.load(Ordering::SeqCst) > 0
353 }
354}
355
356struct PendingGuard<'a> {
357 counter: &'a AtomicUsize,
358}
359
360impl<'a> PendingGuard<'a> {
361 fn new(counter: &'a AtomicUsize) -> Self {
362 Self { counter }
363 }
364}
365
366impl Drop for PendingGuard<'_> {
367 fn drop(&mut self) {
368 let mut current = self.counter.load(Ordering::SeqCst);
369 loop {
370 if current == 0 {
371 return;
372 }
373 match self.counter.compare_exchange(
374 current,
375 current - 1,
376 Ordering::SeqCst,
377 Ordering::SeqCst,
378 ) {
379 Ok(_) => return,
380 Err(next) => current = next,
381 }
382 }
383 }
384}
385
386#[derive(Clone)]
387pub struct UiDispatcher {
388 inner: UiDispatcherRef,
389}
390
391impl UiDispatcher {
392 fn new(inner: UiDispatcherRef) -> Self {
393 Self { inner }
394 }
395
396 pub fn post(&self, task: impl FnOnce() + Send + 'static) {
397 self.inner.post(task);
398 }
399
400 pub fn post_invoke<T>(&self, id: u64, value: T)
401 where
402 T: Send + 'static,
403 {
404 self.inner.post_invoke(id, Box::new(value));
405 }
406
407 pub fn has_pending(&self) -> bool {
408 self.inner.has_pending()
409 }
410}
411
412struct RuntimeInner {
413 scheduler: SchedulerRef,
414 needs_frame: RefCell<bool>,
415 node_updates: RefCell<Vec<Command>>,
416 invalid_scope_count: Cell<usize>,
417 scope_queue: RefCell<Vec<Weak<RecomposeScopeInner>>>,
418 frame_callbacks: RefCell<VecDeque<FrameCallbackEntry>>,
419 next_frame_callback_id: Cell<u64>,
420 last_frame_time_nanos: Cell<Option<u64>>,
421 ui_dispatcher: UiDispatcherRef,
422 ui_rx: RefCell<mpsc::Receiver<UiMessage>>,
423 local_tasks: RefCell<VecDeque<Box<dyn FnOnce() + 'static>>>,
424 ui_conts: RefCell<UiContinuationMap>,
425 next_cont_id: Cell<u64>,
426 ui_thread_id: ThreadId,
427 tasks: RefCell<HashMap<u64, TaskEntry>>,
428 task_order: RefCell<Vec<u64>>,
429 next_task_id: Cell<u64>,
430 state_arena: StateArena,
431 external_state_owners: RefCell<HashMap<StateId, Rc<StateHandleLease>>>,
432 live_recompose_scope_count: Cell<usize>,
433 forgotten_movables: RefCell<Vec<Key>>,
434 next_movable_content_id: Cell<u64>,
435 runtime_id: RuntimeId,
436 #[cfg(target_os = "android")]
437 rescued: RefCell<Option<Rc<thread_state::RescuedThreadState>>>,
438}
439
440struct TaskEntry {
441 label: String,
442 future: Option<Pin<Box<dyn Future<Output = ()> + 'static>>>,
443 runnable: Arc<AtomicBool>,
444 waker: Waker,
445}
446
447pub fn label_next_ui_task(label: impl Into<String>) {
448 thread_state::watch_thread_exit();
449 let label = label.into();
450 thread_state::runtimes(|state| *state.next_task_label.borrow_mut() = Some(label));
451}
452
453impl RuntimeInner {
454 fn new(scheduler: SchedulerRef) -> Self {
455 let (tx, rx) = mpsc::channel();
456 let dispatcher = UiDispatcherRef::new(UiDispatcherInner::new(scheduler.clone(), tx));
457 Self {
458 scheduler,
459 needs_frame: RefCell::new(false),
460 node_updates: RefCell::new(Vec::new()),
461 invalid_scope_count: Cell::new(0),
462 scope_queue: RefCell::new(Vec::new()),
463 frame_callbacks: RefCell::new(VecDeque::new()),
464 next_frame_callback_id: Cell::new(1),
465 last_frame_time_nanos: Cell::new(None),
466 ui_dispatcher: dispatcher,
467 ui_rx: RefCell::new(rx),
468 local_tasks: RefCell::new(VecDeque::new()),
469 ui_conts: RefCell::new(UiContinuationMap::default()),
470 next_cont_id: Cell::new(1),
471 ui_thread_id: std::thread::current().id(),
472 tasks: RefCell::new(HashMap::default()),
473 task_order: RefCell::new(Vec::new()),
474 next_task_id: Cell::new(1),
475 state_arena: StateArena::default(),
476 external_state_owners: RefCell::new(HashMap::default()),
477 live_recompose_scope_count: Cell::new(0),
478 forgotten_movables: RefCell::new(Vec::new()),
479 next_movable_content_id: Cell::new(1),
480 runtime_id: RuntimeId::next(),
481 #[cfg(target_os = "android")]
482 rescued: RefCell::new(None),
483 }
484 }
485
486 fn schedule(&self) {
487 *self.needs_frame.borrow_mut() = true;
488 self.scheduler.schedule_frame();
489 }
490
491 fn enqueue_update(&self, command: Command) {
492 self.node_updates.borrow_mut().push(command);
493 self.schedule();
494 }
495
496 fn take_updates(&self) -> Vec<Command> {
497 self.node_updates.borrow_mut().drain(..).collect::<Vec<_>>()
498 }
499
500 fn has_updates(&self) -> bool {
501 !self.node_updates.borrow().is_empty() || self.has_invalid_scopes()
502 }
503
504 fn register_invalid_scope(&self, scope: Weak<RecomposeScopeInner>) {
505 self.invalid_scope_count
506 .set(self.invalid_scope_count.get() + 1);
507 self.scope_queue.borrow_mut().push(scope);
508 self.schedule();
509 }
510
511 fn requeue_invalid_scope(&self, scope: &RecomposeScope) {
512 if scope.is_enqueued() {
513 self.scope_queue.borrow_mut().push(scope.downgrade());
514 self.schedule();
515 }
516 }
517
518 fn mark_scope_recomposed(&self) {
519 self.invalid_scope_count
520 .set(self.invalid_scope_count.get().saturating_sub(1));
521 }
522
523 fn take_invalidated_scopes(&self) -> Option<Vec<RecomposeScope>> {
524 let mut pending = std::mem::take(&mut *self.scope_queue.borrow_mut());
525 if pending.is_empty() {
526 return None;
527 }
528 let scopes: Vec<RecomposeScope> = pending
529 .iter()
530 .filter_map(RecomposeScope::upgrade)
531 .filter(RecomposeScope::is_enqueued)
532 .collect();
533 pending.clear();
534 let mut queue = self.scope_queue.borrow_mut();
535 if queue.is_empty() {
536 std::mem::swap(&mut *queue, &mut pending);
537 }
538 drop(queue);
539 (!scopes.is_empty()).then_some(scopes)
540 }
541
542 fn has_invalid_scopes(&self) -> bool {
543 self.invalid_scope_count.get() != 0
544 }
545
546 fn queued_invalid_scope_ids(&self) -> Vec<ScopeId> {
547 let mut ids: Vec<ScopeId> = self
548 .scope_queue
549 .borrow()
550 .iter()
551 .filter_map(RecomposeScope::upgrade)
552 .filter(RecomposeScope::is_enqueued)
553 .map(|scope| scope.id())
554 .collect();
555 ids.sort_unstable();
556 ids.dedup();
557 ids
558 }
559
560 fn increment_live_recompose_scope_count(&self) {
561 self.live_recompose_scope_count
562 .set(self.live_recompose_scope_count.get().saturating_add(1));
563 }
564
565 fn decrement_live_recompose_scope_count(&self) {
566 self.live_recompose_scope_count
567 .set(self.live_recompose_scope_count.get().saturating_sub(1));
568 }
569
570 fn live_recompose_scope_count(&self) -> usize {
571 self.live_recompose_scope_count.get()
572 }
573
574 fn has_frame_callbacks(&self) -> bool {
575 !self.frame_callbacks.borrow().is_empty()
576 }
577
578 fn has_transient_frame_callbacks(&self) -> bool {
579 self.frame_callbacks
580 .borrow()
581 .iter()
582 .any(|entry| entry.kind == FrameCallbackKind::Transient)
583 }
584
585 fn enqueue_ui_task(&self, task: Box<dyn FnOnce() + 'static>) {
586 self.local_tasks.borrow_mut().push_back(task);
587 self.schedule();
588 }
589
590 fn spawn_ui_task(&self, future: Pin<Box<dyn Future<Output = ()> + 'static>>) -> u64 {
591 let id = self.next_task_id.get();
592 self.next_task_id.set(id + 1);
593 let label = thread_state::runtimes(|state| state.next_task_label.borrow_mut().take())
594 .unwrap_or_else(|| "unnamed".to_string());
595 let runnable = Arc::new(AtomicBool::new(true));
596 let waker = RuntimeTaskWaker::new(self, Arc::clone(&runnable)).into_waker();
597 self.tasks.borrow_mut().insert(
598 id,
599 TaskEntry {
600 label,
601 future: Some(future),
602 runnable,
603 waker,
604 },
605 );
606 self.task_order.borrow_mut().push(id);
607 self.schedule();
608 id
609 }
610
611 fn cancel_task(&self, id: u64) {
612 let task = self.tasks.borrow_mut().remove(&id);
613 self.task_order.borrow_mut().retain(|queued| *queued != id);
614 drop(task);
615 }
616
617 fn has_task(&self, id: u64) -> bool {
618 self.tasks
619 .try_borrow()
620 .map_or(true, |tasks| tasks.contains_key(&id))
621 }
622
623 fn poll_async_tasks(&self) -> bool {
624 let order = std::mem::take(&mut *self.task_order.borrow_mut());
625 let mut pending = Vec::with_capacity(order.len());
626 let mut made_progress = false;
627 for id in order {
628 let task = {
629 let mut tasks = self.tasks.borrow_mut();
630 let Some(entry) = tasks.get_mut(&id) else {
631 continue;
632 };
633 if entry.runnable.swap(false, Ordering::AcqRel) {
634 entry
635 .future
636 .take()
637 .map(|future| (future, entry.waker.clone()))
638 } else {
639 None
640 }
641 };
642 let Some((mut future, waker)) = task else {
643 pending.push(id);
644 continue;
645 };
646 let mut cx = Context::from_waker(&waker);
647 match future.as_mut().poll(&mut cx) {
648 Poll::Ready(()) => {
649 self.cancel_task(id);
650 made_progress = true;
651 }
652 Poll::Pending => {
653 let mut tasks = self.tasks.borrow_mut();
654 if let Some(entry) = tasks.get_mut(&id) {
655 entry.future = Some(future);
656 pending.push(id);
657 } else {
658 drop(tasks);
659 drop(future);
660 }
661 }
662 }
663 }
664 if !pending.is_empty() {
665 pending.retain(|id| self.has_task(*id));
666 self.task_order.borrow_mut().extend(pending);
667 }
668 made_progress
669 }
670
671 fn drain_ui(&self) {
672 #[cfg(target_os = "emscripten")]
673 crate::concurrency::fire_due_alarms();
674 loop {
675 let mut executed = false;
676
677 {
678 let rx = &mut *self.ui_rx.borrow_mut();
679 for message in rx.try_iter() {
680 executed = true;
681 let _guard = PendingGuard::new(&self.ui_dispatcher.pending);
682 match message {
683 UiMessage::Task(task) => {
684 task();
685 }
686 UiMessage::Invoke { id, value } => {
687 self.invoke_ui_cont(id, value);
688 }
689 }
690 }
691 }
692
693 if self.run_local_tasks() {
694 executed = true;
695 }
696
697 if self.poll_async_tasks() {
698 executed = true;
699 }
700 self.run_local_tasks();
707
708 if !executed {
709 break;
710 }
711 }
712
713 self.clear_needs_frame_if_idle();
714 }
715
716 fn run_local_tasks(&self) -> bool {
717 let mut ran = false;
718 loop {
719 let task = self.local_tasks.borrow_mut().pop_front();
720 let Some(task) = task else {
721 return ran;
722 };
723 ran = true;
724 task();
725 }
726 }
727
728 fn has_pending_ui(&self) -> bool {
729 let local_pending = self
730 .local_tasks
731 .try_borrow()
732 .map_or(true, |tasks| !tasks.is_empty());
733
734 local_pending || self.ui_dispatcher.has_pending() || self.has_runnable_tasks()
735 }
736
737 fn has_runnable_tasks(&self) -> bool {
738 self.tasks.try_borrow().map_or(true, |tasks| {
739 tasks
740 .values()
741 .any(|task| task.runnable.load(Ordering::Acquire))
742 })
743 }
744
745 fn register_ui_cont<T: 'static>(&self, f: impl FnOnce(T) + 'static) -> u64 {
746 debug_assert_eq!(
747 std::thread::current().id(),
748 self.ui_thread_id,
749 "UI continuation registered off the runtime thread",
750 );
751 let id = self.next_cont_id.get();
752 self.next_cont_id.set(id + 1);
753 let callback = RefCell::new(Some(f));
754 self.ui_conts.borrow_mut().insert(
755 id,
756 Box::new(move |value: Box<dyn Any>| {
757 let Ok(value) = value.downcast::<T>() else {
758 return false;
759 };
760 let Some(slot) = callback.borrow_mut().take() else {
761 return true;
762 };
763 slot(*value);
764 true
765 }),
766 );
767 id
768 }
769
770 fn invoke_ui_cont(&self, id: u64, value: Box<dyn Any + Send>) {
771 debug_assert_eq!(
772 std::thread::current().id(),
773 self.ui_thread_id,
774 "UI continuation invoked off the runtime thread",
775 );
776 let callback = { self.ui_conts.borrow_mut().remove(&id) };
777 if let Some(callback) = callback {
778 let value: Box<dyn Any> = value;
779 if !callback(value) {
780 self.ui_conts.borrow_mut().insert(id, callback);
781 }
782 }
783 }
784
785 fn cancel_ui_cont(&self, id: u64) {
786 let continuation = self.ui_conts.borrow_mut().remove(&id);
787 drop(continuation);
788 }
789
790 fn register_frame_callback(
791 &self,
792 kind: FrameCallbackKind,
793 callback: Box<dyn FnOnce(u64) + 'static>,
794 ) -> FrameCallbackId {
795 let id = self.next_frame_callback_id.get();
796 self.next_frame_callback_id.set(id + 1);
797 self.frame_callbacks
798 .borrow_mut()
799 .push_back(FrameCallbackEntry {
800 id,
801 kind,
802 callback: Some(callback),
803 });
804 self.schedule();
805 id
806 }
807
808 fn cancel_frame_callback(&self, id: FrameCallbackId) {
809 let removed = {
810 let mut callbacks = self.frame_callbacks.borrow_mut();
811 callbacks
812 .iter()
813 .position(|entry| entry.id == id)
814 .and_then(|index| callbacks.remove(index))
815 };
816 drop(removed);
817 self.clear_needs_frame_if_idle();
818 }
819
820 fn clear_needs_frame_if_idle(&self) {
821 if !self.has_invalid_scopes()
822 && !self.has_updates()
823 && !self.has_frame_callbacks()
824 && !self.has_pending_ui()
825 {
826 *self.needs_frame.borrow_mut() = false;
827 }
828 }
829
830 fn drain_frame_callbacks(&self, frame_time_nanos: u64) {
831 if self
832 .last_frame_time_nanos
833 .get()
834 .is_some_and(|previous| frame_time_nanos <= previous)
835 {
836 return;
837 }
838 self.last_frame_time_nanos.set(Some(frame_time_nanos));
839 let next_frame_id = self.next_frame_callback_id.get();
840 if self.has_frame_callbacks() {
841 let _ = crate::run_in_mutable_snapshot(|| {
842 loop {
843 let entry = {
844 let mut callbacks = self.frame_callbacks.borrow_mut();
845 if callbacks
846 .front()
847 .is_some_and(|entry| entry.id < next_frame_id)
848 {
849 callbacks.pop_front()
850 } else {
851 None
852 }
853 };
854 let Some(mut entry) = entry else {
855 break;
856 };
857 if let Some(callback) = entry.callback.take() {
858 callback(frame_time_nanos);
859 }
860 }
861 });
862 }
863
864 self.clear_needs_frame_if_idle();
865 }
866
867 fn debug_stats(&self) -> RuntimeDebugStats {
868 let node_updates = self.node_updates.borrow();
869 let scope_queue = self.scope_queue.borrow();
870 let frame_callbacks = self.frame_callbacks.borrow();
871 let local_tasks = self.local_tasks.borrow();
872 let ui_conts = self.ui_conts.borrow();
873 let tasks = self.tasks.borrow();
874 let external_state_owners = self.external_state_owners.borrow();
875
876 RuntimeDebugStats {
877 node_updates_len: node_updates.len(),
878 node_updates_cap: node_updates.capacity(),
879 invalid_scopes_len: self.invalid_scope_count.get(),
880 scope_queue_len: scope_queue.len(),
881 scope_queue_cap: scope_queue.capacity(),
882 frame_callbacks_len: frame_callbacks.len(),
883 frame_callbacks_cap: frame_callbacks.capacity(),
884 local_tasks_len: local_tasks.len(),
885 local_tasks_cap: local_tasks.capacity(),
886 ui_conts_len: ui_conts.len(),
887 ui_conts_cap: ui_conts.capacity(),
888 tasks_len: tasks.len(),
889 tasks_cap: tasks.capacity(),
890 external_state_owners_len: external_state_owners.len(),
891 external_state_owners_cap: external_state_owners.capacity(),
892 ui_dispatcher_pending: self.ui_dispatcher.pending.load(Ordering::SeqCst),
893 }
894 }
895}
896
897#[derive(Clone)]
898pub struct Runtime {
899 inner: Rc<RuntimeInner>,
900}
901
902impl Runtime {
903 pub fn new(scheduler: SchedulerRef) -> Self {
904 thread_state::watch_thread_exit();
905 let inner = Rc::new(RuntimeInner::new(scheduler));
906 let runtime = Self { inner };
907 let handle = runtime.handle();
908 thread_state::runtimes(|state| {
909 state
910 .registered_runtimes
911 .borrow_mut()
912 .insert(handle.id(), handle.clone());
913 *state.last_runtime.borrow_mut() = Some(handle);
914 });
915 runtime
916 }
917
918 #[cfg(target_os = "android")]
919 pub(crate) fn restore_thread_state(&self) {
920 if let Some(rescued) = self.inner.rescued.take() {
921 thread_state::restore(&rescued);
922 }
923 }
924
925 pub fn handle(&self) -> RuntimeHandle {
926 RuntimeHandle {
927 inner: Rc::downgrade(&self.inner),
928 dispatcher: UiDispatcher::new(self.inner.ui_dispatcher.clone()),
929 ui_thread_id: self.inner.ui_thread_id,
930 id: self.inner.runtime_id,
931 }
932 }
933
934 pub fn has_updates(&self) -> bool {
935 self.inner.has_updates()
936 }
937
938 pub fn needs_frame(&self) -> bool {
939 *self.inner.needs_frame.borrow() || self.inner.has_runnable_tasks()
940 }
941
942 pub fn set_needs_frame(&self, value: bool) {
943 *self.inner.needs_frame.borrow_mut() = value;
944 }
945
946 pub fn last_frame_time_nanos(&self) -> Option<u64> {
951 self.inner.last_frame_time_nanos.get()
952 }
953
954 #[cfg(any(feature = "internal", test))]
955 pub fn frame_clock(&self) -> FrameClock {
956 FrameClock::new(self.handle())
957 }
958}
959
960impl Drop for Runtime {
961 fn drop(&mut self) {
962 if Rc::strong_count(&self.inner) != 1 {
963 return;
964 }
965 let id = self.inner.runtime_id;
966 thread_state::try_runtimes(|state| {
967 state.registered_runtimes.borrow_mut().remove(&id);
968 let mut last = state.last_runtime.borrow_mut();
969 if last.as_ref().is_some_and(|handle| handle.id() == id) {
970 *last = None;
971 }
972 });
973 thread_state::release_when_idle();
974 }
975}
976
977#[derive(Default)]
978pub struct DefaultScheduler;
979
980impl RuntimeScheduler for DefaultScheduler {
981 fn schedule_frame(&self) {}
982}
983
984#[cfg(test)]
985#[derive(Default)]
986pub struct TestScheduler;
987
988#[cfg(test)]
989impl RuntimeScheduler for TestScheduler {
990 fn schedule_frame(&self) {}
991}
992
993#[cfg(test)]
994pub struct TestRuntime {
995 runtime: Runtime,
996}
997
998#[cfg(test)]
999impl Default for TestRuntime {
1000 fn default() -> Self {
1001 Self::new()
1002 }
1003}
1004
1005#[cfg(test)]
1006impl TestRuntime {
1007 pub fn new() -> Self {
1008 Self {
1009 runtime: Runtime::new(Arc::new(TestScheduler)),
1010 }
1011 }
1012
1013 pub fn handle(&self) -> RuntimeHandle {
1014 self.runtime.handle()
1015 }
1016}
1017
1018#[derive(Clone)]
1019pub struct RuntimeHandle {
1020 inner: Weak<RuntimeInner>,
1021 dispatcher: UiDispatcher,
1022 ui_thread_id: ThreadId,
1023 id: RuntimeId,
1024}
1025
1026pub(crate) struct ScopeRuntime(Weak<RuntimeInner>);
1030
1031impl ScopeRuntime {
1032 pub(crate) fn mark_scope_recomposed(&self) {
1033 if let Some(inner) = self.0.upgrade() {
1034 inner.mark_scope_recomposed();
1035 }
1036 }
1037
1038 pub(crate) fn register_invalid_scope(&self, scope: Weak<RecomposeScopeInner>) {
1039 if let Some(inner) = self.0.upgrade() {
1040 inner.register_invalid_scope(scope);
1041 }
1042 }
1043
1044 pub(crate) fn unregister_state_scope(&self, id: StateId, scope_id: ScopeId) {
1045 if let Some(inner) = self.0.upgrade() {
1046 inner.state_arena.unregister_scope(id, scope_id);
1047 }
1048 }
1049
1050 pub(crate) fn increment_live_recompose_scope_count(&self) {
1051 if let Some(inner) = self.0.upgrade() {
1052 inner.increment_live_recompose_scope_count();
1053 }
1054 }
1055
1056 pub(crate) fn decrement_live_recompose_scope_count(&self) {
1057 if let Some(inner) = self.0.upgrade() {
1058 inner.decrement_live_recompose_scope_count();
1059 }
1060 }
1061}
1062
1063pub struct TaskHandle {
1064 id: u64,
1065 runtime: RuntimeHandle,
1066}
1067
1068pub(crate) struct DeferredStateRelease {
1069 runtime: Weak<RuntimeInner>,
1070 id: StateId,
1071}
1072
1073pub(crate) struct StateHandleLease {
1074 id: StateId,
1075 runtime: RuntimeHandle,
1076}
1077
1078impl StateHandleLease {
1079 pub(crate) fn id(&self) -> StateId {
1080 self.id
1081 }
1082
1083 pub(crate) fn runtime_id(&self) -> RuntimeId {
1084 self.runtime.id()
1085 }
1086}
1087
1088impl Drop for StateHandleLease {
1089 fn drop(&mut self) {
1090 defer_state_release(&self.runtime, self.id);
1091 }
1092}
1093
1094struct StateOwnerFrame;
1095
1096impl Drop for StateOwnerFrame {
1097 fn drop(&mut self) {
1098 thread_state::runtimes(|state| state.state_owners.borrow_mut().pop());
1099 }
1100}
1101
1102pub(crate) fn collecting_states<T>(build: impl FnOnce() -> T) -> (T, Vec<Rc<StateHandleLease>>) {
1120 thread_state::runtimes(|state| state.state_owners.borrow_mut().push(Vec::new()));
1121 let frame = StateOwnerFrame;
1122 let value = build();
1123 let states = thread_state::runtimes(|state| {
1124 state
1125 .state_owners
1126 .borrow_mut()
1127 .last_mut()
1128 .map(std::mem::take)
1129 .unwrap_or_default()
1130 });
1131 drop(frame);
1132 (value, states)
1133}
1134
1135fn hand_to_current_owner(lease: &Rc<StateHandleLease>) -> bool {
1136 thread_state::runtimes(|state| match state.state_owners.borrow_mut().last_mut() {
1137 Some(owner) => {
1138 owner.push(Rc::clone(lease));
1139 true
1140 }
1141 None => false,
1142 })
1143}
1144
1145impl RuntimeHandle {
1146 pub fn id(&self) -> RuntimeId {
1147 self.id
1148 }
1149
1150 pub(crate) fn alloc_state<T: Clone + 'static>(&self, value: T) -> Rc<StateHandleLease> {
1151 let id = self.with_state_arena(|arena| arena.alloc(value, self.clone()));
1152 let lease = Rc::new(StateHandleLease {
1153 id,
1154 runtime: self.clone(),
1155 });
1156 self.with_state_arena(|arena| arena.register_lease(id, &lease));
1157 lease
1158 }
1159
1160 pub(crate) fn alloc_state_with_policy<T: Clone + 'static>(
1161 &self,
1162 value: T,
1163 policy: Rc<dyn MutationPolicy<T>>,
1164 ) -> Rc<StateHandleLease> {
1165 let id =
1166 self.with_state_arena(|arena| arena.alloc_with_policy(value, self.clone(), policy));
1167 let lease = Rc::new(StateHandleLease {
1168 id,
1169 runtime: self.clone(),
1170 });
1171 self.with_state_arena(|arena| arena.register_lease(id, &lease));
1172 lease
1173 }
1174
1175 pub(crate) fn alloc_persistent_state<T: Clone + 'static>(
1176 &self,
1177 value: T,
1178 ) -> crate::MutableState<T> {
1179 self.hand_out(self.alloc_state(value))
1180 }
1181
1182 pub(crate) fn alloc_persistent_state_with_policy<T: Clone + 'static>(
1183 &self,
1184 value: T,
1185 policy: Rc<dyn MutationPolicy<T>>,
1186 ) -> crate::MutableState<T> {
1187 self.hand_out(self.alloc_state_with_policy(value, policy))
1188 }
1189
1190 fn hand_out<T: Clone + 'static>(&self, lease: Rc<StateHandleLease>) -> crate::MutableState<T> {
1191 if !hand_to_current_owner(&lease)
1192 && let Some(inner) = self.inner.upgrade()
1193 {
1194 inner
1195 .external_state_owners
1196 .borrow_mut()
1197 .insert(lease.id(), Rc::clone(&lease));
1198 }
1199 crate::MutableState::from_lease(&lease)
1200 }
1201
1202 pub(crate) fn retain_state_lease(&self, id: StateId) -> Option<Rc<StateHandleLease>> {
1203 self.with_state_arena(|arena| arena.retain_lease(id))
1204 }
1205
1206 pub(crate) fn with_state_arena<R>(&self, f: impl FnOnce(&StateArena) -> R) -> R {
1207 self.try_with_state_arena(f)
1208 .unwrap_or_else(|| panic!("runtime dropped"))
1209 }
1210
1211 pub(crate) fn try_with_state_arena<R>(&self, f: impl FnOnce(&StateArena) -> R) -> Option<R> {
1212 self.inner.upgrade().map(|inner| f(&inner.state_arena))
1213 }
1214
1215 pub fn state_arena_stats(&self) -> (usize, usize) {
1216 self.try_with_state_arena(StateArena::stats)
1217 .unwrap_or_default()
1218 }
1219
1220 pub fn state_arena_debug_stats(&self) -> StateArenaDebugStats {
1221 self.try_with_state_arena(StateArena::debug_stats)
1222 .unwrap_or_default()
1223 }
1224
1225 pub fn debug_stats(&self) -> RuntimeDebugStats {
1226 self.inner
1227 .upgrade()
1228 .map(|inner| inner.debug_stats())
1229 .unwrap_or_default()
1230 }
1231
1232 pub fn live_ui_task_labels(&self) -> Vec<(u64, String)> {
1233 self.inner
1234 .upgrade()
1235 .map(|inner| {
1236 inner
1237 .tasks
1238 .borrow()
1239 .iter()
1240 .map(|(id, entry)| (*id, entry.label.clone()))
1241 .collect()
1242 })
1243 .unwrap_or_default()
1244 }
1245
1246 pub fn schedule(&self) {
1247 if let Some(inner) = self.inner.upgrade() {
1248 inner.schedule();
1249 }
1250 }
1251
1252 pub(crate) fn enqueue_node_update(&self, command: Command) {
1253 if let Some(inner) = self.inner.upgrade() {
1254 inner.enqueue_update(command);
1255 }
1256 }
1257
1258 pub fn enqueue_ui_task(&self, task: Box<dyn FnOnce() + 'static>) {
1265 if let Some(inner) = self.inner.upgrade() {
1266 inner.enqueue_ui_task(task);
1267 } else {
1268 task();
1269 }
1270 }
1271
1272 pub fn spawn_ui<F>(&self, fut: F) -> Option<TaskHandle>
1273 where
1274 F: Future<Output = ()> + 'static,
1275 {
1276 self.inner.upgrade().map(|inner| {
1277 let id = inner.spawn_ui_task(Box::pin(fut));
1278 TaskHandle {
1279 id,
1280 runtime: self.clone(),
1281 }
1282 })
1283 }
1284
1285 pub fn cancel_task(&self, id: u64) {
1286 if let Some(inner) = self.inner.upgrade() {
1287 inner.cancel_task(id);
1288 }
1289 }
1290
1291 pub fn has_task(&self, id: u64) -> bool {
1293 self.inner.upgrade().is_some_and(|inner| inner.has_task(id))
1294 }
1295
1296 pub fn post_ui(&self, task: impl FnOnce() + Send + 'static) {
1301 self.dispatcher.post(task);
1302 }
1303
1304 pub fn register_ui_cont<T: 'static>(&self, f: impl FnOnce(T) + 'static) -> Option<u64> {
1305 self.inner.upgrade().map(|inner| inner.register_ui_cont(f))
1306 }
1307
1308 pub fn cancel_ui_cont(&self, id: u64) {
1309 if let Some(inner) = self.inner.upgrade() {
1310 inner.cancel_ui_cont(id);
1311 }
1312 }
1313
1314 pub fn drain_ui(&self) {
1315 if let Some(inner) = self.inner.upgrade() {
1316 inner.drain_ui();
1317 }
1318 }
1319
1320 pub fn has_pending_ui(&self) -> bool {
1321 self.inner.upgrade().map_or_else(
1322 || self.dispatcher.has_pending(),
1323 |inner| inner.has_pending_ui(),
1324 )
1325 }
1326
1327 pub fn register_frame_callback(
1328 &self,
1329 callback: impl FnOnce(u64) + 'static,
1330 ) -> Option<FrameCallbackId> {
1331 self.inner.upgrade().map(|inner| {
1332 inner.register_frame_callback(FrameCallbackKind::Transient, Box::new(callback))
1333 })
1334 }
1335
1336 pub fn register_perpetual_frame_callback(
1337 &self,
1338 callback: impl FnOnce(u64) + 'static,
1339 ) -> Option<FrameCallbackId> {
1340 self.inner.upgrade().map(|inner| {
1341 inner.register_frame_callback(FrameCallbackKind::Perpetual, Box::new(callback))
1342 })
1343 }
1344
1345 pub fn cancel_frame_callback(&self, id: FrameCallbackId) {
1346 if let Some(inner) = self.inner.upgrade() {
1347 inner.cancel_frame_callback(id);
1348 }
1349 }
1350
1351 pub fn drain_frame_callbacks(&self, frame_time_nanos: u64) {
1355 if let Some(inner) = self.inner.upgrade() {
1356 inner.drain_frame_callbacks(frame_time_nanos);
1357 }
1358 }
1359
1360 pub fn last_frame_time_nanos(&self) -> Option<u64> {
1363 self.inner
1364 .upgrade()
1365 .and_then(|inner| inner.last_frame_time_nanos.get())
1366 }
1367
1368 #[cfg(any(feature = "internal", test))]
1369 pub fn frame_clock(&self) -> FrameClock {
1370 FrameClock::new(self.clone())
1371 }
1372
1373 pub fn set_needs_frame(&self, value: bool) {
1374 if let Some(inner) = self.inner.upgrade() {
1375 *inner.needs_frame.borrow_mut() = value;
1376 }
1377 }
1378
1379 pub(crate) fn take_updates(&self) -> Vec<Command> {
1380 self.inner
1381 .upgrade()
1382 .map(|inner| inner.take_updates())
1383 .unwrap_or_default()
1384 }
1385
1386 pub fn has_updates(&self) -> bool {
1387 self.inner
1388 .upgrade()
1389 .is_some_and(|inner| inner.has_updates())
1390 }
1391
1392 pub(crate) fn scope_runtime(&self) -> ScopeRuntime {
1394 ScopeRuntime(Weak::clone(&self.inner))
1395 }
1396
1397 pub(crate) fn requeue_invalid_scope(&self, scope: &RecomposeScope) {
1398 if let Some(inner) = self.inner.upgrade() {
1399 inner.requeue_invalid_scope(scope);
1400 }
1401 }
1402
1403 pub(crate) fn take_invalidated_scopes(&self) -> Option<Vec<RecomposeScope>> {
1404 self.inner
1405 .upgrade()
1406 .and_then(|inner| inner.take_invalidated_scopes())
1407 }
1408
1409 pub fn forget_movable(&self, id: Key) {
1413 if let Some(inner) = self.inner.upgrade() {
1414 inner.forgotten_movables.borrow_mut().push(id);
1415 inner.schedule();
1416 }
1417 }
1418
1419 pub(crate) fn take_forgotten_movables(&self) -> Vec<Key> {
1420 self.inner
1421 .upgrade()
1422 .map(|inner| std::mem::take(&mut *inner.forgotten_movables.borrow_mut()))
1423 .unwrap_or_default()
1424 }
1425
1426 pub(crate) fn next_movable_content_id(&self) -> Key {
1431 let Some(inner) = self.inner.upgrade() else {
1432 log::error!("movable content asked a runtime that is gone for an identity");
1433 return 0;
1434 };
1435 let id = inner.next_movable_content_id.get();
1436 inner.next_movable_content_id.set(id.wrapping_add(1).max(1));
1437 id
1438 }
1439
1440 pub fn has_invalid_scopes(&self) -> bool {
1441 self.inner
1442 .upgrade()
1443 .is_some_and(|inner| inner.has_invalid_scopes())
1444 }
1445
1446 fn live_recompose_scope_count(&self) -> usize {
1447 self.inner
1448 .upgrade()
1449 .map(|inner| inner.live_recompose_scope_count())
1450 .unwrap_or_default()
1451 }
1452
1453 #[doc(hidden)]
1454 pub fn debug_invalid_scope_ids(&self) -> Vec<usize> {
1455 self.inner
1456 .upgrade()
1457 .map(|inner| inner.queued_invalid_scope_ids())
1458 .unwrap_or_default()
1459 }
1460
1461 pub fn has_frame_callbacks(&self) -> bool {
1462 self.inner
1463 .upgrade()
1464 .is_some_and(|inner| inner.has_frame_callbacks())
1465 }
1466
1467 pub fn has_transient_frame_callbacks(&self) -> bool {
1468 self.inner
1469 .upgrade()
1470 .is_some_and(|inner| inner.has_transient_frame_callbacks())
1471 }
1472
1473 pub fn assert_ui_thread(&self) {
1474 debug_assert_eq!(
1475 std::thread::current().id(),
1476 self.ui_thread_id,
1477 "state mutated off the runtime's UI thread"
1478 );
1479 }
1480
1481 pub fn dispatcher(&self) -> UiDispatcher {
1482 self.dispatcher.clone()
1483 }
1484
1485 #[doc(hidden)]
1486 pub fn with_deferred_state_releases<R>(&self, f: impl FnOnce() -> R) -> R {
1487 let _scope = enter_state_teardown_scope();
1488 f()
1489 }
1490}
1491
1492impl TaskHandle {
1493 pub fn cancel(&self) {
1494 self.runtime.cancel_task(self.id);
1495 }
1496
1497 pub fn is_finished(&self) -> bool {
1499 !self.runtime.has_task(self.id)
1500 }
1501}
1502
1503pub(crate) struct FrameCallbackEntry {
1504 id: FrameCallbackId,
1505 kind: FrameCallbackKind,
1506 callback: Option<Box<dyn FnOnce(u64) + 'static>>,
1507}
1508
1509#[cfg(not(target_arch = "wasm32"))]
1510struct RuntimeTaskWaker {
1511 scheduler: SchedulerRef,
1512 runnable: Arc<AtomicBool>,
1513}
1514
1515#[cfg(target_arch = "wasm32")]
1516struct RuntimeTaskWaker {
1517 runtime_id: RuntimeId,
1518 runnable: Arc<AtomicBool>,
1519}
1520
1521impl RuntimeTaskWaker {
1522 #[cfg(not(target_arch = "wasm32"))]
1523 fn new(inner: &RuntimeInner, runnable: Arc<AtomicBool>) -> Self {
1524 let scheduler = inner.scheduler.clone();
1525 Self {
1526 scheduler,
1527 runnable,
1528 }
1529 }
1530
1531 #[cfg(target_arch = "wasm32")]
1532 fn new(inner: &RuntimeInner, runnable: Arc<AtomicBool>) -> Self {
1533 let runtime_id = inner.runtime_id;
1534 Self {
1535 runtime_id,
1536 runnable,
1537 }
1538 }
1539
1540 fn into_waker(self) -> Waker {
1541 futures_task::waker(Arc::new(self))
1542 }
1543}
1544
1545impl futures_task::ArcWake for RuntimeTaskWaker {
1546 #[cfg(not(target_arch = "wasm32"))]
1547 fn wake_by_ref(arc_self: &Arc<Self>) {
1548 arc_self.runnable.store(true, Ordering::Release);
1549 arc_self.scheduler.schedule_frame();
1550 }
1551
1552 #[cfg(target_arch = "wasm32")]
1553 fn wake_by_ref(arc_self: &Arc<Self>) {
1554 arc_self.runnable.store(true, Ordering::Release);
1555 if let Some(handle) = runtime_handle_by_id(arc_self.runtime_id) {
1556 handle.schedule();
1557 }
1558 }
1559}
1560
1561#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
1562pub struct RuntimeThreadLocalDebugStats {
1563 pub active_runtimes_len: usize,
1564 pub active_runtimes_cap: usize,
1565 pub registered_runtimes_len: usize,
1566 pub registered_runtimes_cap: usize,
1567 pub deferred_state_releases_len: usize,
1568 pub deferred_state_releases_cap: usize,
1569}
1570
1571pub fn current_runtime_handle() -> Option<RuntimeHandle> {
1576 thread_state::try_runtimes(|state| {
1577 if let Some(handle) = state.active_runtimes.borrow().last() {
1578 return Some(handle.clone());
1579 }
1580 state.last_runtime.borrow().clone()
1581 })
1582 .flatten()
1583}
1584
1585pub(crate) fn runtime_handle_by_id(id: RuntimeId) -> Option<RuntimeHandle> {
1586 thread_state::try_runtimes(|state| state.registered_runtimes.borrow().get(&id).cloned())
1587 .flatten()
1588}
1589
1590#[cfg(target_os = "android")]
1591pub(crate) fn share_rescue<'a>(
1592 handles: impl Iterator<Item = &'a RuntimeHandle>,
1593) -> Option<Rc<thread_state::RescuedThreadState>> {
1594 let live: Vec<Rc<RuntimeInner>> = handles
1595 .filter_map(|handle| handle.inner.upgrade())
1596 .collect();
1597 let shared = live
1598 .iter()
1599 .find_map(|inner| inner.rescued.borrow().clone())
1600 .or_else(|| (!live.is_empty()).then(Rc::default))?;
1601 for inner in &live {
1602 *inner.rescued.borrow_mut() = Some(Rc::clone(&shared));
1603 }
1604 Some(shared)
1605}
1606
1607pub(crate) fn with_state_arena_by_id<R>(
1608 id: RuntimeId,
1609 f: impl FnOnce(&StateArena) -> R,
1610) -> Option<R> {
1611 let inner = thread_state::try_runtimes(|state| {
1612 state
1613 .registered_runtimes
1614 .borrow()
1615 .get(&id)
1616 .and_then(|handle| handle.inner.upgrade())
1617 })
1618 .flatten()?;
1619 Some(f(&inner.state_arena))
1620}
1621
1622pub(crate) fn live_recompose_scope_count() -> usize {
1623 thread_state::runtimes(|state| {
1624 state
1625 .registered_runtimes
1626 .borrow()
1627 .values()
1628 .map(RuntimeHandle::live_recompose_scope_count)
1629 .sum()
1630 })
1631}
1632
1633pub fn debug_runtime_thread_local_stats() -> RuntimeThreadLocalDebugStats {
1634 thread_state::runtimes(|state| {
1635 let active = state.active_runtimes.borrow();
1636 let registered = state.registered_runtimes.borrow();
1637 let deferred = state.deferred_state_releases.borrow();
1638 RuntimeThreadLocalDebugStats {
1639 active_runtimes_len: active.len(),
1640 active_runtimes_cap: active.capacity(),
1641 registered_runtimes_len: registered.len(),
1642 registered_runtimes_cap: registered.capacity(),
1643 deferred_state_releases_len: deferred.len(),
1644 deferred_state_releases_cap: deferred.capacity(),
1645 }
1646 })
1647}
1648
1649fn defer_state_release(runtime: &RuntimeHandle, id: StateId) {
1650 let deferred = thread_state::try_runtimes(|state| {
1651 if state.state_teardown_depth.get() == 0 {
1652 return false;
1653 }
1654 state
1655 .deferred_state_releases
1656 .borrow_mut()
1657 .push(DeferredStateRelease {
1658 runtime: Weak::clone(&runtime.inner),
1659 id,
1660 });
1661 true
1662 });
1663 if deferred != Some(true) {
1664 release_state(&runtime.inner, id);
1665 }
1666}
1667
1668fn release_state(runtime: &Weak<RuntimeInner>, id: StateId) {
1669 if let Some(inner) = runtime.upgrade() {
1670 inner.state_arena.release(id);
1671 }
1672}
1673
1674pub(crate) struct StateTeardownScope;
1675
1676pub(crate) fn enter_state_teardown_scope() -> StateTeardownScope {
1677 thread_state::try_runtimes(|state| {
1678 let depth = &state.state_teardown_depth;
1679 depth.set(depth.get() + 1);
1680 });
1681 StateTeardownScope
1682}
1683
1684impl Drop for StateTeardownScope {
1685 fn drop(&mut self) {
1686 let finished = thread_state::try_runtimes(|state| {
1687 let depth = &state.state_teardown_depth;
1688 let next = depth.get().saturating_sub(1);
1689 depth.set(next);
1690 if next == 0 {
1691 let mut releases = state.deferred_state_releases.borrow_mut();
1692 while let Some(deferred) = releases.pop() {
1693 release_state(&deferred.runtime, deferred.id);
1694 }
1695 }
1696 next == 0
1697 });
1698 if finished == Some(true) {
1699 thread_state::release_when_idle();
1700 }
1701 }
1702}
1703
1704pub(crate) fn push_active_runtime(handle: &RuntimeHandle) {
1705 thread_state::runtimes(|state| {
1706 state
1707 .registered_runtimes
1708 .borrow_mut()
1709 .insert(handle.id(), handle.clone());
1710 state.active_runtimes.borrow_mut().push(handle.clone());
1711 *state.last_runtime.borrow_mut() = Some(handle.clone());
1712 });
1713}
1714
1715pub(crate) fn pop_active_runtime() {
1716 thread_state::runtimes(|state| {
1717 state.active_runtimes.borrow_mut().pop();
1718 });
1719}
1720
1721pub fn schedule_frame() {
1723 if let Some(handle) = current_runtime_handle() {
1724 handle.schedule();
1725 return;
1726 }
1727 log::debug!(
1728 target: "cranpose::runtime",
1729 "ignoring frame request without an active runtime",
1730 );
1731}
1732
1733pub fn schedule_node_update(
1735 update: impl FnOnce(&mut dyn Applier) -> Result<(), NodeError> + 'static,
1736) {
1737 if let Some(handle) = current_runtime_handle() {
1738 handle.enqueue_node_update(Command::callback(update));
1739 } else {
1740 drop(update);
1741 log::debug!(
1742 target: "cranpose::runtime",
1743 "ignoring node update request without an active runtime",
1744 );
1745 }
1746}
1747
1748#[cfg(test)]
1749#[path = "tests/runtime_tests.rs"]
1750mod tests;