瀏覽代碼

Add debug information to borrows and ownership in signals

Evan Almloff 1 年之前
父節點
當前提交
df85b25548

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

@@ -15,3 +15,4 @@ rand = "0.8.5"
 [features]
 default = ["check_generation"]
 check_generation = []
+debug_borrows = []

+ 404 - 41
packages/generational-box/src/lib.rs

@@ -2,9 +2,13 @@
 #![warn(missing_docs)]
 
 use std::{
+    any::Any,
     cell::{Cell, Ref, RefCell, RefMut},
-    fmt::Debug,
+    error::Error,
+    fmt::{Debug, Display},
     marker::PhantomData,
+    ops::{Deref, DerefMut},
+    panic::Location,
     rc::Rc,
 };
 
@@ -153,6 +157,8 @@ pub struct GenerationalBox<T> {
     raw: MemoryLocation,
     #[cfg(any(debug_assertions, feature = "check_generation"))]
     generation: u32,
+    #[cfg(any(debug_assertions, feature = "check_generation"))]
+    created_at: &'static std::panic::Location<'static>,
     _marker: PhantomData<T>,
 }
 
@@ -161,7 +167,7 @@ impl<T: 'static> Debug for GenerationalBox<T> {
         #[cfg(any(debug_assertions, feature = "check_generation"))]
         f.write_fmt(format_args!(
             "{:?}@{:?}",
-            self.raw.data.as_ptr(),
+            self.raw.0.data.as_ptr(),
             self.generation
         ))?;
         #[cfg(not(any(debug_assertions, feature = "check_generation")))]
@@ -175,7 +181,7 @@ impl<T: 'static> GenerationalBox<T> {
     fn validate(&self) -> bool {
         #[cfg(any(debug_assertions, feature = "check_generation"))]
         {
-            self.raw.generation.get() == self.generation
+            self.raw.0.generation.get() == self.generation
         }
         #[cfg(not(any(debug_assertions, feature = "check_generation")))]
         {
@@ -184,43 +190,51 @@ impl<T: 'static> GenerationalBox<T> {
     }
 
     /// Try to read the value. Returns None if the value is no longer valid.
-    pub fn try_read(&self) -> Option<Ref<'_, T>> {
-        self.validate()
-            .then(|| {
-                Ref::filter_map(self.raw.data.borrow(), |any| {
-                    any.as_ref()?.downcast_ref::<T>()
-                })
-                .ok()
-            })
-            .flatten()
+    #[track_caller]
+    pub fn try_read(&self) -> Result<GenerationalRef<'_, T>, BorrowError> {
+        if !self.validate() {
+            return Err(BorrowError::Dropped(ValueDroppedError {
+                #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+                created_at: self.created_at,
+            }));
+        }
+        self.raw.try_borrow(
+            #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+            self.created_at,
+        )
     }
 
     /// Read the value. Panics if the value is no longer valid.
-    pub fn read(&self) -> Ref<'_, T> {
+    #[track_caller]
+    pub fn read(&self) -> GenerationalRef<'_, T> {
         self.try_read().unwrap()
     }
 
     /// Try to write the value. Returns None if the value is no longer valid.
-    pub fn try_write(&self) -> Option<RefMut<'_, T>> {
-        self.validate()
-            .then(|| {
-                RefMut::filter_map(self.raw.data.borrow_mut(), |any| {
-                    any.as_mut()?.downcast_mut::<T>()
-                })
-                .ok()
-            })
-            .flatten()
+    #[track_caller]
+    pub fn try_write(&self) -> Result<GenerationalRefMut<'_, T>, BorrowMutError> {
+        if !self.validate() {
+            return Err(BorrowMutError::Dropped(ValueDroppedError {
+                #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+                created_at: self.created_at,
+            }));
+        }
+        self.raw.try_borrow_mut(
+            #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+            self.created_at,
+        )
     }
 
     /// Write the value. Panics if the value is no longer valid.
-    pub fn write(&self) -> RefMut<'_, T> {
+    #[track_caller]
+    pub fn write(&self) -> GenerationalRefMut<'_, T> {
         self.try_write().unwrap()
     }
 
     /// Set the value. Panics if the value is no longer valid.
     pub fn set(&self, value: T) {
         self.validate().then(|| {
-            *self.raw.data.borrow_mut() = Some(Box::new(value));
+            *self.raw.0.data.borrow_mut() = Some(Box::new(value));
         });
     }
 
@@ -228,7 +242,8 @@ impl<T: 'static> GenerationalBox<T> {
     pub fn ptr_eq(&self, other: &Self) -> bool {
         #[cfg(any(debug_assertions, feature = "check_generation"))]
         {
-            self.raw.data.as_ptr() == other.raw.data.as_ptr() && self.generation == other.generation
+            self.raw.0.data.as_ptr() == other.raw.0.data.as_ptr()
+                && self.generation == other.generation
         }
         #[cfg(not(any(debug_assertions, feature = "check_generation")))]
         {
@@ -246,26 +261,38 @@ impl<T> Clone for GenerationalBox<T> {
 }
 
 #[derive(Clone, Copy)]
-struct MemoryLocation {
-    data: &'static RefCell<Option<Box<dyn std::any::Any>>>,
+struct MemoryLocation(&'static MemoryLocationInner);
+
+struct MemoryLocationInner {
+    data: RefCell<Option<Box<dyn std::any::Any>>>,
     #[cfg(any(debug_assertions, feature = "check_generation"))]
-    generation: &'static Cell<u32>,
+    generation: Cell<u32>,
+    #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+    borrowed_at: RefCell<Vec<&'static std::panic::Location<'static>>>,
+    #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+    borrowed_mut_at: Cell<Option<&'static std::panic::Location<'static>>>,
 }
 
 impl MemoryLocation {
     #[allow(unused)]
     fn drop(&self) {
-        let old = self.data.borrow_mut().take();
+        let old = self.0.data.borrow_mut().take();
         #[cfg(any(debug_assertions, feature = "check_generation"))]
         if old.is_some() {
             drop(old);
-            let new_generation = self.generation.get() + 1;
-            self.generation.set(new_generation);
+            let new_generation = self.0.generation.get() + 1;
+            self.0.generation.set(new_generation);
         }
     }
 
-    fn replace<T: 'static>(&mut self, value: T) -> GenerationalBox<T> {
-        let mut inner_mut = self.data.borrow_mut();
+    fn replace_with_caller<T: 'static>(
+        &mut self,
+        value: T,
+        #[cfg(any(debug_assertions, feature = "check_generation"))] caller: &'static Location<
+            'static,
+        >,
+    ) -> GenerationalBox<T> {
+        let mut inner_mut = self.0.data.borrow_mut();
 
         let raw = Box::new(value);
         let old = inner_mut.replace(raw);
@@ -273,10 +300,318 @@ impl MemoryLocation {
         GenerationalBox {
             raw: *self,
             #[cfg(any(debug_assertions, feature = "check_generation"))]
-            generation: self.generation.get(),
+            generation: self.0.generation.get(),
+            #[cfg(any(debug_assertions, feature = "check_generation"))]
+            created_at: caller,
             _marker: PhantomData,
         }
     }
+
+    #[track_caller]
+    fn try_borrow<T: Any>(
+        &self,
+        #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+        created_at: &'static std::panic::Location<'static>,
+    ) -> Result<GenerationalRef<'_, T>, BorrowError> {
+        #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+        self.0
+            .borrowed_at
+            .borrow_mut()
+            .push(std::panic::Location::caller());
+        match self.0.data.try_borrow() {
+            Ok(borrow) => match Ref::filter_map(borrow, |any| any.as_ref()?.downcast_ref::<T>()) {
+                Ok(reference) => Ok(GenerationalRef {
+                    inner: reference,
+                    #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+                    borrow: GenerationalRefBorrowInfo {
+                        borrowed_at: std::panic::Location::caller(),
+                        borrowed_from: self.0,
+                    },
+                }),
+                Err(_) => Err(BorrowError::Dropped(ValueDroppedError {
+                    #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+                    created_at,
+                })),
+            },
+            Err(_) => Err(BorrowError::AlreadyBorrowedMut(AlreadyBorrowedMutError {
+                #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+                borrowed_mut_at: self.0.borrowed_mut_at.get().unwrap(),
+            })),
+        }
+    }
+
+    #[track_caller]
+    fn try_borrow_mut<T: Any>(
+        &self,
+        #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+        created_at: &'static std::panic::Location<'static>,
+    ) -> Result<GenerationalRefMut<'_, T>, BorrowMutError> {
+        #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+        {
+            self.0
+                .borrowed_mut_at
+                .set(Some(std::panic::Location::caller()));
+        }
+        match self.0.data.try_borrow_mut() {
+            Ok(borrow_mut) => {
+                match RefMut::filter_map(borrow_mut, |any| any.as_mut()?.downcast_mut::<T>()) {
+                    Ok(reference) => Ok(GenerationalRefMut {
+                        inner: reference,
+                        #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+                        borrow: GenerationalRefMutBorrowInfo {
+                            borrowed_from: self.0,
+                        },
+                    }),
+                    Err(_) => Err(BorrowMutError::Dropped(ValueDroppedError {
+                        #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+                        created_at,
+                    })),
+                }
+            }
+            Err(_) => Err(BorrowMutError::AlreadyBorrowed(AlreadyBorrowedError {
+                #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+                borrowed_at: self.0.borrowed_at.borrow().clone(),
+            })),
+        }
+    }
+}
+
+#[derive(Debug, Clone)]
+/// An error that can occur when trying to borrow a value.
+pub enum BorrowError {
+    /// The value was dropped.
+    Dropped(ValueDroppedError),
+    /// The value was already borrowed mutably.
+    AlreadyBorrowedMut(AlreadyBorrowedMutError),
+}
+
+impl Display for BorrowError {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            BorrowError::Dropped(error) => Display::fmt(error, f),
+            BorrowError::AlreadyBorrowedMut(error) => Display::fmt(error, f),
+        }
+    }
+}
+
+impl Error for BorrowError {}
+
+#[derive(Debug, Clone)]
+/// An error that can occur when trying to borrow a value mutably.
+pub enum BorrowMutError {
+    /// The value was dropped.
+    Dropped(ValueDroppedError),
+    /// The value was already borrowed.
+    AlreadyBorrowed(AlreadyBorrowedError),
+    /// The value was already borrowed mutably.
+    AlreadyBorrowedMut(AlreadyBorrowedMutError),
+}
+
+impl Display for BorrowMutError {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            BorrowMutError::Dropped(error) => Display::fmt(error, f),
+            BorrowMutError::AlreadyBorrowedMut(error) => Display::fmt(error, f),
+            BorrowMutError::AlreadyBorrowed(error) => Display::fmt(error, f),
+        }
+    }
+}
+
+impl Error for BorrowMutError {}
+
+/// An error that can occur when trying to use a value that has been dropped.
+#[derive(Debug, Copy, Clone)]
+pub struct ValueDroppedError {
+    #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+    created_at: &'static std::panic::Location<'static>,
+}
+
+impl Display for ValueDroppedError {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.write_str("Failed to borrow because the value was dropped.")?;
+        #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+        f.write_fmt(format_args!("created_at: {}", self.created_at))?;
+        Ok(())
+    }
+}
+
+impl std::error::Error for ValueDroppedError {}
+
+/// An error that can occur when trying to borrow a value that has already been borrowed mutably.
+#[derive(Debug, Copy, Clone)]
+pub struct AlreadyBorrowedMutError {
+    #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+    borrowed_mut_at: &'static std::panic::Location<'static>,
+}
+
+impl Display for AlreadyBorrowedMutError {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.write_str("Failed to borrow because the value was already borrowed mutably.")?;
+        #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+        f.write_fmt(format_args!("borrowed_mut_at: {}", self.borrowed_mut_at))?;
+        Ok(())
+    }
+}
+
+impl std::error::Error for AlreadyBorrowedMutError {}
+
+/// An error that can occur when trying to borrow a value mutably that has already been borrowed immutably.
+#[derive(Debug, Clone)]
+pub struct AlreadyBorrowedError {
+    #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+    borrowed_at: Vec<&'static std::panic::Location<'static>>,
+}
+
+impl Display for AlreadyBorrowedError {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.write_str("Failed to borrow mutably because the value was already borrowed immutably.")?;
+        #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+        f.write_str("borrowed_at:")?;
+        #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+        for location in self.borrowed_at.iter() {
+            f.write_fmt(format_args!("\t{}", location))?;
+        }
+        Ok(())
+    }
+}
+
+impl std::error::Error for AlreadyBorrowedError {}
+
+/// A reference to a value in a generational box.
+pub struct GenerationalRef<'a, T: 'static> {
+    inner: Ref<'a, T>,
+    #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+    borrow: GenerationalRefBorrowInfo,
+}
+
+impl<'a, T: 'static> GenerationalRef<'a, T> {
+    /// Map one ref type to another.
+    pub fn map<U, F>(orig: GenerationalRef<'a, T>, f: F) -> GenerationalRef<'a, U>
+    where
+        F: FnOnce(&T) -> &U,
+    {
+        GenerationalRef {
+            inner: Ref::map(orig.inner, f),
+            #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+            borrow: GenerationalRefBorrowInfo {
+                borrowed_at: orig.borrow.borrowed_at,
+                borrowed_from: orig.borrow.borrowed_from,
+            },
+        }
+    }
+
+    /// Filter one ref type to another.
+    pub fn filter_map<U, F>(orig: GenerationalRef<'a, T>, f: F) -> Option<GenerationalRef<'a, U>>
+    where
+        F: FnOnce(&T) -> Option<&U>,
+    {
+        let Self {
+            inner,
+            #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+            borrow,
+        } = orig;
+        Ref::filter_map(inner, f).ok().map(|inner| GenerationalRef {
+            inner,
+            #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+            borrow: GenerationalRefBorrowInfo {
+                borrowed_at: borrow.borrowed_at,
+                borrowed_from: borrow.borrowed_from,
+            },
+        })
+    }
+}
+
+impl<'a, T: 'static> Deref for GenerationalRef<'a, T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        self.inner.deref()
+    }
+}
+
+#[cfg(any(debug_assertions, feature = "debug_borrows"))]
+struct GenerationalRefBorrowInfo {
+    borrowed_at: &'static std::panic::Location<'static>,
+    borrowed_from: &'static MemoryLocationInner,
+}
+
+#[cfg(any(debug_assertions, feature = "debug_borrows"))]
+impl Drop for GenerationalRefBorrowInfo {
+    fn drop(&mut self) {
+        self.borrowed_from
+            .borrowed_at
+            .borrow_mut()
+            .retain(|location| std::ptr::eq(*location, self.borrowed_at as *const _));
+    }
+}
+
+/// A mutable reference to a value in a generational box.
+pub struct GenerationalRefMut<'a, T: 'static> {
+    inner: RefMut<'a, T>,
+    #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+    borrow: GenerationalRefMutBorrowInfo,
+}
+
+impl<'a, T: 'static> GenerationalRefMut<'a, T> {
+    /// Map one ref type to another.
+    pub fn map<U, F>(orig: GenerationalRefMut<'a, T>, f: F) -> GenerationalRefMut<'a, U>
+    where
+        F: FnOnce(&mut T) -> &mut U,
+    {
+        GenerationalRefMut {
+            inner: RefMut::map(orig.inner, f),
+            #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+            borrow: orig.borrow,
+        }
+    }
+
+    /// Filter one ref type to another.
+    pub fn filter_map<U, F>(
+        orig: GenerationalRefMut<'a, T>,
+        f: F,
+    ) -> Option<GenerationalRefMut<'a, U>>
+    where
+        F: FnOnce(&mut T) -> Option<&mut U>,
+    {
+        let Self {
+            inner,
+            #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+            borrow,
+        } = orig;
+        RefMut::filter_map(inner, f)
+            .ok()
+            .map(|inner| GenerationalRefMut {
+                inner,
+                #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+                borrow,
+            })
+    }
+}
+
+impl<'a, T: 'static> Deref for GenerationalRefMut<'a, T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        self.inner.deref()
+    }
+}
+
+impl<'a, T: 'static> DerefMut for GenerationalRefMut<'a, T> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        self.inner.deref_mut()
+    }
+}
+
+#[cfg(any(debug_assertions, feature = "debug_borrows"))]
+struct GenerationalRefMutBorrowInfo {
+    borrowed_from: &'static MemoryLocationInner,
+}
+
+#[cfg(any(debug_assertions, feature = "debug_borrows"))]
+impl Drop for GenerationalRefMutBorrowInfo {
+    fn drop(&mut self) {
+        self.borrowed_from.borrowed_mut_at.take();
+    }
 }
 
 /// Handles recycling generational boxes that have been dropped. Your application should have one store or one store per thread.
@@ -305,12 +640,16 @@ impl Store {
         if let Some(location) = self.recycled.borrow_mut().pop() {
             location
         } else {
-            let data: &'static RefCell<_> = self.bump.alloc(RefCell::new(None));
-            MemoryLocation {
-                data,
+            let data: &'static MemoryLocationInner = self.bump.alloc(MemoryLocationInner {
+                data: RefCell::new(None),
                 #[cfg(any(debug_assertions, feature = "check_generation"))]
-                generation: self.bump.alloc(Cell::new(0)),
-            }
+                generation: Cell::new(0),
+                #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+                borrowed_at: Default::default(),
+                #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+                borrowed_mut_at: Default::default(),
+            });
+            MemoryLocation(data)
         }
     }
 
@@ -331,9 +670,31 @@ pub struct Owner {
 
 impl Owner {
     /// Insert a value into the store. The value will be dropped when the owner is dropped.
+    #[track_caller]
     pub fn insert<T: 'static>(&self, value: T) -> GenerationalBox<T> {
         let mut location = self.store.claim();
-        let key = location.replace(value);
+        let key = location.replace_with_caller(
+            value,
+            #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+            std::panic::Location::caller(),
+        );
+        self.owned.borrow_mut().push(location);
+        key
+    }
+
+    /// 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,
+        #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+        caller: &'static std::panic::Location<'static>,
+    ) -> GenerationalBox<T> {
+        let mut location = self.store.claim();
+        let key = location.replace_with_caller(
+            value,
+            #[cfg(any(debug_assertions, feature = "debug_borrows"))]
+            caller,
+        );
         self.owned.borrow_mut().push(location);
         key
     }
@@ -344,7 +705,9 @@ impl Owner {
         GenerationalBox {
             raw: location,
             #[cfg(any(debug_assertions, feature = "check_generation"))]
-            generation: location.generation.get(),
+            generation: location.0.generation.get(),
+            #[cfg(any(debug_assertions, feature = "check_generation"))]
+            created_at: std::panic::Location::caller(),
             _marker: PhantomData,
         }
     }

+ 1 - 1
packages/signals/src/effect.rs

@@ -83,7 +83,7 @@ impl Effect {
 
     /// Run the effect callback immediately. Returns `true` if the effect was run. Returns `false` is the effect is dead.
     pub fn try_run(&self) {
-        if let Some(mut callback) = self.callback.try_write() {
+        if let Ok(mut callback) = self.callback.try_write() {
             {
                 get_effect_stack().effects.borrow_mut().push(*self);
             }

+ 18 - 15
packages/signals/src/impls.rs

@@ -1,7 +1,7 @@
 use crate::rt::CopyValue;
 use crate::signal::{ReadOnlySignal, Signal, Write};
-
-use std::cell::{Ref, RefMut};
+use generational_box::GenerationalRef;
+use generational_box::GenerationalRefMut;
 
 use std::{
     fmt::{Debug, Display},
@@ -38,8 +38,8 @@ macro_rules! read_impls {
 
         impl<T: 'static> $ty<Vec<T>> {
             /// Read a value from the inner vector.
-            pub fn get(&self, index: usize) -> Option<Ref<'_, T>> {
-                Ref::filter_map(self.read(), |v| v.get(index)).ok()
+            pub fn get(&self, index: usize) -> Option<GenerationalRef<'_, T>> {
+                GenerationalRef::filter_map(self.read(), |v| v.get(index))
             }
         }
 
@@ -52,9 +52,9 @@ macro_rules! read_impls {
                 self.with(|v| v.clone()).unwrap()
             }
 
-            /// Attemps to read the inner value of the Option.
-            pub fn as_ref(&self) -> Option<Ref<'_, T>> {
-                Ref::filter_map(self.read(), |v| v.as_ref()).ok()
+            /// Attempts to read the inner value of the Option.
+            pub fn as_ref(&self) -> Option<GenerationalRef<'_, T>> {
+                GenerationalRef::filter_map(self.read(), |v| v.as_ref())
             }
         }
     };
@@ -182,19 +182,22 @@ macro_rules! write_impls {
             }
 
             /// Gets the value out of the Option, or inserts the given value if the Option is empty.
-            pub fn get_or_insert(&self, default: T) -> Ref<'_, T> {
+            pub fn get_or_insert(&self, default: T) -> GenerationalRef<'_, T> {
                 self.get_or_insert_with(|| default)
             }
 
             /// Gets the value out of the Option, or inserts the value returned by the given function if the Option is empty.
-            pub fn get_or_insert_with(&self, default: impl FnOnce() -> T) -> Ref<'_, T> {
+            pub fn get_or_insert_with(
+                &self,
+                default: impl FnOnce() -> T,
+            ) -> GenerationalRef<'_, T> {
                 let borrow = self.read();
                 if borrow.is_none() {
                     drop(borrow);
                     self.with_mut(|v| *v = Some(default()));
-                    Ref::map(self.read(), |v| v.as_ref().unwrap())
+                    GenerationalRef::map(self.read(), |v| v.as_ref().unwrap())
                 } else {
-                    Ref::map(borrow, |v| v.as_ref().unwrap())
+                    GenerationalRef::map(borrow, |v| v.as_ref().unwrap())
                 }
             }
         }
@@ -238,15 +241,15 @@ impl<T: Clone + 'static> IntoIterator for CopyValue<Vec<T>> {
 
 impl<T: 'static> CopyValue<Vec<T>> {
     /// Write to an element in the inner vector.
-    pub fn get_mut(&self, index: usize) -> Option<RefMut<'_, T>> {
-        RefMut::filter_map(self.write(), |v| v.get_mut(index)).ok()
+    pub fn get_mut(&self, index: usize) -> Option<GenerationalRefMut<'_, T>> {
+        GenerationalRefMut::filter_map(self.write(), |v| v.get_mut(index))
     }
 }
 
 impl<T: 'static> CopyValue<Option<T>> {
     /// Deref the inner value mutably.
-    pub fn as_mut(&self) -> Option<RefMut<'_, T>> {
-        RefMut::filter_map(self.write(), |v| v.as_mut()).ok()
+    pub fn as_mut(&self) -> Option<GenerationalRefMut<'_, T>> {
+        GenerationalRefMut::filter_map(self.write(), |v| v.as_mut())
     }
 }
 

+ 24 - 9
packages/signals/src/rt.rs

@@ -1,14 +1,15 @@
-use std::cell::{Ref, RefMut};
-
+use std::panic::Location;
 use std::rc::Rc;
 
 use dioxus_core::prelude::{
-    consume_context, consume_context_from_scope, current_scope_id, provide_context,
+    consume_context, consume_context_from_scope, current_scope_id, has_context, provide_context,
     provide_context_to_scope, provide_root_context,
 };
 use dioxus_core::ScopeId;
 
-use generational_box::{GenerationalBox, Owner, Store};
+use generational_box::{
+    BorrowError, BorrowMutError, GenerationalBox, GenerationalRef, GenerationalRefMut, Owner, Store,
+};
 
 fn current_store() -> Store {
     match consume_context() {
@@ -21,7 +22,7 @@ fn current_store() -> Store {
 }
 
 fn current_owner() -> Rc<Owner> {
-    match consume_context() {
+    match has_context() {
         Some(rt) => rt,
         None => {
             let owner = Rc::new(current_store().owner());
@@ -74,6 +75,7 @@ impl<T: 'static> CopyValue<T> {
     /// Create a new CopyValue. The value will be stored in the current component.
     ///
     /// Once the component this value is created in is dropped, the value will be dropped.
+    #[track_caller]
     pub fn new(value: T) -> Self {
         let owner = current_owner();
 
@@ -83,6 +85,15 @@ impl<T: 'static> CopyValue<T> {
         }
     }
 
+    pub(crate) fn new_with_caller(value: T, caller: &'static Location<'static>) -> Self {
+        let owner = current_owner();
+
+        Self {
+            value: owner.insert_with_caller(value, caller),
+            origin_scope: current_scope_id().expect("in a virtual dom"),
+        }
+    }
+
     /// Create a new CopyValue. The value will be stored in the given scope. When the specified scope is dropped, the value will be dropped.
     pub fn new_in_scope(value: T, scope: ScopeId) -> Self {
         let owner = owner_in_scope(scope);
@@ -108,22 +119,26 @@ impl<T: 'static> CopyValue<T> {
     }
 
     /// Try to read the value. If the value has been dropped, this will return None.
-    pub fn try_read(&self) -> Option<Ref<'_, T>> {
+    #[track_caller]
+    pub fn try_read(&self) -> Result<GenerationalRef<'_, T>, BorrowError> {
         self.value.try_read()
     }
 
     /// Read the value. If the value has been dropped, this will panic.
-    pub fn read(&self) -> Ref<'_, T> {
+    #[track_caller]
+    pub fn read(&self) -> GenerationalRef<'_, T> {
         self.value.read()
     }
 
     /// Try to write the value. If the value has been dropped, this will return None.
-    pub fn try_write(&self) -> Option<RefMut<'_, T>> {
+    #[track_caller]
+    pub fn try_write(&self) -> Result<GenerationalRefMut<'_, T>, BorrowMutError> {
         self.value.try_write()
     }
 
     /// Write the value. If the value has been dropped, this will panic.
-    pub fn write(&self) -> RefMut<'_, T> {
+    #[track_caller]
+    pub fn write(&self) -> GenerationalRefMut<'_, T> {
         self.value.write()
     }
 

+ 49 - 9
packages/signals/src/signal.rs

@@ -1,6 +1,7 @@
 use std::{
-    cell::{Ref, RefCell, RefMut},
+    cell::RefCell,
     ops::{Deref, DerefMut},
+    panic::Location,
     rc::Rc,
     sync::Arc,
 };
@@ -9,6 +10,7 @@ use dioxus_core::{
     prelude::{current_scope_id, has_context, provide_context, schedule_update_any},
     ScopeId, ScopeState,
 };
+use generational_box::{GenerationalRef, GenerationalRefMut};
 
 use crate::{CopyValue, Effect};
 
@@ -43,8 +45,18 @@ use crate::{CopyValue, Effect};
 ///     }
 /// }
 /// ```
+#[track_caller]
 pub fn use_signal<T: 'static>(cx: &ScopeState, f: impl FnOnce() -> T) -> Signal<T> {
-    *cx.use_hook(|| Signal::new(f()))
+    #[cfg(debug_assertions)]
+    let caller = Location::caller();
+
+    *cx.use_hook(|| {
+        Signal::new_with_caller(
+            f(),
+            #[cfg(debug_assertions)]
+            caller,
+        )
+    })
 }
 
 #[derive(Clone)]
@@ -134,6 +146,7 @@ impl<'de, T: serde::Deserialize<'de> + 'static> serde::Deserialize<'de> for Sign
 
 impl<T: 'static> Signal<T> {
     /// Creates a new Signal. Signals are a Copy state management solution with automatic dependency tracking.
+    #[track_caller]
     pub fn new(value: T) -> Self {
         Self {
             inner: CopyValue::new(SignalData {
@@ -145,6 +158,25 @@ impl<T: 'static> Signal<T> {
         }
     }
 
+    /// Creates a new Signal. Signals are a Copy state management solution with automatic dependency tracking.
+    fn new_with_caller(
+        value: T,
+        #[cfg(debug_assertions)] caller: &'static Location<'static>,
+    ) -> Self {
+        Self {
+            inner: CopyValue::new_with_caller(
+                SignalData {
+                    subscribers: Default::default(),
+                    effect_subscribers: Default::default(),
+                    update_any: schedule_update_any().expect("in a virtual dom"),
+                    value,
+                },
+                #[cfg(debug_assertions)]
+                caller,
+            ),
+        }
+    }
+
     /// Get the scope the signal was created in.
     pub fn origin_scope(&self) -> ScopeId {
         self.inner.origin_scope()
@@ -152,7 +184,8 @@ impl<T: 'static> Signal<T> {
 
     /// Get the current value of the signal. This will subscribe the current scope to the signal.
     /// If the signal has been dropped, this will panic.
-    pub fn read(&self) -> Ref<T> {
+    #[track_caller]
+    pub fn read(&self) -> GenerationalRef<T> {
         let inner = self.inner.read();
         if let Some(effect) = Effect::current() {
             let mut effect_subscribers = inner.effect_subscribers.borrow_mut();
@@ -176,14 +209,15 @@ impl<T: 'static> Signal<T> {
                 }
             }
         }
-        Ref::map(inner, |v| &v.value)
+        GenerationalRef::map(inner, |v| &v.value)
     }
 
     /// Get a mutable reference to the signal's value.
     /// If the signal has been dropped, this will panic.
+    #[track_caller]
     pub fn write(&self) -> Write<'_, T> {
         let inner = self.inner.write();
-        let borrow = RefMut::map(inner, |v| &mut v.value);
+        let borrow = GenerationalRefMut::map(inner, |v| &mut v.value);
         Write {
             write: borrow,
             signal: SignalSubscriberDrop { signal: *self },
@@ -219,12 +253,14 @@ impl<T: 'static> Signal<T> {
     }
 
     /// Set the value of the signal. This will trigger an update on all subscribers.
+    #[track_caller]
     pub fn set(&self, value: T) {
         *self.write() = value;
     }
 
     /// Run a closure with a reference to the signal's value.
     /// If the signal has been dropped, this will panic.
+    #[track_caller]
     pub fn with<O>(&self, f: impl FnOnce(&T) -> O) -> O {
         let write = self.read();
         f(&*write)
@@ -232,6 +268,7 @@ impl<T: 'static> Signal<T> {
 
     /// Run a closure with a mutable reference to the signal's value.
     /// If the signal has been dropped, this will panic.
+    #[track_caller]
     pub fn with_mut<O>(&self, f: impl FnOnce(&mut T) -> O) -> O {
         let mut write = self.write();
         f(&mut *write)
@@ -241,6 +278,7 @@ impl<T: 'static> Signal<T> {
 impl<T: Clone + 'static> Signal<T> {
     /// Get the current value of the signal. This will subscribe the current scope to the signal.
     /// If the signal has been dropped, this will panic.
+    #[track_caller]
     pub fn value(&self) -> T {
         self.read().clone()
     }
@@ -264,7 +302,7 @@ impl<T: 'static> Drop for SignalSubscriberDrop<T> {
 
 /// A mutable reference to a signal's value.
 pub struct Write<'a, T: 'static, I: 'static = T> {
-    write: RefMut<'a, T>,
+    write: GenerationalRefMut<'a, T>,
     signal: SignalSubscriberDrop<I>,
 }
 
@@ -273,7 +311,7 @@ impl<'a, T: 'static, I: 'static> Write<'a, T, I> {
     pub fn map<O>(myself: Self, f: impl FnOnce(&mut T) -> &mut O) -> Write<'a, O, I> {
         let Self { write, signal } = myself;
         Write {
-            write: RefMut::map(write, f),
+            write: GenerationalRefMut::map(write, f),
             signal,
         }
     }
@@ -284,7 +322,7 @@ impl<'a, T: 'static, I: 'static> Write<'a, T, I> {
         f: impl FnOnce(&mut T) -> Option<&mut O>,
     ) -> Option<Write<'a, O, I>> {
         let Self { write, signal } = myself;
-        let write = RefMut::filter_map(write, f).ok();
+        let write = GenerationalRefMut::filter_map(write, f);
         write.map(|write| Write { write, signal })
     }
 }
@@ -320,11 +358,13 @@ impl<T: 'static> ReadOnlySignal<T> {
     }
 
     /// Get the current value of the signal. This will subscribe the current scope to the signal.
-    pub fn read(&self) -> Ref<T> {
+    #[track_caller]
+    pub fn read(&self) -> GenerationalRef<T> {
         self.inner.read()
     }
 
     /// Run a closure with a reference to the signal's value.
+    #[track_caller]
     pub fn with<O>(&self, f: impl FnOnce(&T) -> O) -> O {
         self.inner.with(f)
     }