Skip to content

Commit

Permalink
feat: add signal.into_inner() (#3343)
Browse files Browse the repository at this point in the history
  • Loading branch information
stefnotch authored Dec 17, 2024
1 parent 2324853 commit 1661fe2
Show file tree
Hide file tree
Showing 13 changed files with 209 additions and 13 deletions.
11 changes: 10 additions & 1 deletion reactive_graph/src/owner/arc_stored_value.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
signal::guards::{Plain, ReadGuard, UntrackedWriteGuard},
traits::{DefinedAt, IsDisposed, ReadValue, WriteValue},
traits::{DefinedAt, IntoInner, IsDisposed, ReadValue, WriteValue},
};
use std::{
fmt::{Debug, Formatter},
Expand Down Expand Up @@ -124,3 +124,12 @@ impl<T> IsDisposed for ArcStoredValue<T> {
false
}
}

impl<T> IntoInner for ArcStoredValue<T> {
type Value = T;

#[inline(always)]
fn into_inner(self) -> Option<Self::Value> {
Some(Arc::into_inner(self.value)?.into_inner().unwrap())
}
}
11 changes: 10 additions & 1 deletion reactive_graph/src/owner/arena_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use super::{
arena::{Arena, NodeId},
LocalStorage, Storage, SyncStorage, OWNER,
};
use crate::traits::{Dispose, IsDisposed};
use crate::traits::{Dispose, IntoInner, IsDisposed};
use send_wrapper::SendWrapper;
use std::{any::Any, hash::Hash, marker::PhantomData};

Expand Down Expand Up @@ -134,3 +134,12 @@ impl<T, S> Dispose for ArenaItem<T, S> {
Arena::with_mut(|arena| arena.remove(self.node));
}
}

impl<T, S: Storage<T>> IntoInner for ArenaItem<T, S> {
type Value = T;

#[inline(always)]
fn into_inner(self) -> Option<Self::Value> {
S::take(self.node)
}
}
24 changes: 24 additions & 0 deletions reactive_graph/src/owner/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ pub trait Storage<T>: Send + Sync + 'static {

/// Sets a new value for the stored value. If it has been disposed, returns `Some(T)`.
fn try_set(node: NodeId, value: T) -> Option<T>;

/// Takes an item from the arena if it exists and can be accessed from this thread.
/// If it cannot be casted, it will still be removed from the arena.
fn take(node: NodeId) -> Option<T>;
}

/// A form of [`Storage`] that stores the type as itself, with no wrapper.
Expand Down Expand Up @@ -100,6 +104,16 @@ where
}
})
}

fn take(node: NodeId) -> Option<T> {
Arena::with_mut(|arena| {
let m = arena.remove(node)?;
match m.downcast::<T>() {
Ok(inner) => Some(*inner),
Err(_) => None,
}
})
}
}

/// A form of [`Storage`] that stores the type with a wrapper that makes it `Send + Sync`, but only
Expand Down Expand Up @@ -148,4 +162,14 @@ where
}
})
}

fn take(node: NodeId) -> Option<T> {
Arena::with_mut(|arena| {
let m = arena.remove(node)?;
match m.downcast::<SendWrapper<T>>() {
Ok(inner) => Some(inner.take()),
Err(_) => None,
}
})
}
}
17 changes: 16 additions & 1 deletion reactive_graph/src/owner/stored_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ use super::{
};
use crate::{
signal::guards::{Plain, ReadGuard, UntrackedWriteGuard},
traits::{DefinedAt, Dispose, IsDisposed, ReadValue, WriteValue},
traits::{
DefinedAt, Dispose, IntoInner, IsDisposed, ReadValue, WriteValue,
},
unwrap_signal,
};
use std::{
Expand Down Expand Up @@ -162,6 +164,19 @@ impl<T, S> Dispose for StoredValue<T, S> {
}
}

impl<T, S> IntoInner for StoredValue<T, S>
where
T: 'static,
S: Storage<ArcStoredValue<T>>,
{
type Value = T;

#[inline(always)]
fn into_inner(self) -> Option<Self::Value> {
self.value.into_inner()?.into_inner()
}
}

impl<T> From<ArcStoredValue<T>> for StoredValue<T>
where
T: Send + Sync + 'static,
Expand Down
3 changes: 2 additions & 1 deletion reactive_graph/src/signal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ pub fn arc_signal<T>(value: T) -> (ArcReadSignal<T>, ArcWriteSignal<T>) {
pub fn signal<T: Send + Sync + 'static>(
value: T,
) -> (ReadSignal<T>, WriteSignal<T>) {
RwSignal::new(value).split()
let (r, w) = arc_signal(value);
(r.into(), w.into())
}

/// Creates an arena-allocated signal.
Expand Down
11 changes: 10 additions & 1 deletion reactive_graph/src/signal/arc_read.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use super::{
};
use crate::{
graph::SubscriberSet,
traits::{DefinedAt, IsDisposed, ReadUntracked},
traits::{DefinedAt, IntoInner, IsDisposed, ReadUntracked},
};
use core::fmt::{Debug, Formatter, Result};
use std::{
Expand Down Expand Up @@ -128,6 +128,15 @@ impl<T> IsDisposed for ArcReadSignal<T> {
}
}

impl<T> IntoInner for ArcReadSignal<T> {
type Value = T;

#[inline(always)]
fn into_inner(self) -> Option<Self::Value> {
Some(Arc::into_inner(self.value)?.into_inner().unwrap())
}
}

impl<T> AsSubscriberSet for ArcReadSignal<T> {
type Output = Arc<RwLock<SubscriberSet>>;

Expand Down
11 changes: 10 additions & 1 deletion reactive_graph/src/signal/arc_rw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use super::{
use crate::{
graph::{ReactiveNode, SubscriberSet},
prelude::{IsDisposed, Notify},
traits::{DefinedAt, ReadUntracked, UntrackableGuard, Write},
traits::{DefinedAt, IntoInner, ReadUntracked, UntrackableGuard, Write},
};
use core::fmt::{Debug, Formatter, Result};
use std::{
Expand Down Expand Up @@ -230,6 +230,15 @@ impl<T> IsDisposed for ArcRwSignal<T> {
}
}

impl<T> IntoInner for ArcRwSignal<T> {
type Value = T;

#[inline(always)]
fn into_inner(self) -> Option<Self::Value> {
Some(Arc::into_inner(self.value)?.into_inner().unwrap())
}
}

impl<T> AsSubscriberSet for ArcRwSignal<T> {
type Output = Arc<RwLock<SubscriberSet>>;

Expand Down
11 changes: 10 additions & 1 deletion reactive_graph/src/signal/arc_write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use super::guards::{UntrackedWriteGuard, WriteGuard};
use crate::{
graph::{ReactiveNode, SubscriberSet},
prelude::{IsDisposed, Notify},
traits::{DefinedAt, UntrackableGuard, Write},
traits::{DefinedAt, IntoInner, UntrackableGuard, Write},
};
use core::fmt::{Debug, Formatter, Result};
use std::{
Expand Down Expand Up @@ -116,6 +116,15 @@ impl<T> IsDisposed for ArcWriteSignal<T> {
}
}

impl<T> IntoInner for ArcWriteSignal<T> {
type Value = T;

#[inline(always)]
fn into_inner(self) -> Option<Self::Value> {
Some(Arc::into_inner(self.value)?.into_inner().unwrap())
}
}

impl<T> Notify for ArcWriteSignal<T> {
fn notify(&self) {
self.inner.mark_dirty();
Expand Down
14 changes: 13 additions & 1 deletion reactive_graph/src/signal/read.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use super::{
use crate::{
graph::SubscriberSet,
owner::{ArenaItem, FromLocal, LocalStorage, Storage, SyncStorage},
traits::{DefinedAt, Dispose, IsDisposed, ReadUntracked},
traits::{DefinedAt, Dispose, IntoInner, IsDisposed, ReadUntracked},
unwrap_signal,
};
use core::fmt::Debug;
Expand Down Expand Up @@ -122,6 +122,18 @@ impl<T, S> IsDisposed for ReadSignal<T, S> {
}
}

impl<T, S> IntoInner for ReadSignal<T, S>
where
S: Storage<ArcReadSignal<T>>,
{
type Value = T;

#[inline(always)]
fn into_inner(self) -> Option<Self::Value> {
self.inner.into_inner()?.into_inner()
}
}

impl<T, S> AsSubscriberSet for ReadSignal<T, S>
where
S: Storage<ArcReadSignal<T>>,
Expand Down
14 changes: 13 additions & 1 deletion reactive_graph/src/signal/rw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::{
owner::{ArenaItem, FromLocal, LocalStorage, Storage, SyncStorage},
signal::guards::{UntrackedWriteGuard, WriteGuard},
traits::{
DefinedAt, Dispose, IsDisposed, Notify, ReadUntracked,
DefinedAt, Dispose, IntoInner, IsDisposed, Notify, ReadUntracked,
UntrackableGuard, Write,
},
unwrap_signal,
Expand Down Expand Up @@ -313,6 +313,18 @@ impl<T: 'static, S> IsDisposed for RwSignal<T, S> {
}
}

impl<T, S> IntoInner for RwSignal<T, S>
where
S: Storage<ArcRwSignal<T>>,
{
type Value = T;

#[inline(always)]
fn into_inner(self) -> Option<Self::Value> {
self.inner.into_inner()?.into_inner()
}
}

impl<T, S> AsSubscriberSet for RwSignal<T, S>
where
S: Storage<ArcRwSignal<T>>,
Expand Down
47 changes: 45 additions & 2 deletions reactive_graph/src/signal/write.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
use super::{guards::WriteGuard, ArcWriteSignal};
use crate::{
owner::{ArenaItem, Storage, SyncStorage},
traits::{DefinedAt, Dispose, IsDisposed, Notify, UntrackableGuard, Write},
owner::{ArenaItem, FromLocal, LocalStorage, Storage, SyncStorage},
traits::{
DefinedAt, Dispose, IntoInner, IsDisposed, Notify, UntrackableGuard,
Write,
},
};
use core::fmt::Debug;
use guardian::ArcRwLockWriteGuardian;
Expand Down Expand Up @@ -108,12 +111,52 @@ impl<T, S> DefinedAt for WriteSignal<T, S> {
}
}

impl<T> From<ArcWriteSignal<T>> for WriteSignal<T>
where
T: Send + Sync + 'static,
{
#[track_caller]
fn from(value: ArcWriteSignal<T>) -> Self {
WriteSignal {
#[cfg(debug_assertions)]
defined_at: Location::caller(),
inner: ArenaItem::new_with_storage(value),
}
}
}

impl<T> FromLocal<ArcWriteSignal<T>> for WriteSignal<T, LocalStorage>
where
T: 'static,
{
#[track_caller]
fn from_local(value: ArcWriteSignal<T>) -> Self {
WriteSignal {
#[cfg(debug_assertions)]
defined_at: Location::caller(),
inner: ArenaItem::new_with_storage(value),
}
}
}

impl<T, S> IsDisposed for WriteSignal<T, S> {
fn is_disposed(&self) -> bool {
self.inner.is_disposed()
}
}

impl<T, S> IntoInner for WriteSignal<T, S>
where
S: Storage<ArcWriteSignal<T>>,
{
type Value = T;

#[inline(always)]
fn into_inner(self) -> Option<Self::Value> {
self.inner.into_inner()?.into_inner()
}
}

impl<T, S> Notify for WriteSignal<T, S>
where
T: 'static,
Expand Down
12 changes: 12 additions & 0 deletions reactive_graph/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -630,6 +630,18 @@ pub trait IsDisposed {
fn is_disposed(&self) -> bool;
}

/// Turns a signal back into a raw value.
pub trait IntoInner {
/// The type of the value contained in the signal.
type Value;

/// Returns the inner value if this is the only reference to to the signal.
/// Otherwise, returns `None` and drops this reference.
/// # Panics
/// Panics if the inner lock is poisoned.
fn into_inner(self) -> Option<Self::Value>;
}

/// Describes where the signal was defined. This is used for diagnostic warnings and is purely a
/// debug-mode tool.
pub trait DefinedAt {
Expand Down
36 changes: 34 additions & 2 deletions reactive_graph/tests/signal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use reactive_graph::{
owner::Owner,
signal::{arc_signal, signal, ArcRwSignal, RwSignal},
traits::{
Get, GetUntracked, Read, Set, Update, UpdateUntracked, With,
WithUntracked, Write,
Dispose, Get, GetUntracked, IntoInner, Read, Set, Update,
UpdateUntracked, With, WithUntracked, Write,
},
};

Expand Down Expand Up @@ -108,3 +108,35 @@ fn update_signal() {
set_a.set(4);
assert_eq!(a.get(), 4);
}

#[test]
fn into_inner_signal() {
let owner = Owner::new();
owner.set();

let rw_signal = RwSignal::new(1);
assert_eq!(rw_signal.get(), 1);
assert_eq!(rw_signal.into_inner(), Some(1));
}

#[test]
fn into_inner_arc_signal() {
let owner = Owner::new();
owner.set();

let (a, b) = arc_signal(2);
assert_eq!(a.get(), 2);
std::mem::drop(b);
assert_eq!(a.into_inner(), Some(2));
}

#[test]
fn into_inner_non_arc_signal() {
let owner = Owner::new();
owner.set();

let (a, b) = signal(2);
assert_eq!(a.get(), 2);
b.dispose();
assert_eq!(a.into_inner(), Some(2));
}

0 comments on commit 1661fe2

Please sign in to comment.