浏览代码

Fix cloned rsx double drop (#2839)

* Implement Rc generational pointers

* Clean up error handling a bit

* start adding tests

* Simplify a few types

* Separate Rc and normal generational boxes

* Add tests for rc drop and read

* Move over the pointer instead of the data in ReadOnlySignal props

* Fix panic on ref drop and release mode builds

* final rc signal cleanup

* use rc generational boxes for event handlers as well

* add a regression test for read only signal cloning

* merge main

---------

Co-authored-by: Jonathan Kelley <jkelleyrtp@gmail.com>
Evan Almloff 8 月之前
父节点
当前提交
baf5a95cae

+ 1 - 0
Cargo.lock

@@ -4031,6 +4031,7 @@ dependencies = [
  "criterion",
  "criterion",
  "parking_lot",
  "parking_lot",
  "rand 0.8.5",
  "rand 0.8.5",
+ "tracing",
 ]
 ]
 
 
 [[package]]
 [[package]]

+ 7 - 4
packages/core-macro/src/props/mod.rs

@@ -652,8 +652,11 @@ mod struct_info {
                         let new_value: &_ = &*new.#signal_fields.peek();
                         let new_value: &_ = &*new.#signal_fields.peek();
                         (&old_value).compare(&&new_value)
                         (&old_value).compare(&&new_value)
                     };
                     };
+                    // Make the old fields point to the new fields
+                    #signal_fields.point_to(new.#signal_fields).unwrap();
                     if !field_eq {
                     if !field_eq {
-                        (#signal_fields).__set(new.#signal_fields.__take());
+                        // If the fields are not equal, mark the signal as dirty to rerun any subscribers
+                        (#signal_fields).mark_dirty();
                     }
                     }
                     // Move the old value back
                     // Move the old value back
                     self.#signal_fields = #signal_fields;
                     self.#signal_fields = #signal_fields;
@@ -683,13 +686,13 @@ mod struct_info {
                     quote! {
                     quote! {
                         // If the event handler is None, we don't need to update it
                         // If the event handler is None, we don't need to update it
                         if let (Some(old_handler), Some(new_handler)) = (self.#name.as_mut(), new.#name.as_ref()) {
                         if let (Some(old_handler), Some(new_handler)) = (self.#name.as_mut(), new.#name.as_ref()) {
-                            old_handler.__set(new_handler.__take());
+                            old_handler.__point_to(new_handler);
                         }
                         }
                     }
                     }
                 } else {
                 } else {
                     quote! {
                     quote! {
                         // Update the event handlers
                         // Update the event handlers
-                        self.#name.__set(new.#name.__take());
+                        self.#name.__point_to(&new.#name);
                     }
                     }
                 }
                 }
             }).collect();
             }).collect();
@@ -1447,7 +1450,7 @@ Finally, call `.build()` to create the instance of `{name}`.
                     #[derive(Clone)]
                     #[derive(Clone)]
                     #vis struct #name #generics_with_bounds #where_clause {
                     #vis struct #name #generics_with_bounds #where_clause {
                         inner: #original_name #ty_generics,
                         inner: #original_name #ty_generics,
-                        owner: Owner,
+                        owner: dioxus_core::internal::generational_box::Owner,
                     }
                     }
 
 
                     impl #original_impl_generics PartialEq for #name #ty_generics #where_clause {
                     impl #original_impl_generics PartialEq for #name #ty_generics #where_clause {

+ 49 - 0
packages/core-macro/tests/values_memoize_in_place.rs

@@ -161,3 +161,52 @@ fn spreads_memorize_in_place() {
     assert!(props.memoize(&CompProps::builder().build()));
     assert!(props.memoize(&CompProps::builder().build()));
     assert_eq!(props.attributes, vec![]);
     assert_eq!(props.attributes, vec![]);
 }
 }
+
+// Regression test for https://github.com/DioxusLabs/dioxus/issues/2331
+#[test]
+fn cloning_read_only_signal_components_work() {
+    fn app() -> Element {
+        if generation() < 5 {
+            println!("Generating new props");
+            needs_update();
+        }
+
+        let read_only_signal_rsx = rsx! {
+            TakesReadOnlySignalNonClone { sig: NonCloneable(generation() as i32) }
+            TakesReadOnlySignalNum { sig: generation() as i32 }
+        };
+
+        rsx! {
+            {read_only_signal_rsx.clone()}
+            {read_only_signal_rsx}
+        }
+    }
+
+    struct NonCloneable<T>(T);
+
+    #[component]
+    fn TakesReadOnlySignalNum(sig: ReadOnlySignal<i32>) -> Element {
+        rsx! {}
+    }
+
+    #[component]
+    fn TakesReadOnlySignalNonClone(sig: ReadOnlySignal<NonCloneable<i32>>) -> Element {
+        rsx! {}
+    }
+
+    set_event_converter(Box::new(dioxus::html::SerializedHtmlEventConverter));
+    let mut dom = VirtualDom::new(app);
+
+    let mutations = dom.rebuild_to_vec();
+    println!("{:#?}", mutations);
+    dom.mark_dirty(ScopeId::APP);
+    for _ in 0..20 {
+        let event = Event::new(
+            Rc::new(PlatformEventData::new(Box::<SerializedMouseData>::default())) as Rc<dyn Any>,
+            true,
+        );
+        dom.runtime().handle_event("click", event, ElementId(1));
+        dom.render_immediate(&mut dioxus_core::NoOpMutations);
+    }
+    dom.render_immediate(&mut dioxus_core::NoOpMutations);
+}

+ 20 - 36
packages/core/src/events.rs

@@ -1,6 +1,6 @@
 use crate::{properties::SuperFrom, runtime::RuntimeGuard, Runtime, ScopeId};
 use crate::{properties::SuperFrom, runtime::RuntimeGuard, Runtime, ScopeId};
 use generational_box::GenerationalBox;
 use generational_box::GenerationalBox;
-use std::{cell::RefCell, marker::PhantomData, rc::Rc};
+use std::{cell::RefCell, marker::PhantomData, panic::Location, rc::Rc};
 
 
 /// A wrapper around some generic data that handles the event's state
 /// A wrapper around some generic data that handles the event's state
 ///
 ///
@@ -431,19 +431,10 @@ impl<Args: 'static, Ret: 'static> PartialEq for Callback<Args, Ret> {
 }
 }
 
 
 pub(super) struct ExternalListenerCallback<Args, Ret> {
 pub(super) struct ExternalListenerCallback<Args, Ret> {
-    callback: Rc<RefCell<dyn FnMut(Args) -> Ret>>,
+    callback: Box<dyn FnMut(Args) -> Ret>,
     runtime: std::rc::Weak<Runtime>,
     runtime: std::rc::Weak<Runtime>,
 }
 }
 
 
-impl<Args, Ret> Clone for ExternalListenerCallback<Args, Ret> {
-    fn clone(&self) -> Self {
-        Self {
-            callback: self.callback.clone(),
-            runtime: self.runtime.clone(),
-        }
-    }
-}
-
 impl<Args: 'static, Ret: 'static> Callback<Args, Ret> {
 impl<Args: 'static, Ret: 'static> Callback<Args, Ret> {
     /// Create a new [`Callback`] from an [`FnMut`]. The callback is owned by the current scope and will be dropped when the scope is dropped.
     /// Create a new [`Callback`] from an [`FnMut`]. The callback is owned by the current scope and will be dropped when the scope is dropped.
     /// This should not be called directly in the body of a component because it will not be dropped until the component is dropped.
     /// This should not be called directly in the body of a component because it will not be dropped until the component is dropped.
@@ -456,9 +447,8 @@ impl<Args: 'static, Ret: 'static> Callback<Args, Ret> {
             .current_scope_id()
             .current_scope_id()
             .unwrap_or_else(|e| panic!("{}", e));
             .unwrap_or_else(|e| panic!("{}", e));
         let owner = crate::innerlude::current_owner::<generational_box::UnsyncStorage>();
         let owner = crate::innerlude::current_owner::<generational_box::UnsyncStorage>();
-        let callback = owner.insert(Some(ExternalListenerCallback {
-            callback: Rc::new(RefCell::new(move |event: Args| f(event).spawn()))
-                as Rc<RefCell<dyn FnMut(Args) -> Ret>>,
+        let callback = owner.insert_rc(Some(ExternalListenerCallback {
+            callback: Box::new(move |event: Args| f(event).spawn()),
             runtime: Rc::downgrade(&runtime),
             runtime: Rc::downgrade(&runtime),
         }));
         }));
         Self { callback, origin }
         Self { callback, origin }
@@ -471,11 +461,13 @@ impl<Args: 'static, Ret: 'static> Callback<Args, Ret> {
         let origin = runtime
         let origin = runtime
             .current_scope_id()
             .current_scope_id()
             .unwrap_or_else(|e| panic!("{}", e));
             .unwrap_or_else(|e| panic!("{}", e));
-        let callback = GenerationalBox::leak(Some(ExternalListenerCallback {
-            callback: Rc::new(RefCell::new(move |event: Args| f(event).spawn()))
-                as Rc<RefCell<dyn FnMut(Args) -> Ret>>,
-            runtime: Rc::downgrade(&runtime),
-        }));
+        let callback = GenerationalBox::leak_rc(
+            Some(ExternalListenerCallback {
+                callback: Box::new(move |event: Args| f(event).spawn()),
+                runtime: Rc::downgrade(&runtime),
+            }),
+            Location::caller(),
+        );
         Self { callback, origin }
         Self { callback, origin }
     }
     }
 
 
@@ -484,16 +476,13 @@ impl<Args: 'static, Ret: 'static> Callback<Args, Ret> {
     /// This borrows the callback using a RefCell. Recursively calling a callback will cause a panic.
     /// This borrows the callback using a RefCell. Recursively calling a callback will cause a panic.
     #[track_caller]
     #[track_caller]
     pub fn call(&self, arguments: Args) -> Ret {
     pub fn call(&self, arguments: Args) -> Ret {
-        if let Some(callback) = self.callback.read().as_ref() {
+        if let Some(callback) = self.callback.write().as_mut() {
             let runtime = callback
             let runtime = callback
                 .runtime
                 .runtime
                 .upgrade()
                 .upgrade()
                 .expect("Callback was called after the runtime was dropped");
                 .expect("Callback was called after the runtime was dropped");
             let _guard = RuntimeGuard::new(runtime.clone());
             let _guard = RuntimeGuard::new(runtime.clone());
-            runtime.with_scope_on_stack(self.origin, || {
-                let mut callback = callback.callback.borrow_mut();
-                callback(arguments)
-            })
+            runtime.with_scope_on_stack(self.origin, || (callback.callback)(arguments))
         } else {
         } else {
             panic!("Callback was manually dropped")
             panic!("Callback was manually dropped")
         }
         }
@@ -511,24 +500,19 @@ impl<Args: 'static, Ret: 'static> Callback<Args, Ret> {
         self.callback.set(None);
         self.callback.set(None);
     }
     }
 
 
-    #[doc(hidden)]
-    /// This should only be used by the `rsx!` macro.
-    pub fn __set(&mut self, value: Rc<RefCell<dyn FnMut(Args) -> Ret>>) {
+    /// Replace the function in the callback with a new one
+    pub fn replace(&mut self, callback: Box<dyn FnMut(Args) -> Ret>) {
+        let runtime = Runtime::current().unwrap_or_else(|e| panic!("{}", e));
         self.callback.set(Some(ExternalListenerCallback {
         self.callback.set(Some(ExternalListenerCallback {
-            callback: value,
-            runtime: Rc::downgrade(&Runtime::current().unwrap()),
+            callback,
+            runtime: Rc::downgrade(&runtime),
         }));
         }));
     }
     }
 
 
     #[doc(hidden)]
     #[doc(hidden)]
     /// This should only be used by the `rsx!` macro.
     /// This should only be used by the `rsx!` macro.
-    pub fn __take(&self) -> Rc<RefCell<dyn FnMut(Args) -> Ret>> {
-        self.callback
-            .read()
-            .as_ref()
-            .expect("Callback was manually dropped")
-            .callback
-            .clone()
+    pub fn __point_to(&mut self, other: &Self) {
+        self.callback.point_to(other.callback).unwrap();
     }
     }
 }
 }
 
 

+ 3 - 0
packages/core/src/lib.rs

@@ -41,6 +41,9 @@ pub mod internal {
         HotReloadDynamicAttribute, HotReloadDynamicNode, HotReloadLiteral,
         HotReloadDynamicAttribute, HotReloadDynamicNode, HotReloadLiteral,
         HotReloadTemplateWithLocation, HotReloadedTemplate, HotreloadedLiteral, NamedAttribute,
         HotReloadTemplateWithLocation, HotReloadedTemplate, HotreloadedLiteral, NamedAttribute,
     };
     };
+
+    #[doc(hidden)]
+    pub use generational_box;
 }
 }
 
 
 pub(crate) mod innerlude {
 pub(crate) mod innerlude {

+ 1 - 1
packages/core/src/suspense/component.rs

@@ -50,7 +50,7 @@ where
     }
     }
     fn memoize(&mut self, new: &Self) -> bool {
     fn memoize(&mut self, new: &Self) -> bool {
         let equal = self == new;
         let equal = self == new;
-        self.fallback.__set(new.fallback.__take());
+        self.fallback.__point_to(&new.fallback);
         if !equal {
         if !equal {
             let new_clone = new.clone();
             let new_clone = new.clone();
             self.children = new_clone.children;
             self.children = new_clone.children;

+ 1 - 0
packages/generational-box/Cargo.toml

@@ -11,6 +11,7 @@ keywords = ["generational", "box", "memory", "allocator"]
 
 
 [dependencies]
 [dependencies]
 parking_lot = "0.12.1"
 parking_lot = "0.12.1"
+tracing.workspace = true
 
 
 [dev-dependencies]
 [dev-dependencies]
 rand = { workspace = true }
 rand = { workspace = true }

+ 40 - 10
packages/generational-box/src/entry.rs

@@ -2,36 +2,66 @@ use crate::{
     BorrowError, BorrowMutError, GenerationalLocation, GenerationalRefBorrowGuard,
     BorrowError, BorrowMutError, GenerationalLocation, GenerationalRefBorrowGuard,
     GenerationalRefBorrowMutGuard,
     GenerationalRefBorrowMutGuard,
 };
 };
+use std::{
+    num::NonZeroU64,
+    sync::atomic::{AtomicU64, Ordering},
+};
 
 
-pub(crate) struct StorageEntry<T> {
-    generation: u64,
-
-    pub data: Option<T>,
+pub(crate) struct RcStorageEntry<T> {
+    ref_count: AtomicU64,
+    pub data: T,
 }
 }
 
 
-impl<T> Default for StorageEntry<T> {
-    fn default() -> Self {
+impl<T> RcStorageEntry<T> {
+    pub const fn new(data: T) -> Self {
         Self {
         Self {
-            generation: 0,
-            data: None,
+            ref_count: AtomicU64::new(0),
+            data,
         }
         }
     }
     }
+
+    pub fn add_ref(&self) {
+        self.ref_count.fetch_add(1, Ordering::SeqCst);
+    }
+
+    pub fn drop_ref(&self) -> bool {
+        let new_ref_count = self.ref_count.fetch_sub(1, Ordering::SeqCst);
+        new_ref_count == 0
+    }
+}
+
+pub(crate) struct StorageEntry<T> {
+    generation: NonZeroU64,
+    pub(crate) data: T,
 }
 }
 
 
 impl<T> StorageEntry<T> {
 impl<T> StorageEntry<T> {
+    pub const fn new(data: T) -> Self {
+        Self {
+            generation: NonZeroU64::MIN,
+            data,
+        }
+    }
+
     pub fn valid(&self, location: &GenerationalLocation) -> bool {
     pub fn valid(&self, location: &GenerationalLocation) -> bool {
         self.generation == location.generation
         self.generation == location.generation
     }
     }
 
 
     pub fn increment_generation(&mut self) {
     pub fn increment_generation(&mut self) {
-        self.generation += 1;
+        self.generation = self.generation.checked_add(1).unwrap();
     }
     }
 
 
-    pub fn generation(&self) -> u64 {
+    pub fn generation(&self) -> NonZeroU64 {
         self.generation
         self.generation
     }
     }
 }
 }
 
 
+impl<T: Default + 'static> Default for StorageEntry<T> {
+    fn default() -> Self {
+        Self::new(T::default())
+    }
+}
+
 #[derive(Default)]
 #[derive(Default)]
 pub(crate) struct MemoryLocationBorrowInfo(
 pub(crate) struct MemoryLocationBorrowInfo(
     #[cfg(any(debug_assertions, feature = "debug_borrows"))]
     #[cfg(any(debug_assertions, feature = "debug_borrows"))]

+ 13 - 1
packages/generational-box/src/error.rs

@@ -5,7 +5,10 @@ use std::fmt::Display;
 use crate::GenerationalLocation;
 use crate::GenerationalLocation;
 
 
 /// A result that can be returned from a borrow operation.
 /// A result that can be returned from a borrow operation.
-pub type BorrowResult<T> = std::result::Result<T, BorrowError>;
+pub type BorrowResult<T = ()> = std::result::Result<T, BorrowError>;
+
+/// A result that can be returned from a borrow mut operation.
+pub type BorrowMutResult<T = ()> = std::result::Result<T, BorrowMutError>;
 
 
 #[derive(Debug, Clone, PartialEq)]
 #[derive(Debug, Clone, PartialEq)]
 /// An error that can occur when trying to borrow a value.
 /// An error that can occur when trying to borrow a value.
@@ -50,6 +53,15 @@ impl Display for BorrowMutError {
 
 
 impl Error for BorrowMutError {}
 impl Error for BorrowMutError {}
 
 
+impl From<BorrowError> for BorrowMutError {
+    fn from(error: BorrowError) -> Self {
+        match error {
+            BorrowError::Dropped(error) => BorrowMutError::Dropped(error),
+            BorrowError::AlreadyBorrowedMut(error) => BorrowMutError::AlreadyBorrowedMut(error),
+        }
+    }
+}
+
 /// An error that can occur when trying to use a value that has been dropped.
 /// An error that can occur when trying to use a value that has been dropped.
 #[derive(Debug, Copy, Clone, PartialEq)]
 #[derive(Debug, Copy, Clone, PartialEq)]
 pub struct ValueDroppedError {
 pub struct ValueDroppedError {

+ 117 - 52
packages/generational-box/src/lib.rs

@@ -5,6 +5,7 @@ use parking_lot::Mutex;
 use std::{
 use std::{
     fmt::Debug,
     fmt::Debug,
     marker::PhantomData,
     marker::PhantomData,
+    num::NonZeroU64,
     ops::{Deref, DerefMut},
     ops::{Deref, DerefMut},
     sync::Arc,
     sync::Arc,
 };
 };
@@ -24,7 +25,7 @@ mod unsync;
 #[derive(Clone, Copy, PartialEq, Eq, Hash)]
 #[derive(Clone, Copy, PartialEq, Eq, Hash)]
 pub struct GenerationalBoxId {
 pub struct GenerationalBoxId {
     data_ptr: *const (),
     data_ptr: *const (),
-    generation: u64,
+    generation: NonZeroU64,
 }
 }
 
 
 // Safety: GenerationalBoxId is Send and Sync because there is no way to access the pointer.
 // Safety: GenerationalBoxId is Send and Sync because there is no way to access the pointer.
@@ -54,9 +55,19 @@ impl<T, S: Storage<T>> GenerationalBox<T, S> {
     /// Create a new generational box by leaking a value into the storage. This is useful for creating
     /// Create a new generational box by leaking a value into the storage. This is useful for creating
     /// a box that needs to be manually dropped with no owners.
     /// a box that needs to be manually dropped with no owners.
     #[track_caller]
     #[track_caller]
-    pub fn leak(value: T) -> Self {
-        let location = S::claim(std::panic::Location::caller());
-        location.set(value);
+    pub fn leak(value: T, location: &'static std::panic::Location<'static>) -> Self {
+        let location = S::new(value, location);
+        Self {
+            raw: location,
+            _marker: PhantomData,
+        }
+    }
+
+    /// Create a new reference counted generational box by leaking a value into the storage. This is useful for creating
+    /// a box that needs to be manually dropped with no owners.
+    #[track_caller]
+    pub fn leak_rc(value: T, location: &'static std::panic::Location<'static>) -> Self {
+        let location = S::new_rc(value, location);
         Self {
         Self {
             raw: location,
             raw: location,
             _marker: PhantomData,
             _marker: PhantomData,
@@ -98,8 +109,12 @@ impl<T, S: Storage<T>> GenerationalBox<T, S> {
     }
     }
 
 
     /// Set the value. Panics if the value is no longer valid.
     /// Set the value. Panics if the value is no longer valid.
-    pub fn set(&self, value: T) {
-        S::set(self.raw, value);
+    #[track_caller]
+    pub fn set(&self, value: T)
+    where
+        T: 'static,
+    {
+        *self.write() = value;
     }
     }
 
 
     /// Returns true if the pointer is equal to the other pointer.
     /// Returns true if the pointer is equal to the other pointer.
@@ -108,24 +123,30 @@ impl<T, S: Storage<T>> GenerationalBox<T, S> {
     }
     }
 
 
     /// Drop the value out of the generational box and invalidate the generational box.
     /// Drop the value out of the generational box and invalidate the generational box.
-    /// This will return the value if the value was taken.
-    pub fn manually_drop(&self) -> Option<T>
+    pub fn manually_drop(&self)
     where
     where
         T: 'static,
         T: 'static,
     {
     {
-        self.raw.take()
+        self.raw.recycle();
     }
     }
 
 
     /// Try to get the location the generational box was created at. In release mode this will always return None.
     /// Try to get the location the generational box was created at. In release mode this will always return None.
     pub fn created_at(&self) -> Option<&'static std::panic::Location<'static>> {
     pub fn created_at(&self) -> Option<&'static std::panic::Location<'static>> {
-        #[cfg(debug_assertions)]
-        {
-            Some(self.raw.location.created_at)
-        }
-        #[cfg(not(debug_assertions))]
-        {
-            None
-        }
+        self.raw.location.created_at()
+    }
+
+    /// Get a reference to the value
+    #[track_caller]
+    pub fn leak_reference(&self) -> BorrowResult<GenerationalBox<T, S>> {
+        Ok(Self {
+            raw: S::new_reference(self.raw)?,
+            _marker: std::marker::PhantomData,
+        })
+    }
+
+    /// Change this box to point to another generational box
+    pub fn point_to(&mut self, other: GenerationalBox<T, S>) -> BorrowResult {
+        S::change_reference(self.raw, other.raw)
     }
     }
 }
 }
 
 
@@ -140,17 +161,35 @@ impl<T, S> Clone for GenerationalBox<T, S> {
 /// A trait for a storage backing type. (RefCell, RwLock, etc.)
 /// A trait for a storage backing type. (RefCell, RwLock, etc.)
 pub trait Storage<Data = ()>: AnyStorage + 'static {
 pub trait Storage<Data = ()>: AnyStorage + 'static {
     /// Try to read the value. Returns None if the value is no longer valid.
     /// Try to read the value. Returns None if the value is no longer valid.
-    fn try_read(
-        location: GenerationalPointer<Self>,
-    ) -> Result<Self::Ref<'static, Data>, BorrowError>;
+    fn try_read(pointer: GenerationalPointer<Self>) -> BorrowResult<Self::Ref<'static, Data>>;
 
 
     /// Try to write the value. Returns None if the value is no longer valid.
     /// Try to write the value. Returns None if the value is no longer valid.
-    fn try_write(
-        location: GenerationalPointer<Self>,
-    ) -> Result<Self::Mut<'static, Data>, BorrowMutError>;
+    fn try_write(pointer: GenerationalPointer<Self>) -> BorrowMutResult<Self::Mut<'static, Data>>;
+
+    /// Create a new memory location. This will either create a new memory location or recycle an old one.
+    fn new(
+        value: Data,
+        caller: &'static std::panic::Location<'static>,
+    ) -> GenerationalPointer<Self>;
+
+    /// Create a new reference counted memory location. This will either create a new memory location or recycle an old one.
+    fn new_rc(
+        value: Data,
+        caller: &'static std::panic::Location<'static>,
+    ) -> GenerationalPointer<Self>;
+
+    /// Reference another location if the location is valid
+    ///
+    /// This method may return an error if the other box is no longer valid or it is already borrowed mutably.
+    fn new_reference(inner: GenerationalPointer<Self>) -> BorrowResult<GenerationalPointer<Self>>;
 
 
-    /// Set the value if the location is valid
-    fn set(location: GenerationalPointer<Self>, value: Data);
+    /// Change the reference a signal is pointing to
+    ///
+    /// This method may return an error if the other box is no longer valid or it is already borrowed mutably.
+    fn change_reference(
+        pointer: GenerationalPointer<Self>,
+        rc_pointer: GenerationalPointer<Self>,
+    ) -> BorrowResult;
 }
 }
 
 
 /// A trait for any storage backing type.
 /// A trait for any storage backing type.
@@ -206,10 +245,7 @@ pub trait AnyStorage: Default + 'static {
     fn data_ptr(&self) -> *const ();
     fn data_ptr(&self) -> *const ();
 
 
     /// Recycle a memory location. This will drop the memory location and return it to the runtime.
     /// Recycle a memory location. This will drop the memory location and return it to the runtime.
-    fn recycle(location: GenerationalPointer<Self>) -> Option<Box<dyn std::any::Any>>;
-
-    /// Claim a new memory location. This will either create a new memory location or recycle an old one.
-    fn claim(caller: &'static std::panic::Location<'static>) -> GenerationalPointer<Self>;
+    fn recycle(location: GenerationalPointer<Self>);
 
 
     /// Create a new owner. The owner will be responsible for dropping all of the generational boxes that it creates.
     /// Create a new owner. The owner will be responsible for dropping all of the generational boxes that it creates.
     fn owner() -> Owner<Self> {
     fn owner() -> Owner<Self> {
@@ -222,11 +258,24 @@ pub trait AnyStorage: Default + 'static {
 #[derive(Debug, Clone, Copy)]
 #[derive(Debug, Clone, Copy)]
 pub(crate) struct GenerationalLocation {
 pub(crate) struct GenerationalLocation {
     /// The generation this location is associated with. Using the location after this generation is invalidated will return errors.
     /// The generation this location is associated with. Using the location after this generation is invalidated will return errors.
-    generation: u64,
+    generation: NonZeroU64,
     #[cfg(any(debug_assertions, feature = "debug_ownership"))]
     #[cfg(any(debug_assertions, feature = "debug_ownership"))]
     created_at: &'static std::panic::Location<'static>,
     created_at: &'static std::panic::Location<'static>,
 }
 }
 
 
+impl GenerationalLocation {
+    pub(crate) fn created_at(&self) -> Option<&'static std::panic::Location<'static>> {
+        #[cfg(debug_assertions)]
+        {
+            Some(self.created_at)
+        }
+        #[cfg(not(debug_assertions))]
+        {
+            None
+        }
+    }
+}
+
 /// A pointer to a specific generational box and generation in that box.
 /// A pointer to a specific generational box and generation in that box.
 pub struct GenerationalPointer<S: 'static = UnsyncStorage> {
 pub struct GenerationalPointer<S: 'static = UnsyncStorage> {
     /// The storage that is backing this location
     /// The storage that is backing this location
@@ -261,20 +310,6 @@ impl<S: 'static> Clone for GenerationalPointer<S> {
 impl<S: 'static> Copy for GenerationalPointer<S> {}
 impl<S: 'static> Copy for GenerationalPointer<S> {}
 
 
 impl<S> GenerationalPointer<S> {
 impl<S> GenerationalPointer<S> {
-    fn take<T: 'static>(self) -> Option<T>
-    where
-        S: Storage<T>,
-    {
-        S::recycle(self).map(|value| *(value.downcast().unwrap()))
-    }
-
-    fn set<T>(self, value: T)
-    where
-        S: Storage<T>,
-    {
-        S::set(self, value)
-    }
-
     #[track_caller]
     #[track_caller]
     fn try_read<T>(self) -> Result<S::Ref<'static, T>, BorrowError>
     fn try_read<T>(self) -> Result<S::Ref<'static, T>, BorrowError>
     where
     where
@@ -346,8 +381,17 @@ impl<S: AnyStorage> Owner<S> {
         self.insert_with_caller(value, std::panic::Location::caller())
         self.insert_with_caller(value, std::panic::Location::caller())
     }
     }
 
 
+    /// Create a new reference counted box. The box will be dropped when all references are dropped.
+    #[track_caller]
+    pub fn insert_rc<T: 'static>(&self, value: T) -> GenerationalBox<T, S>
+    where
+        S: Storage<T>,
+    {
+        self.insert_rc_with_caller(value, std::panic::Location::caller())
+    }
+
     /// Insert a value into the store with a specific location blamed for creating the value. The value will be dropped when the owner is dropped.
     /// Insert a value into the store with a specific location blamed for creating the value. The value will be dropped when the owner is dropped.
-    pub fn insert_with_caller<T: 'static>(
+    pub fn insert_rc_with_caller<T: 'static>(
         &self,
         &self,
         value: T,
         value: T,
         caller: &'static std::panic::Location<'static>,
         caller: &'static std::panic::Location<'static>,
@@ -355,23 +399,44 @@ impl<S: AnyStorage> Owner<S> {
     where
     where
         S: Storage<T>,
         S: Storage<T>,
     {
     {
-        let location = S::claim(caller);
-        location.set(value);
+        let location = S::new_rc(value, caller);
         self.0.lock().owned.push(location);
         self.0.lock().owned.push(location);
         GenerationalBox {
         GenerationalBox {
             raw: location,
             raw: location,
-            _marker: PhantomData,
+            _marker: std::marker::PhantomData,
         }
         }
     }
     }
 
 
-    /// Creates an invalid handle. This is useful for creating a handle that will be filled in later. If you use this before the value is filled in, you will get may get a panic or an out of date value.
-    #[track_caller]
-    pub fn invalid<T: 'static>(&self) -> GenerationalBox<T, S> {
-        let location = S::claim(std::panic::Location::caller());
+    /// Insert a value into the store with a specific location blamed for creating the value. The value will be dropped when the owner is dropped.
+    pub fn insert_with_caller<T: 'static>(
+        &self,
+        value: T,
+        caller: &'static std::panic::Location<'static>,
+    ) -> GenerationalBox<T, S>
+    where
+        S: Storage<T>,
+    {
+        let location = S::new(value, caller);
         self.0.lock().owned.push(location);
         self.0.lock().owned.push(location);
         GenerationalBox {
         GenerationalBox {
             raw: location,
             raw: location,
             _marker: PhantomData,
             _marker: PhantomData,
         }
         }
     }
     }
+
+    /// Create a new reference to an existing box. The reference will be dropped when the owner is dropped.
+    ///
+    /// This method may return an error if the other box is no longer valid or it is already borrowed mutably.
+    #[track_caller]
+    pub fn insert_reference<T: 'static>(
+        &self,
+        other: GenerationalBox<T, S>,
+    ) -> BorrowResult<GenerationalBox<T, S>>
+    where
+        S: Storage<T>,
+    {
+        let location = other.leak_reference()?;
+        self.0.lock().owned.push(location.raw);
+        Ok(location)
+    }
 }
 }

+ 272 - 52
packages/generational-box/src/sync.rs

@@ -1,20 +1,174 @@
 use parking_lot::{
 use parking_lot::{
     MappedRwLockReadGuard, MappedRwLockWriteGuard, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard,
     MappedRwLockReadGuard, MappedRwLockWriteGuard, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard,
 };
 };
-use std::sync::{Arc, OnceLock};
+use std::{
+    any::Any,
+    fmt::Debug,
+    num::NonZeroU64,
+    sync::{Arc, OnceLock},
+};
 
 
 use crate::{
 use crate::{
-    entry::{MemoryLocationBorrowInfo, StorageEntry},
+    entry::{MemoryLocationBorrowInfo, RcStorageEntry, StorageEntry},
     error::{self, ValueDroppedError},
     error::{self, ValueDroppedError},
     references::{GenerationalRef, GenerationalRefMut},
     references::{GenerationalRef, GenerationalRefMut},
-    AnyStorage, GenerationalLocation, GenerationalPointer, Storage,
+    AnyStorage, BorrowError, BorrowMutError, BorrowMutResult, BorrowResult, GenerationalLocation,
+    GenerationalPointer, Storage,
 };
 };
 
 
+type RwLockStorageEntryRef = RwLockReadGuard<'static, StorageEntry<RwLockStorageEntryData>>;
+type RwLockStorageEntryMut = RwLockWriteGuard<'static, StorageEntry<RwLockStorageEntryData>>;
+
+pub(crate) enum RwLockStorageEntryData {
+    Reference(GenerationalPointer<SyncStorage>),
+    Rc(RcStorageEntry<Box<dyn Any + Send + Sync>>),
+    Data(Box<dyn Any + Send + Sync>),
+    Empty,
+}
+
+impl Default for RwLockStorageEntryData {
+    fn default() -> Self {
+        Self::Empty
+    }
+}
+
+impl Debug for RwLockStorageEntryData {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Self::Reference(location) => write!(f, "Reference({:?})", location),
+            Self::Rc(_) => write!(f, "Rc"),
+            Self::Data(_) => write!(f, "Data"),
+            Self::Empty => write!(f, "Empty"),
+        }
+    }
+}
+
+impl RwLockStorageEntryData {
+    pub const fn new_full(data: Box<dyn Any + Send + Sync>) -> Self {
+        Self::Data(data)
+    }
+}
+
 /// A thread safe storage. This is slower than the unsync storage, but allows you to share the value between threads.
 /// A thread safe storage. This is slower than the unsync storage, but allows you to share the value between threads.
 #[derive(Default)]
 #[derive(Default)]
 pub struct SyncStorage {
 pub struct SyncStorage {
     borrow_info: MemoryLocationBorrowInfo,
     borrow_info: MemoryLocationBorrowInfo,
-    data: RwLock<StorageEntry<Box<dyn std::any::Any + Send + Sync>>>,
+    data: RwLock<StorageEntry<RwLockStorageEntryData>>,
+}
+
+impl SyncStorage {
+    pub(crate) fn read(
+        pointer: GenerationalPointer<Self>,
+    ) -> BorrowResult<MappedRwLockReadGuard<'static, Box<dyn Any + Send + Sync + 'static>>> {
+        Self::get_split_ref(pointer).map(|(_, guard)| {
+            RwLockReadGuard::map(guard, |data| match &data.data {
+                RwLockStorageEntryData::Data(data) => data,
+                RwLockStorageEntryData::Rc(data) => &data.data,
+                _ => unreachable!(),
+            })
+        })
+    }
+
+    pub(crate) fn get_split_ref(
+        mut pointer: GenerationalPointer<Self>,
+    ) -> BorrowResult<(GenerationalPointer<Self>, RwLockStorageEntryRef)> {
+        loop {
+            let borrow = pointer.storage.data.read();
+            if !borrow.valid(&pointer.location) {
+                return Err(BorrowError::Dropped(ValueDroppedError::new_for_location(
+                    pointer.location,
+                )));
+            }
+            match &borrow.data {
+                // If this is a reference, keep traversing the pointers
+                RwLockStorageEntryData::Reference(data) => {
+                    pointer = *data;
+                }
+                // Otherwise return the value
+                RwLockStorageEntryData::Data(_) | RwLockStorageEntryData::Rc(_) => {
+                    return Ok((pointer, borrow));
+                }
+                RwLockStorageEntryData::Empty => {
+                    return Err(BorrowError::Dropped(ValueDroppedError::new_for_location(
+                        pointer.location,
+                    )));
+                }
+            }
+        }
+    }
+
+    pub(crate) fn write(
+        pointer: GenerationalPointer<Self>,
+    ) -> BorrowMutResult<MappedRwLockWriteGuard<'static, Box<dyn Any + Send + Sync + 'static>>>
+    {
+        Self::get_split_mut(pointer).map(|(_, guard)| {
+            RwLockWriteGuard::map(guard, |data| match &mut data.data {
+                RwLockStorageEntryData::Data(data) => data,
+                RwLockStorageEntryData::Rc(data) => &mut data.data,
+                _ => unreachable!(),
+            })
+        })
+    }
+
+    pub(crate) fn get_split_mut(
+        mut pointer: GenerationalPointer<Self>,
+    ) -> BorrowMutResult<(GenerationalPointer<Self>, RwLockStorageEntryMut)> {
+        loop {
+            let borrow = pointer.storage.data.write();
+            if !borrow.valid(&pointer.location) {
+                return Err(BorrowMutError::Dropped(
+                    ValueDroppedError::new_for_location(pointer.location),
+                ));
+            }
+            match &borrow.data {
+                // If this is a reference, keep traversing the pointers
+                RwLockStorageEntryData::Reference(data) => {
+                    pointer = *data;
+                }
+                // Otherwise return the value
+                RwLockStorageEntryData::Data(_) | RwLockStorageEntryData::Rc(_) => {
+                    return Ok((pointer, borrow));
+                }
+                RwLockStorageEntryData::Empty => {
+                    return Err(BorrowMutError::Dropped(
+                        ValueDroppedError::new_for_location(pointer.location),
+                    ));
+                }
+            }
+        }
+    }
+
+    fn create_new(
+        value: RwLockStorageEntryData,
+        #[allow(unused)] caller: &'static std::panic::Location<'static>,
+    ) -> GenerationalPointer<Self> {
+        match sync_runtime().lock().pop() {
+            Some(storage) => {
+                let mut write = storage.data.write();
+                let location = GenerationalLocation {
+                    generation: write.generation(),
+                    #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+                    created_at: caller,
+                };
+                write.data = value;
+                GenerationalPointer { storage, location }
+            }
+            None => {
+                let storage: &'static Self = &*Box::leak(Box::new(Self {
+                    borrow_info: Default::default(),
+                    data: RwLock::new(StorageEntry::new(value)),
+                }));
+
+                let location = GenerationalLocation {
+                    generation: NonZeroU64::MIN,
+                    #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+                    created_at: caller,
+                };
+
+                GenerationalPointer { storage, location }
+            }
+        }
+    }
 }
 }
 
 
 static SYNC_RUNTIME: OnceLock<Arc<Mutex<Vec<&'static SyncStorage>>>> = OnceLock::new();
 static SYNC_RUNTIME: OnceLock<Arc<Mutex<Vec<&'static SyncStorage>>>> = OnceLock::new();
@@ -71,42 +225,50 @@ impl AnyStorage for SyncStorage {
         self.data.data_ptr() as *const ()
         self.data.data_ptr() as *const ()
     }
     }
 
 
-    #[track_caller]
-    #[allow(unused)]
-    fn claim(caller: &'static std::panic::Location<'static>) -> GenerationalPointer<Self> {
-        match sync_runtime().lock().pop() {
-            Some(mut storage) => {
-                let location = GenerationalLocation {
-                    generation: storage.data.read().generation(),
-                    #[cfg(any(debug_assertions, feature = "debug_borrows"))]
-                    created_at: caller,
-                };
-                GenerationalPointer { storage, location }
-            }
-            None => {
-                let storage: &'static Self = &*Box::leak(Box::default());
+    fn recycle(pointer: GenerationalPointer<Self>) {
+        let mut borrow_mut = pointer.storage.data.write();
 
 
-                let location = GenerationalLocation {
-                    generation: 0,
-                    #[cfg(any(debug_assertions, feature = "debug_borrows"))]
-                    created_at: caller,
-                };
+        // First check if the generation is still valid
+        if !borrow_mut.valid(&pointer.location) {
+            return;
+        }
 
 
-                GenerationalPointer { storage, location }
+        borrow_mut.increment_generation();
+
+        // Then decrement the reference count or drop the value if it's the last reference
+        match &mut borrow_mut.data {
+            // If this is the original reference, drop the value
+            RwLockStorageEntryData::Data(_) => borrow_mut.data = RwLockStorageEntryData::Empty,
+            // If this is a rc, just ignore the drop
+            RwLockStorageEntryData::Rc(_) => {}
+            // If this is a reference, decrement the reference count
+            RwLockStorageEntryData::Reference(reference) => {
+                drop_ref(*reference);
             }
             }
+            RwLockStorageEntryData::Empty => {}
         }
         }
+
+        sync_runtime().lock().push(pointer.storage);
     }
     }
+}
 
 
-    fn recycle(pointer: GenerationalPointer<Self>) -> Option<Box<dyn std::any::Any>> {
-        let mut borrow_mut = pointer.storage.data.write();
-        // First check if the generation is still valid
-        if !borrow_mut.valid(&pointer.location) {
-            return None;
+fn drop_ref(pointer: GenerationalPointer<SyncStorage>) {
+    let mut borrow_mut = pointer.storage.data.write();
+
+    // First check if the generation is still valid
+    if !borrow_mut.valid(&pointer.location) {
+        return;
+    }
+
+    if let RwLockStorageEntryData::Rc(entry) = &mut borrow_mut.data {
+        // Decrement the reference count
+        if entry.drop_ref() {
+            // If the reference count is now zero, drop the value
+            borrow_mut.data = RwLockStorageEntryData::Empty;
+            sync_runtime().lock().push(pointer.storage);
         }
         }
-        borrow_mut.increment_generation();
-        let old_data = borrow_mut.data.take();
-        sync_runtime().lock().push(pointer.storage);
-        old_data.map(|data| data as Box<dyn std::any::Any>)
+    } else {
+        unreachable!("References should always point to a data entry directly");
     }
     }
 }
 }
 
 
@@ -115,15 +277,11 @@ impl<T: Sync + Send + 'static> Storage<T> for SyncStorage {
     fn try_read(
     fn try_read(
         pointer: GenerationalPointer<Self>,
         pointer: GenerationalPointer<Self>,
     ) -> Result<Self::Ref<'static, T>, error::BorrowError> {
     ) -> Result<Self::Ref<'static, T>, error::BorrowError> {
-        let read = pointer.storage.data.read();
+        let read = Self::read(pointer)?;
 
 
-        let read = RwLockReadGuard::try_map(read, |any| {
-            // Verify the generation is still correct
-            if !any.valid(&pointer.location) {
-                return None;
-            }
+        let read = MappedRwLockReadGuard::try_map(read, |any| {
             // Then try to downcast
             // Then try to downcast
-            any.data.as_ref()?.downcast_ref()
+            any.downcast_ref()
         });
         });
         match read {
         match read {
             Ok(guard) => Ok(GenerationalRef::new(
             Ok(guard) => Ok(GenerationalRef::new(
@@ -140,15 +298,11 @@ impl<T: Sync + Send + 'static> Storage<T> for SyncStorage {
     fn try_write(
     fn try_write(
         pointer: GenerationalPointer<Self>,
         pointer: GenerationalPointer<Self>,
     ) -> Result<Self::Mut<'static, T>, error::BorrowMutError> {
     ) -> Result<Self::Mut<'static, T>, error::BorrowMutError> {
-        let write = pointer.storage.data.write();
+        let write = Self::write(pointer)?;
 
 
-        let write = RwLockWriteGuard::try_map(write, |any| {
-            // Verify the generation is still correct
-            if !any.valid(&pointer.location) {
-                return None;
-            }
+        let write = MappedRwLockWriteGuard::try_map(write, |any| {
             // Then try to downcast
             // Then try to downcast
-            any.data.as_mut()?.downcast_mut()
+            any.downcast_mut()
         });
         });
         match write {
         match write {
             Ok(guard) => Ok(GenerationalRefMut::new(
             Ok(guard) => Ok(GenerationalRefMut::new(
@@ -161,12 +315,78 @@ impl<T: Sync + Send + 'static> Storage<T> for SyncStorage {
         }
         }
     }
     }
 
 
-    fn set(pointer: GenerationalPointer<Self>, value: T) {
-        let mut write = pointer.storage.data.write();
+    fn new(value: T, caller: &'static std::panic::Location<'static>) -> GenerationalPointer<Self> {
+        Self::create_new(RwLockStorageEntryData::new_full(Box::new(value)), caller)
+    }
+
+    fn new_rc(
+        value: T,
+        caller: &'static std::panic::Location<'static>,
+    ) -> GenerationalPointer<Self> {
+        // Create the data that the rc points to
+        let data = Self::create_new(
+            RwLockStorageEntryData::Rc(RcStorageEntry::new(Box::new(value))),
+            caller,
+        );
+        Self::create_new(RwLockStorageEntryData::Reference(data), caller)
+    }
+
+    fn new_reference(
+        location: GenerationalPointer<Self>,
+    ) -> BorrowResult<GenerationalPointer<Self>> {
+        // Chase the reference to get the final location
+        let (location, value) = Self::get_split_ref(location)?;
+        if let RwLockStorageEntryData::Rc(data) = &value.data {
+            data.add_ref();
+        } else {
+            unreachable!()
+        }
+        Ok(Self::create_new(
+            RwLockStorageEntryData::Reference(location),
+            location
+                .location
+                .created_at()
+                .unwrap_or(std::panic::Location::caller()),
+        ))
+    }
+
+    fn change_reference(
+        location: GenerationalPointer<Self>,
+        other: GenerationalPointer<Self>,
+    ) -> BorrowResult {
+        if location == other {
+            return Ok(());
+        }
+
+        let (other_final, other_write) = Self::get_split_ref(other)?;
+
+        let mut write = location.storage.data.write();
         // First check if the generation is still valid
         // First check if the generation is still valid
-        if !write.valid(&pointer.location) {
-            return;
+        if !write.valid(&location.location) {
+            return Err(BorrowError::Dropped(ValueDroppedError::new_for_location(
+                location.location,
+            )));
+        }
+
+        if let (RwLockStorageEntryData::Reference(reference), RwLockStorageEntryData::Rc(data)) =
+            (&mut write.data, &other_write.data)
+        {
+            if reference == &other_final {
+                return Ok(());
+            }
+            drop_ref(*reference);
+            *reference = other_final;
+            data.add_ref();
+        } else {
+            tracing::trace!(
+                "References should always point to a data entry directly found {:?} instead",
+                other_write.data
+            );
+            return Err(BorrowError::Dropped(ValueDroppedError::new_for_location(
+                other_final.location,
+            )));
         }
         }
-        write.data = Some(Box::new(value));
+
+        Ok(())
     }
     }
 }
 }

+ 265 - 60
packages/generational-box/src/unsync.rs

@@ -1,35 +1,175 @@
 use crate::{
 use crate::{
-    entry::{MemoryLocationBorrowInfo, StorageEntry},
+    entry::{MemoryLocationBorrowInfo, RcStorageEntry, StorageEntry},
     error,
     error,
     references::{GenerationalRef, GenerationalRefMut},
     references::{GenerationalRef, GenerationalRefMut},
-    AnyStorage, BorrowError, BorrowMutError, GenerationalLocation, GenerationalPointer, Storage,
+    AnyStorage, BorrowError, BorrowMutError, BorrowMutResult, BorrowResult, GenerationalLocation,
+    GenerationalPointer, Storage, ValueDroppedError,
 };
 };
-use std::cell::{Ref, RefCell, RefMut};
+use std::{
+    any::Any,
+    cell::{Ref, RefCell, RefMut},
+    fmt::Debug,
+    num::NonZeroU64,
+};
+
+type RefCellStorageEntryRef = Ref<'static, StorageEntry<RefCellStorageEntryData>>;
+
+type RefCellStorageEntryMut = RefMut<'static, StorageEntry<RefCellStorageEntryData>>;
 
 
 thread_local! {
 thread_local! {
     static UNSYNC_RUNTIME: RefCell<Vec<&'static UnsyncStorage>> = const { RefCell::new(Vec::new()) };
     static UNSYNC_RUNTIME: RefCell<Vec<&'static UnsyncStorage>> = const { RefCell::new(Vec::new()) };
 }
 }
 
 
+pub(crate) enum RefCellStorageEntryData {
+    Reference(GenerationalPointer<UnsyncStorage>),
+    Rc(RcStorageEntry<Box<dyn Any>>),
+    Data(Box<dyn Any>),
+    Empty,
+}
+
+impl Debug for RefCellStorageEntryData {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Self::Reference(pointer) => write!(f, "Reference({:?})", pointer.location),
+            Self::Rc(_) => write!(f, "Rc"),
+            Self::Data(_) => write!(f, "Data"),
+            Self::Empty => write!(f, "Empty"),
+        }
+    }
+}
+
+impl Default for RefCellStorageEntryData {
+    fn default() -> Self {
+        Self::Empty
+    }
+}
+
 /// A unsync storage. This is the default storage type.
 /// A unsync storage. This is the default storage type.
 #[derive(Default)]
 #[derive(Default)]
 pub struct UnsyncStorage {
 pub struct UnsyncStorage {
     borrow_info: MemoryLocationBorrowInfo,
     borrow_info: MemoryLocationBorrowInfo,
-    data: RefCell<StorageEntry<Box<dyn std::any::Any>>>,
+    data: RefCell<StorageEntry<RefCellStorageEntryData>>,
 }
 }
 
 
 impl UnsyncStorage {
 impl UnsyncStorage {
-    fn try_borrow_mut(
-        &self,
-    ) -> Result<RefMut<'_, StorageEntry<Box<dyn std::any::Any>>>, BorrowMutError> {
-        self.data
-            .try_borrow_mut()
-            .map_err(|_| self.borrow_info.borrow_mut_error())
+    pub(crate) fn read(
+        pointer: GenerationalPointer<Self>,
+    ) -> BorrowResult<Ref<'static, Box<dyn Any>>> {
+        Self::get_split_ref(pointer).map(|(_, guard)| {
+            Ref::map(guard, |data| match &data.data {
+                RefCellStorageEntryData::Data(data) => data,
+                RefCellStorageEntryData::Rc(data) => &data.data,
+                _ => unreachable!(),
+            })
+        })
+    }
+
+    pub(crate) fn get_split_ref(
+        mut pointer: GenerationalPointer<Self>,
+    ) -> BorrowResult<(GenerationalPointer<Self>, RefCellStorageEntryRef)> {
+        loop {
+            let borrow = pointer
+                .storage
+                .data
+                .try_borrow()
+                .map_err(|_| pointer.storage.borrow_info.borrow_error())?;
+            if !borrow.valid(&pointer.location) {
+                return Err(BorrowError::Dropped(ValueDroppedError::new_for_location(
+                    pointer.location,
+                )));
+            }
+            match &borrow.data {
+                // If this is a reference, keep traversing the pointers
+                RefCellStorageEntryData::Reference(data) => {
+                    pointer = *data;
+                }
+                // Otherwise return the value
+                RefCellStorageEntryData::Rc(_) | RefCellStorageEntryData::Data(_) => {
+                    return Ok((pointer, borrow));
+                }
+                RefCellStorageEntryData::Empty => {
+                    return Err(BorrowError::Dropped(ValueDroppedError::new_for_location(
+                        pointer.location,
+                    )));
+                }
+            }
+        }
     }
     }
 
 
-    fn try_borrow(&self) -> Result<Ref<'_, StorageEntry<Box<dyn std::any::Any>>>, BorrowError> {
-        self.data
-            .try_borrow()
-            .map_err(|_| self.borrow_info.borrow_error())
+    pub(crate) fn write(
+        pointer: GenerationalPointer<Self>,
+    ) -> BorrowMutResult<RefMut<'static, Box<dyn Any>>> {
+        Self::get_split_mut(pointer).map(|(_, guard)| {
+            RefMut::map(guard, |data| match &mut data.data {
+                RefCellStorageEntryData::Data(data) => data,
+                RefCellStorageEntryData::Rc(data) => &mut data.data,
+                _ => unreachable!(),
+            })
+        })
+    }
+
+    pub(crate) fn get_split_mut(
+        mut pointer: GenerationalPointer<Self>,
+    ) -> BorrowMutResult<(GenerationalPointer<Self>, RefCellStorageEntryMut)> {
+        loop {
+            let borrow = pointer
+                .storage
+                .data
+                .try_borrow_mut()
+                .map_err(|_| pointer.storage.borrow_info.borrow_mut_error())?;
+            if !borrow.valid(&pointer.location) {
+                return Err(BorrowMutError::Dropped(
+                    ValueDroppedError::new_for_location(pointer.location),
+                ));
+            }
+            match &borrow.data {
+                // If this is a reference, keep traversing the pointers
+                RefCellStorageEntryData::Reference(data) => {
+                    pointer = *data;
+                }
+                // Otherwise return the value
+                RefCellStorageEntryData::Data(_) | RefCellStorageEntryData::Rc(_) => {
+                    return Ok((pointer, borrow));
+                }
+                RefCellStorageEntryData::Empty => {
+                    return Err(BorrowMutError::Dropped(
+                        ValueDroppedError::new_for_location(pointer.location),
+                    ));
+                }
+            }
+        }
+    }
+
+    fn create_new(
+        value: RefCellStorageEntryData,
+        #[allow(unused)] caller: &'static std::panic::Location<'static>,
+    ) -> GenerationalPointer<Self> {
+        UNSYNC_RUNTIME.with(|runtime| match runtime.borrow_mut().pop() {
+            Some(storage) => {
+                let mut write = storage.data.borrow_mut();
+                let location = GenerationalLocation {
+                    generation: write.generation(),
+                    #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+                    created_at: caller,
+                };
+                write.data = value;
+                GenerationalPointer { storage, location }
+            }
+            None => {
+                let storage: &'static Self = &*Box::leak(Box::new(Self {
+                    borrow_info: Default::default(),
+                    data: RefCell::new(StorageEntry::new(value)),
+                }));
+
+                let location = GenerationalLocation {
+                    generation: NonZeroU64::MIN,
+                    #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+                    created_at: caller,
+                };
+
+                GenerationalPointer { storage, location }
+            }
+        })
     }
     }
 }
 }
 
 
@@ -81,44 +221,51 @@ impl AnyStorage for UnsyncStorage {
         self.data.as_ptr() as *const ()
         self.data.as_ptr() as *const ()
     }
     }
 
 
-    #[allow(unused)]
-    fn claim(caller: &'static std::panic::Location<'static>) -> GenerationalPointer<Self> {
-        UNSYNC_RUNTIME.with(|runtime| {
-            if let Some(storage) = runtime.borrow_mut().pop() {
-                let location = GenerationalLocation {
-                    generation: storage.data.borrow().generation(),
-                    #[cfg(any(debug_assertions, feature = "debug_borrows"))]
-                    created_at: caller,
-                };
-                GenerationalPointer { storage, location }
-            } else {
-                let data: &'static Self = &*Box::leak(Box::default());
-                let location = GenerationalLocation {
-                    generation: 0,
-                    #[cfg(any(debug_assertions, feature = "debug_borrows"))]
-                    created_at: caller,
-                };
-                GenerationalPointer {
-                    storage: data,
-                    location,
-                }
-            }
-        })
-    }
-
-    fn recycle(pointer: GenerationalPointer<Self>) -> Option<Box<dyn std::any::Any>> {
+    fn recycle(pointer: GenerationalPointer<Self>) {
         let mut borrow_mut = pointer.storage.data.borrow_mut();
         let mut borrow_mut = pointer.storage.data.borrow_mut();
 
 
         // First check if the generation is still valid
         // First check if the generation is still valid
         if !borrow_mut.valid(&pointer.location) {
         if !borrow_mut.valid(&pointer.location) {
-            return None;
+            return;
         }
         }
 
 
         borrow_mut.increment_generation();
         borrow_mut.increment_generation();
-        let old_data = borrow_mut.data.take();
+        // Then decrement the reference count or drop the value if it's the last reference
+        match &mut borrow_mut.data {
+            // If this is the original reference, drop the value
+            RefCellStorageEntryData::Data(_) => borrow_mut.data = RefCellStorageEntryData::Empty,
+            // If this is a rc, just ignore the drop
+            RefCellStorageEntryData::Rc(_) => {}
+            // If this is a reference, decrement the reference count
+            RefCellStorageEntryData::Reference(reference) => {
+                let reference = *reference;
+                drop(borrow_mut);
+                drop_ref(reference);
+            }
+            RefCellStorageEntryData::Empty => {}
+        }
+
         UNSYNC_RUNTIME.with(|runtime| runtime.borrow_mut().push(pointer.storage));
         UNSYNC_RUNTIME.with(|runtime| runtime.borrow_mut().push(pointer.storage));
+    }
+}
+
+fn drop_ref(pointer: GenerationalPointer<UnsyncStorage>) {
+    let mut borrow_mut = pointer.storage.data.borrow_mut();
 
 
-        old_data
+    // First check if the generation is still valid
+    if !borrow_mut.valid(&pointer.location) {
+        return;
+    }
+
+    if let RefCellStorageEntryData::Rc(entry) = &mut borrow_mut.data {
+        // Decrement the reference count
+        if entry.drop_ref() {
+            // If the reference count is now zero, drop the value
+            borrow_mut.data = RefCellStorageEntryData::Empty;
+            UNSYNC_RUNTIME.with(|runtime| runtime.borrow_mut().push(pointer.storage));
+        }
+    } else {
+        unreachable!("References should always point to a data entry directly",);
     }
     }
 }
 }
 
 
@@ -127,15 +274,11 @@ impl<T: 'static> Storage<T> for UnsyncStorage {
     fn try_read(
     fn try_read(
         pointer: GenerationalPointer<Self>,
         pointer: GenerationalPointer<Self>,
     ) -> Result<Self::Ref<'static, T>, error::BorrowError> {
     ) -> Result<Self::Ref<'static, T>, error::BorrowError> {
-        let read = pointer.storage.try_borrow()?;
+        let read = Self::read(pointer)?;
 
 
         let ref_ = Ref::filter_map(read, |any| {
         let ref_ = Ref::filter_map(read, |any| {
-            // Verify the generation is still correct
-            if !any.valid(&pointer.location) {
-                return None;
-            }
             // Then try to downcast
             // Then try to downcast
-            any.data.as_ref()?.downcast_ref()
+            any.downcast_ref()
         });
         });
         match ref_ {
         match ref_ {
             Ok(guard) => Ok(GenerationalRef::new(
             Ok(guard) => Ok(GenerationalRef::new(
@@ -152,15 +295,11 @@ impl<T: 'static> Storage<T> for UnsyncStorage {
     fn try_write(
     fn try_write(
         pointer: GenerationalPointer<Self>,
         pointer: GenerationalPointer<Self>,
     ) -> Result<Self::Mut<'static, T>, error::BorrowMutError> {
     ) -> Result<Self::Mut<'static, T>, error::BorrowMutError> {
-        let write = pointer.storage.try_borrow_mut()?;
+        let write = Self::write(pointer)?;
 
 
         let ref_mut = RefMut::filter_map(write, |any| {
         let ref_mut = RefMut::filter_map(write, |any| {
-            // Verify the generation is still correct
-            if !any.valid(&pointer.location) {
-                return None;
-            }
             // Then try to downcast
             // Then try to downcast
-            any.data.as_mut()?.downcast_mut()
+            any.downcast_mut()
         });
         });
         match ref_mut {
         match ref_mut {
             Ok(guard) => Ok(GenerationalRefMut::new(
             Ok(guard) => Ok(GenerationalRefMut::new(
@@ -173,12 +312,78 @@ impl<T: 'static> Storage<T> for UnsyncStorage {
         }
         }
     }
     }
 
 
-    fn set(pointer: GenerationalPointer<Self>, value: T) {
-        let mut borrow_mut = pointer.storage.data.borrow_mut();
+    fn new(value: T, caller: &'static std::panic::Location<'static>) -> GenerationalPointer<Self> {
+        Self::create_new(RefCellStorageEntryData::Data(Box::new(value)), caller)
+    }
+
+    fn new_rc(
+        value: T,
+        caller: &'static std::panic::Location<'static>,
+    ) -> GenerationalPointer<Self> {
+        // Create the data that the rc points to
+        let data = Self::create_new(
+            RefCellStorageEntryData::Rc(RcStorageEntry::new(Box::new(value))),
+            caller,
+        );
+        Self::create_new(RefCellStorageEntryData::Reference(data), caller)
+    }
+
+    fn new_reference(
+        pointer: GenerationalPointer<Self>,
+    ) -> BorrowResult<GenerationalPointer<Self>> {
+        // Chase the reference to get the final location
+        let (pointer, value) = Self::get_split_ref(pointer)?;
+        if let RefCellStorageEntryData::Rc(data) = &value.data {
+            data.add_ref();
+        } else {
+            unreachable!()
+        }
+        Ok(Self::create_new(
+            RefCellStorageEntryData::Reference(pointer),
+            pointer
+                .location
+                .created_at()
+                .unwrap_or(std::panic::Location::caller()),
+        ))
+    }
+
+    fn change_reference(
+        location: GenerationalPointer<Self>,
+        other: GenerationalPointer<Self>,
+    ) -> BorrowResult {
+        if location == other {
+            return Ok(());
+        }
+
+        let (other_final, other_write) = Self::get_split_ref(other)?;
+
+        let mut write = location.storage.data.borrow_mut();
         // First check if the generation is still valid
         // First check if the generation is still valid
-        if !borrow_mut.valid(&pointer.location) {
-            return;
+        if !write.valid(&location.location) {
+            return Err(BorrowError::Dropped(ValueDroppedError::new_for_location(
+                location.location,
+            )));
         }
         }
-        borrow_mut.data = Some(Box::new(value));
+
+        if let (RefCellStorageEntryData::Reference(reference), RefCellStorageEntryData::Rc(data)) =
+            (&mut write.data, &other_write.data)
+        {
+            if reference == &other_final {
+                return Ok(());
+            }
+            drop_ref(*reference);
+            *reference = other_final;
+            data.add_ref();
+        } else {
+            tracing::trace!(
+                "References should always point to a data entry directly found {:?} instead",
+                other_write.data
+            );
+            return Err(BorrowError::Dropped(ValueDroppedError::new_for_location(
+                other_final.location,
+            )));
+        }
+
+        Ok(())
     }
     }
 }
 }

+ 61 - 0
packages/generational-box/tests/reference_counting.rs

@@ -0,0 +1,61 @@
+use generational_box::{Storage, SyncStorage, UnsyncStorage};
+
+#[test]
+fn reference_counting() {
+    fn reference_counting<S: Storage<String>>() {
+        let data = String::from("hello world");
+        let reference;
+        {
+            let outer_owner = S::owner();
+            {
+                // create an owner
+                let owner = S::owner();
+                // insert data into the store
+                let original = owner.insert_rc(data);
+                reference = outer_owner.insert_reference(original).unwrap();
+                // The reference should point to the value immediately
+                assert_eq!(&*reference.read(), "hello world");
+                // Original is dropped
+            }
+            // The reference should still point to the value
+            assert_eq!(&*reference.read(), "hello world");
+        }
+        // Now that all references are dropped, the value should be dropped
+        assert!(reference.try_read().is_err());
+    }
+
+    reference_counting::<UnsyncStorage>();
+    reference_counting::<SyncStorage>();
+}
+
+#[test]
+fn move_reference_in_place() {
+    fn move_reference_in_place<S: Storage<String>>() {
+        let data1 = String::from("hello world");
+        let data2 = String::from("hello world 2");
+
+        // create an owner
+        let original_owner = S::owner();
+        // insert data into the store
+        let original = original_owner.insert_rc(data1.clone());
+        let mut reference = original_owner.insert_reference(original).unwrap();
+        // The reference should point to the original value
+        assert_eq!(&*reference.read(), &data1);
+
+        let new_owner = S::owner();
+        // Move the reference in place
+        let new = new_owner.insert_rc(data2.clone());
+        reference.point_to(new).unwrap();
+        // The reference should point to the new value
+        assert_eq!(&*reference.read(), &data2);
+
+        // make sure both got dropped
+        drop(original_owner);
+        drop(new_owner);
+        assert!(original.try_read().is_err());
+        assert!(new.try_read().is_err());
+    }
+
+    move_reference_in_place::<UnsyncStorage>();
+    move_reference_in_place::<SyncStorage>();
+}

+ 1 - 4
packages/hooks/src/use_callback.rs

@@ -1,6 +1,3 @@
-use std::cell::RefCell;
-use std::rc::Rc;
-
 use dioxus_core::prelude::use_hook;
 use dioxus_core::prelude::use_hook;
 use dioxus_core::prelude::Callback;
 use dioxus_core::prelude::Callback;
 
 
@@ -19,7 +16,7 @@ pub fn use_callback<T: 'static, O: 'static>(f: impl FnMut(T) -> O + 'static) ->
 
 
     if let Some(callback) = callback.take() {
     if let Some(callback) = callback.take() {
         // Every time this hook is called replace the inner callback with the new callback
         // Every time this hook is called replace the inner callback with the new callback
-        inner.__set(Rc::new(RefCell::new(callback)));
+        inner.replace(Box::new(callback));
     }
     }
 
 
     inner
     inner

+ 2 - 1
packages/html/src/document/mod.rs

@@ -133,7 +133,8 @@ impl Document for NoOpDocument {
     }
     }
 }
 }
 
 
-struct NoOpEvaluator;
+/// The default No-Op evaluator
+pub struct NoOpEvaluator;
 impl Evaluator for NoOpEvaluator {
 impl Evaluator for NoOpEvaluator {
     fn send(&self, _data: serde_json::Value) -> Result<(), EvalError> {
     fn send(&self, _data: serde_json::Value) -> Result<(), EvalError> {
         Err(EvalError::Unsupported)
         Err(EvalError::Unsupported)

+ 16 - 3
packages/signals/src/copy_value.rs

@@ -71,6 +71,19 @@ impl<T: 'static, S: Storage<T>> CopyValue<T, S> {
         Self::new_with_caller(value, std::panic::Location::caller())
         Self::new_with_caller(value, std::panic::Location::caller())
     }
     }
 
 
+    /// Create a new CopyValue without an owner. This will leak memory if you don't manually drop it.
+    pub fn leak_with_caller(value: T, caller: &'static std::panic::Location<'static>) -> Self {
+        Self {
+            value: GenerationalBox::leak(value, caller),
+            origin_scope: current_scope_id().expect("in a virtual dom"),
+        }
+    }
+
+    /// Point to another copy value
+    pub fn point_to(&mut self, other: Self) -> BorrowResult {
+        self.value.point_to(other.value)
+    }
+
     pub(crate) fn new_with_caller(
     pub(crate) fn new_with_caller(
         value: T,
         value: T,
         caller: &'static std::panic::Location<'static>,
         caller: &'static std::panic::Location<'static>,
@@ -78,7 +91,7 @@ impl<T: 'static, S: Storage<T>> CopyValue<T, S> {
         let owner = current_owner();
         let owner = current_owner();
 
 
         Self {
         Self {
-            value: owner.insert_with_caller(value, caller),
+            value: owner.insert_rc_with_caller(value, caller),
             origin_scope: current_scope_id().expect("in a virtual dom"),
             origin_scope: current_scope_id().expect("in a virtual dom"),
         }
         }
     }
     }
@@ -99,13 +112,13 @@ impl<T: 'static, S: Storage<T>> CopyValue<T, S> {
         let owner = scope.owner();
         let owner = scope.owner();
 
 
         Self {
         Self {
-            value: owner.insert_with_caller(value, caller),
+            value: owner.insert_rc_with_caller(value, caller),
             origin_scope: scope,
             origin_scope: scope,
         }
         }
     }
     }
 
 
     /// Manually drop the value in the CopyValue, invalidating the value in the process.
     /// Manually drop the value in the CopyValue, invalidating the value in the process.
-    pub fn manually_drop(&self) -> Option<T> {
+    pub fn manually_drop(&self) {
         self.value.manually_drop()
         self.value.manually_drop()
     }
     }
 
 

+ 8 - 16
packages/signals/src/read_only_signal.rs

@@ -42,25 +42,17 @@ impl<T: 'static, S: Storage<SignalData<T>>> ReadOnlySignal<T, S> {
         self.inner.id()
         self.inner.id()
     }
     }
 
 
-    #[doc(hidden)]
-    /// This should only be used by the `rsx!` macro.
-    pub fn __set(&mut self, value: T) {
-        use crate::write::Writable;
-        use warnings::Warning;
-        // This is only called when converting T -> ReadOnlySignal<T> which will not cause loops
-        crate::warnings::signal_write_in_component_body::allow(|| {
-            crate::warnings::signal_read_and_write_in_reactive_scope::allow(|| {
-                self.inner.set(value);
-            });
-        });
+    /// Point to another signal
+    pub fn point_to(&mut self, other: Self) -> BorrowResult {
+        self.inner.point_to(other.inner)
     }
     }
 
 
     #[doc(hidden)]
     #[doc(hidden)]
-    /// This should only be used by the `rsx!` macro.
-    pub fn __take(&self) -> T {
-        self.inner
-            .manually_drop()
-            .expect("Signal has already been dropped")
+    /// This is only used by the `props` macro.
+    /// Mark any readers of the signal as dirty
+    pub fn mark_dirty(&mut self) {
+        use crate::write::Writable;
+        _ = self.inner.try_write();
     }
     }
 }
 }
 
 

+ 20 - 2
packages/signals/src/signal.rs

@@ -182,6 +182,19 @@ impl<T: 'static, S: Storage<SignalData<T>>> Signal<T, S> {
         }
         }
     }
     }
 
 
+    /// Create a new Signal without an owner. This will leak memory if you don't manually drop it.
+    pub fn leak_with_caller(value: T, caller: &'static std::panic::Location<'static>) -> Self {
+        Self {
+            inner: CopyValue::leak_with_caller(
+                SignalData {
+                    subscribers: Default::default(),
+                    value,
+                },
+                caller,
+            ),
+        }
+    }
+
     /// Create a new signal with a custom owner scope. The signal will be dropped when the owner scope is dropped instead of the current scope.
     /// Create a new signal with a custom owner scope. The signal will be dropped when the owner scope is dropped instead of the current scope.
     #[track_caller]
     #[track_caller]
     #[tracing::instrument(skip(value))]
     #[tracing::instrument(skip(value))]
@@ -208,9 +221,14 @@ impl<T: 'static, S: Storage<SignalData<T>>> Signal<T, S> {
         }
         }
     }
     }
 
 
+    /// Point to another signal
+    pub fn point_to(&mut self, other: Self) -> BorrowResult {
+        self.inner.point_to(other.inner)
+    }
+
     /// Drop the value out of the signal, invalidating the signal in the process.
     /// Drop the value out of the signal, invalidating the signal in the process.
-    pub fn manually_drop(&self) -> Option<T> {
-        self.inner.manually_drop().map(|i| i.value)
+    pub fn manually_drop(&self) {
+        self.inner.manually_drop()
     }
     }
 
 
     /// Get the scope the signal was created in.
     /// Get the scope the signal was created in.

+ 2 - 2
packages/web/src/document.rs

@@ -1,5 +1,5 @@
 use dioxus_core::ScopeId;
 use dioxus_core::ScopeId;
-use dioxus_html::document::{Document, EvalError, Evaluator};
+use dioxus_html::document::{Document, EvalError, Evaluator, NoOpEvaluator};
 use generational_box::{AnyStorage, GenerationalBox, UnsyncStorage};
 use generational_box::{AnyStorage, GenerationalBox, UnsyncStorage};
 use js_sys::Function;
 use js_sys::Function;
 use serde::Serialize;
 use serde::Serialize;
@@ -95,7 +95,7 @@ impl WebEvaluator {
     fn create(js: String) -> GenerationalBox<Box<dyn Evaluator>> {
     fn create(js: String) -> GenerationalBox<Box<dyn Evaluator>> {
         let owner = UnsyncStorage::owner();
         let owner = UnsyncStorage::owner();
 
 
-        let generational_box = owner.invalid();
+        let generational_box = owner.insert(Box::new(NoOpEvaluator) as Box<dyn Evaluator>);
 
 
         // add the drop handler to DioxusChannel so that it gets dropped when the channel is dropped in js
         // add the drop handler to DioxusChannel so that it gets dropped when the channel is dropped in js
         let channels = WebDioxusChannel::new(JSOwner::new(owner));
         let channels = WebDioxusChannel::new(JSOwner::new(owner));