Skip to content

Commit

Permalink
Add a function to free dead roots without doing a full cycle collection
Browse files Browse the repository at this point in the history
  • Loading branch information
jrmuizel committed Oct 9, 2023
1 parent 930c1fe commit e8f6ff5
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 0 deletions.
20 changes: 20 additions & 0 deletions src/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,26 @@ pub fn number_of_roots_buffered() -> usize {
ROOTS.with(|r| r.borrow().len())
}


/// Free's all the of the roots that have a reference count of 0.
/// This is much faster than doing a full cycle collection and will
/// ensure that any memory that was never part of a cycle is freed.
pub fn free_dead_roots() {
ROOTS.with(|r| {
let mut v = r.borrow_mut();
v.retain_mut(|root| {
let s: &dyn CcBoxPtr = unsafe { root.as_ref() };
if s.data().strong() == 0 {
s.data().buffered.set(false);
unsafe { free(*root) };
false
} else {
true
}
});
});
}

/// Invoke cycle collection for all `Cc<T>`s on this thread.
///
/// You may wish to do this when the roots buffer reaches a certain size, when
Expand Down
15 changes: 15 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -984,6 +984,8 @@ pub(crate) unsafe fn drop_value(mut ptr: NonNull<dyn CcBoxPtr>) {
mod tests {
use core::cell::RefCell;

use crate::{number_of_roots_buffered, collect::free_dead_roots};

use super::{collect_cycles, Cc, Trace, Tracer, Weak};

// Tests copied from `Rc<T>`.
Expand Down Expand Up @@ -1506,4 +1508,17 @@ mod tests {
drop(b);
collect_cycles();
}


#[test]
fn freeing_dead_roots() {
assert_eq!(number_of_roots_buffered(), 0);
let a = Cc::new(1);
let b = a.clone();
drop(b);
assert_eq!(number_of_roots_buffered(), 1);
drop(a);
free_dead_roots();
assert_eq!(number_of_roots_buffered(), 0);
}
}

0 comments on commit e8f6ff5

Please sign in to comment.