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