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>
This commit is contained in:
Evan AlmloffandJonathan Kelley authored and GitHub committed 2024-10-14 09:23:09 -07:00
1 parent 20d146d9bd
commit baf5a95cae
19 files changed
+905 -250

No files matched your search

+16 -3
View File
@@ -71,6 +71,19 @@ impl<T: 'static, S: Storage<T>> CopyValue<T, S> {
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(
value: T,
caller: &'static std::panic::Location<'static>,
@@ -78,7 +91,7 @@ impl<T: 'static, S: Storage<T>> CopyValue<T, S> {
let owner = current_owner();
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"),
}
}
@@ -99,13 +112,13 @@ impl<T: 'static, S: Storage<T>> CopyValue<T, S> {
let owner = scope.owner();
Self {
value: owner.insert_with_caller(value, caller),
value: owner.insert_rc_with_caller(value, caller),
origin_scope: scope,
}
}
/// 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()
}
+8 -16
View File
@@ -42,25 +42,17 @@ impl<T: 'static, S: Storage<SignalData<T>>> ReadOnlySignal<T, S> {
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)]
/// 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
View File
@@ -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.
#[track_caller]
#[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.
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.