dioxus_core/
generational_box.rs

1//! Integration with the generational-box crate for copy state management.
2//!
3//! Each scope in dioxus has a single [Owner]
4
5use std::{
6    any::{Any, TypeId},
7    cell::RefCell,
8};
9
10use generational_box::{AnyStorage, Owner, SyncStorage, UnsyncStorage};
11
12use crate::{innerlude::current_scope_id, Runtime, ScopeId};
13
14/// Run a closure with the given owner.
15///
16/// This will override the default owner for the current component.
17pub fn with_owner<S: AnyStorage, F: FnOnce() -> R, R>(owner: Owner<S>, f: F) -> R {
18    let old_owner = set_owner(Some(owner));
19    let result = f();
20    set_owner(old_owner);
21    result
22}
23
24/// Set the owner for the current thread.
25fn set_owner<S: AnyStorage>(owner: Option<Owner<S>>) -> Option<Owner<S>> {
26    let id = TypeId::of::<S>();
27    if id == TypeId::of::<SyncStorage>() {
28        SYNC_OWNER.with(|cell| {
29            std::mem::replace(
30                &mut *cell.borrow_mut(),
31                owner.map(|owner| {
32                    *(Box::new(owner) as Box<dyn Any>)
33                        .downcast::<Owner<SyncStorage>>()
34                        .unwrap()
35                }),
36            )
37            .map(|owner| *(Box::new(owner) as Box<dyn Any>).downcast().unwrap())
38        })
39    } else {
40        UNSYNC_OWNER.with(|cell| {
41            std::mem::replace(
42                &mut *cell.borrow_mut(),
43                owner.map(|owner| {
44                    *(Box::new(owner) as Box<dyn Any>)
45                        .downcast::<Owner<UnsyncStorage>>()
46                        .unwrap()
47                }),
48            )
49            .map(|owner| *(Box::new(owner) as Box<dyn Any>).downcast().unwrap())
50        })
51    }
52}
53
54thread_local! {
55    static SYNC_OWNER: RefCell<Option<Owner<SyncStorage>>> = const { RefCell::new(None) };
56    static UNSYNC_OWNER: RefCell<Option<Owner<UnsyncStorage>>> = const { RefCell::new(None) };
57}
58
59/// Returns the current owner. This owner will be used to drop any `Copy` state that is created by the `generational-box` crate.
60///
61/// If an owner has been set with `with_owner`, that owner will be returned. Otherwise, the owner from the current scope will be returned.
62pub fn current_owner<S: AnyStorage>() -> Owner<S> {
63    let id = TypeId::of::<S>();
64    let override_owner = if id == TypeId::of::<SyncStorage>() {
65        SYNC_OWNER.with(|cell| {
66            let owner = cell.borrow();
67
68            owner.clone().map(|owner| {
69                *(Box::new(owner) as Box<dyn Any>)
70                    .downcast::<Owner<S>>()
71                    .unwrap()
72            })
73        })
74    } else {
75        UNSYNC_OWNER.with(|cell| {
76            cell.borrow().clone().map(|owner| {
77                *(Box::new(owner) as Box<dyn Any>)
78                    .downcast::<Owner<S>>()
79                    .unwrap()
80            })
81        })
82    };
83    if let Some(owner) = override_owner {
84        return owner;
85    }
86
87    // Otherwise get the owner from the current scope
88    current_scope_id().expect("in a virtual dom").owner()
89}
90
91impl ScopeId {
92    /// Get the owner for the current scope.
93    #[track_caller]
94    pub fn owner<S: AnyStorage>(self) -> Owner<S> {
95        Runtime::with_scope(self, |cx| cx.owner::<S>()).unwrap_or_else(|e| panic!("{}", e))
96    }
97}