Skip to content

Commit

Permalink
AnyRoot
Browse files Browse the repository at this point in the history
  • Loading branch information
ecton committed Mar 27, 2024
1 parent b565feb commit bc969f5
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 9 deletions.
4 changes: 2 additions & 2 deletions refuse-pool/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,8 @@ impl RootString {
}

/// Returns a typeless reference to this string.
pub fn as_any(&self) -> AnyRef {
self.0.as_any()
pub const fn downgrade_any(&self) -> AnyRef {
self.0.downgrade_any()
}

/// Returns the number of root references to this string, `self` included.
Expand Down
103 changes: 97 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1550,14 +1550,33 @@ where

/// Returns an untyped "weak" reference erased to this root.
#[must_use]
pub fn downgrade_any(&self) -> AnyRef {
pub const fn downgrade_any(&self) -> AnyRef {
self.reference.as_any()
}

/// Returns this reference as an untyped reference.
/// Returns an untyped root reference.
#[must_use]
pub fn as_any(&self) -> AnyRef {
self.reference.as_any()
pub fn to_any_root(&self) -> AnyRoot {
let roots = &self.as_rooted().roots;
roots.fetch_add(1, Ordering::Acquire);

AnyRoot {
rooted: self.data.cast(),
roots,
any: self.reference.as_any(),
}
}

/// Returns this root as an untyped root.
pub fn into_any_root(self) -> AnyRoot {
// We transfer ownership of this reference to the AnyRoot, so we want to
// avoid calling drop on `self`.
let this = ManuallyDrop::new(self);
AnyRoot {
rooted: this.data.cast(),
roots: &this.as_rooted().roots,
any: this.reference.as_any(),
}
}

fn as_rooted(&self) -> &Rooted<T> {
Expand Down Expand Up @@ -1792,7 +1811,7 @@ where

/// Returns this reference as an untyped reference.
#[must_use]
pub fn as_any(self) -> AnyRef {
pub const fn as_any(self) -> AnyRef {
self.any
}

Expand Down Expand Up @@ -2665,6 +2684,78 @@ impl TypeIndex {
}
}

/// A type-erased root garbage collected reference.
#[derive(Eq, PartialEq, PartialOrd, Ord)]
pub struct AnyRoot {
rooted: *const (),
roots: *const AtomicU64,
any: AnyRef,
}

impl AnyRoot {
/// Loads a reference to the underlying data. Returns `None` if `T` is not
/// the type of the underlying data.
pub fn load<T>(&self) -> Option<&T>
where
T: Collectable,
{
if TypeIndex::of::<T>() == self.any.type_index {
let rooted = unsafe { &*self.rooted.cast::<Rooted<T>>() };
Some(&rooted.value)
} else {
None
}
}

/// Returns an untyped "weak" reference to this root.
pub const fn as_any(&self) -> AnyRef {
self.any
}
}

impl Clone for AnyRoot {
fn clone(&self) -> Self {
unsafe { &*self.roots }.fetch_add(1, Ordering::Acquire);
Self {
rooted: self.rooted,
roots: self.roots,
any: self.any,
}
}
}

impl Drop for AnyRoot {
fn drop(&mut self) {
if unsafe { &*self.roots }.fetch_sub(1, Ordering::Acquire) == 1 {
CollectorCommand::schedule_collect_if_needed();
}
}
}

impl Hash for AnyRoot {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.any.hash(state);
}
}

impl<T> From<Root<T>> for AnyRoot
where
T: Collectable,
{
fn from(value: Root<T>) -> Self {
value.into_any_root()
}
}

// SAFETY: AnyRoot's usage of a pointer prevents auto implementation.
// `Collectable` requires `Send`, and `Root<T>` ensures proper Send + Sync
// behavior in its memory accesses.
unsafe impl Send for AnyRoot {}
// SAFETY: AnyRoot's usage of a pointer prevents auto implementation.
// `Collectable` requires `Send`, and `Root<T>` ensures proper Send + Sync
// behavior in its memory accesses.
unsafe impl Sync for AnyRoot {}

/// A type-erased garbage collected reference.
#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord)]
pub struct AnyRef {
Expand Down Expand Up @@ -2787,7 +2878,7 @@ where
T: Collectable,
{
fn from(value: &'_ Root<T>) -> Self {
value.as_any()
value.downgrade_any()
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ use crate::{CollectionGuard, Root};
fn casting() {
let guard = CollectionGuard::acquire();
let value = Root::new(2_u32, &guard);
let any = value.as_any();
let any = value.downgrade_any();
assert_eq!(any.downcast_ref().load(&guard), Some(&2_u32));
assert_eq!(any.downcast_ref::<u16>().load(&guard), None);
}

0 comments on commit bc969f5

Please sign in to comment.