use std::sync::{atomic::AtomicUsize, Mutex};
use crate::{
arc::{GCArc, GCRef},
traceable::GCTraceable,
};
pub struct GC<T: GCTraceable + 'static> {
gc_refs: Mutex<Vec<GCArc<T>>>,
attach_count: AtomicUsize,
collection_percentage: usize, }
#[allow(dead_code)]
impl<T> GC<T>
where
T: GCTraceable + 'static,
{
pub fn new() -> Self {
Self {
gc_refs: Mutex::new(Vec::new()),
attach_count: AtomicUsize::new(0),
collection_percentage: 20, }
}
pub fn new_with_percentage(percentage: usize) -> Self {
Self {
gc_refs: Mutex::new(Vec::new()),
attach_count: AtomicUsize::new(0),
collection_percentage: percentage,
}
}
pub fn attach(&mut self, gc_arc: GCArc<T>) {
{
let mut gc_refs = self.gc_refs.lock().unwrap();
gc_refs.push(gc_arc);
}
self.attach_count
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
if self.should_collect() {
self.collect();
}
}
pub fn detach(&mut self, gc_arc: &GCArc<T>) -> bool {
let mut gc_refs = self.gc_refs.lock().unwrap();
if let Some(index) = gc_refs.iter().position(|r| GCArc::ptr_eq(r, gc_arc)) {
gc_refs.swap_remove(index);
true
} else {
false
}
}
pub fn collect(&mut self) {
let mut refs = self.gc_refs.lock().unwrap();
for r in refs.iter_mut() {
r.unmark();
}
let mut roots: Vec<&mut GCArc<T>> = refs
.iter_mut()
.filter(
|r| r.strong_ref() > 1, )
.collect();
for r in roots.iter_mut() {
r.mark_and_visit();
}
let retained: Vec<GCArc<T>> = refs
.iter_mut()
.filter(|r| r.is_marked())
.map(|r| r.clone())
.collect();
refs.clear();
refs.extend(retained);
self.attach_count
.store(0, std::sync::atomic::Ordering::Relaxed);
}
pub fn object_count(&self) -> usize {
return self.gc_refs.lock().unwrap().len();
}
pub fn get_all(&self) -> Vec<GCArc<T>> {
self.gc_refs.lock().unwrap().clone()
}
pub fn create(&mut self, obj: T) -> GCArc<T> {
let gc_arc = GCArc::new(obj);
self.attach(gc_arc.clone());
gc_arc
}
fn should_collect(&self) -> bool {
let current_count = self.gc_refs.lock().unwrap().len();
let attach_count = self.attach_count.load(std::sync::atomic::Ordering::Relaxed);
if current_count == 0 {
return false;
}
let threshold = (current_count * self.collection_percentage) / 100;
attach_count >= threshold.max(1) }
}
#[cfg(test)]
mod tests {
use std::cell::RefCell;
use super::*;
use crate::{arc::GCArcWeak, traceable::GCTraceable};
struct TestObject {
value: Option<GCArcWeak<TestObjectCell>>,
}
impl GCTraceable for TestObject {
fn visit(&self) {
if let Some(ref weak) = self.value {
if let Some(strong) = weak.upgrade() {
strong.mark_and_visit();
}
}
}
}
impl Drop for TestObject {
fn drop(&mut self) {
println!("Dropping TestObject: address={:p}", self);
}
}
struct TestObjectCell(RefCell<TestObject>);
impl GCTraceable for TestObjectCell {
fn visit(&self) {
self.0.borrow().visit();
}
}
impl Drop for TestObjectCell {
fn drop(&mut self) {
println!("Dropping TestObjectCell: address={:p}", self);
}
}
#[test]
fn test_gc() {
let mut gc : GC<TestObjectCell> = GC::new_with_percentage(20);
{
let obj1 = gc.create(TestObjectCell {
0: RefCell::new(TestObject { value: None }),
});
let weak_ref = obj1.as_weak();
match obj1.as_ref().0.try_borrow_mut() {
Ok(mut obj) => {
obj.value = Some(weak_ref);
}
Err(_) => {
panic!("Failed to borrow TestObjectCell mutably");
}
}
print!("GC object count before collection: {}\n", gc.object_count());
}
gc.collect();
println!("GC completed, all objects should be dropped now.");
}
}