1use std::{
2 collections::VecDeque,
3 sync::{atomic::AtomicUsize, Mutex},
4};
5
6use rustc_hash::FxHashMap;
7
8use crate::{
9 arc::{GCArc, GCRef},
10 traceable::GCTraceable,
11};
12
13pub struct GC<T: GCTraceable<T> + 'static> {
14 gc_refs: Mutex<Vec<GCArc<T>>>,
15 attach_count: AtomicUsize,
16 collection_percentage: usize, memory_threshold: Option<usize>, allocated_memory: AtomicUsize, }
20
21#[allow(dead_code)]
22impl<T> GC<T>
23where
24 T: GCTraceable<T> + 'static,
25{ pub fn new() -> Self {
27 Self {
28 gc_refs: Mutex::new(Vec::new()),
29 attach_count: AtomicUsize::new(0),
30 collection_percentage: 20, memory_threshold: None, allocated_memory: AtomicUsize::new(0),
33 }
34 } pub fn new_with_percentage(percentage: usize) -> Self {
37 Self {
38 gc_refs: Mutex::new(Vec::new()),
39 attach_count: AtomicUsize::new(0),
40 collection_percentage: percentage,
41 memory_threshold: None, allocated_memory: AtomicUsize::new(0),
43 }
44 }
45
46 pub fn new_with_memory_threshold(memory_threshold: usize) -> Self {
49 Self {
50 gc_refs: Mutex::new(Vec::new()),
51 attach_count: AtomicUsize::new(0),
52 collection_percentage: 20, memory_threshold: Some(memory_threshold),
54 allocated_memory: AtomicUsize::new(0),
55 }
56 }
57
58 pub fn new_with_thresholds(percentage: usize, memory_threshold: usize) -> Self {
61 Self {
62 gc_refs: Mutex::new(Vec::new()),
63 attach_count: AtomicUsize::new(0),
64 collection_percentage: percentage,
65 memory_threshold: Some(memory_threshold),
66 allocated_memory: AtomicUsize::new(0),
67 }
68 } pub fn attach(&mut self, gc_arc: &GCArc<T>) {
69 {
70 let mut gc_refs = self.gc_refs.lock().unwrap();
71 gc_refs.push(gc_arc.clone());
72 }
73
74 self.attach_count
75 .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
76
77 gc_arc
78 .inner()
79 .attached_gc_count
80 .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
81
82 let obj_size = std::mem::size_of::<T>() + std::mem::size_of::<GCArc<T>>();
84 self.allocated_memory
85 .fetch_add(obj_size, std::sync::atomic::Ordering::Relaxed);
86
87 if self.should_collect() {
89 self.collect();
90 }
91 } pub fn detach(&mut self, gc_arc: &GCArc<T>) -> bool {
92 let mut gc_refs = self.gc_refs.lock().unwrap();
93 if let Some(index) = gc_refs.iter().position(|r| GCArc::ptr_eq(r, gc_arc)) {
94 gc_refs.swap_remove(index);
95 gc_arc
96 .inner()
97 .attached_gc_count
98 .fetch_sub(1, std::sync::atomic::Ordering::Relaxed);
99
100 let obj_size = std::mem::size_of::<T>() + std::mem::size_of::<GCArc<T>>();
102 self.allocated_memory
103 .fetch_sub(obj_size, std::sync::atomic::Ordering::Relaxed);
104
105 true
106 } else {
107 false
108 }
109 }
110 pub fn collect(&mut self) {
111 let mut refs = self.gc_refs.lock().unwrap();
119
120 let mut marked = FxHashMap::default();
124
125 for r in refs.iter() {
128 marked.insert(r.as_ref() as *const T as usize, false);
130 }
131
132 let mut queue = VecDeque::new();
136
137 for r in refs.iter() {
144 if r.strong_ref()
145 > r.inner()
146 .attached_gc_count
147 .load(std::sync::atomic::Ordering::Relaxed)
148 {
149 queue.push_back(r.as_weak());
151 }
152 }
153
154 while !queue.is_empty() {
157 let current_weak = queue.pop_front().unwrap();
160
161 let Some(current_strong) = current_weak.upgrade() else {
165 continue; };
167
168 let current_ptr = current_strong.as_ref() as *const T as usize;
170
171 if *marked.get(¤t_ptr).unwrap_or(&false) {
177 continue; }
179
180 marked.insert(current_ptr, true);
182
183 current_strong.as_ref().collect(&mut queue);
187 } let retained: Vec<GCArc<T>> = refs
191 .iter()
192 .filter(|r| {
193 let ptr = r.as_ref() as *const T as usize;
194 let retain = *marked.get(&ptr).unwrap_or(&false);
198 if !retain {
199 r.inner()
201 .attached_gc_count
202 .fetch_sub(1, std::sync::atomic::Ordering::Relaxed);
203
204 let obj_size = std::mem::size_of::<T>() + std::mem::size_of::<GCArc<T>>();
206 self.allocated_memory
207 .fetch_sub(obj_size, std::sync::atomic::Ordering::Relaxed);
208 }
209 retain
210 })
211 .cloned() .collect();
213
214 refs.clear();
216 refs.extend(retained);
221
222 self.attach_count
226 .store(0, std::sync::atomic::Ordering::Relaxed);
227 }
228 pub fn object_count(&self) -> usize {
229 return self.gc_refs.lock().unwrap().len();
230 }
231
232 pub fn get_all(&self) -> Vec<GCArc<T>> {
233 self.gc_refs.lock().unwrap().clone()
234 }
235
236 pub fn create(&mut self, obj: T) -> GCArc<T> {
237 let gc_arc = GCArc::new(obj);
238 self.attach(&gc_arc);
239 gc_arc
240 }
241
242 pub fn allocated_memory(&self) -> usize {
244 self.allocated_memory.load(std::sync::atomic::Ordering::Relaxed)
245 }
246
247 pub fn set_memory_threshold(&mut self, threshold: Option<usize>) {
249 self.memory_threshold = threshold;
250 }
251
252 pub fn memory_threshold(&self) -> Option<usize> {
254 self.memory_threshold
255 } fn should_collect(&self) -> bool {
256 let current_count = self.gc_refs.lock().unwrap().len();
257 let attach_count = self.attach_count.load(std::sync::atomic::Ordering::Relaxed);
258 let current_memory = self.allocated_memory.load(std::sync::atomic::Ordering::Relaxed);
259
260 if current_count == 0 {
261 return false;
262 }
263
264 if let Some(memory_threshold) = self.memory_threshold {
266 if current_memory >= memory_threshold {
267 return true;
268 }
269 }
270
271 let threshold = (current_count * self.collection_percentage) / 100;
273 attach_count >= threshold.max(1) }
275}
276
277impl<T> Drop for GC<T>
278where
279 T: GCTraceable<T> + 'static,
280{ fn drop(&mut self) {
281 let mut refs = self.gc_refs.lock().unwrap();
284 for gc_arc in refs.drain(..) {
285 gc_arc
287 .inner()
288 .attached_gc_count
289 .fetch_sub(1, std::sync::atomic::Ordering::Relaxed);
290
291 let obj_size = std::mem::size_of::<T>() + std::mem::size_of::<GCArc<T>>();
293 self.allocated_memory
294 .fetch_sub(obj_size, std::sync::atomic::Ordering::Relaxed);
295
296 drop(gc_arc);
299 }
300 }
301}
302
303#[cfg(test)]
304mod tests {
305 use std::cell::RefCell;
306
307 use super::*;
308 use crate::{arc::GCArcWeak, traceable::GCTraceable};
309
310 struct TestObject {
311 value: Option<GCArcWeak<TestObjectCell>>,
312 }
313
314 impl GCTraceable<TestObjectCell> for TestObject {
315 fn collect(&self, queue: &mut VecDeque<GCArcWeak<TestObjectCell>>) {
316 if let Some(ref weak_ref) = self.value {
317 queue.push_back(weak_ref.clone());
318 }
319 }
320 }
321
322 impl Drop for TestObject {
323 fn drop(&mut self) {
324 println!("Dropping TestObject: address={:p}", self);
325 }
326 }
327
328 struct TestObjectCell(RefCell<TestObject>);
329 impl GCTraceable<TestObjectCell> for TestObjectCell {
330 fn collect(&self, queue: &mut VecDeque<GCArcWeak<TestObjectCell>>) {
331 if let Ok(obj) = self.0.try_borrow() {
332 if let Some(ref weak_ref) = obj.value {
333 queue.push_back(weak_ref.clone());
334 }
335 }
336 }
337 }
338 impl Drop for TestObjectCell {
339 fn drop(&mut self) {
340 println!("Dropping TestObjectCell: address={:p}", self);
341 }
342 }
343
344 #[test]
345 fn test_gc() {
346 let mut gc: GC<TestObjectCell> = GC::new_with_percentage(20);
347 {
348 let obj1 = gc.create(TestObjectCell {
349 0: RefCell::new(TestObject { value: None }),
350 });
351 let weak_ref = obj1.as_weak();
352 match obj1.as_ref().0.try_borrow_mut() {
353 Ok(mut obj) => {
354 obj.value = Some(weak_ref);
355 }
356 Err(_) => {
357 panic!("Failed to borrow TestObjectCell mutably");
358 }
359 }
360 print!("GC object count before collection: {}\n", gc.object_count());
361 }
362 gc.collect();
363 println!("GC completed, all objects should be dropped now.");
364 }
365
366 #[test]
367 fn test_memory_threshold_gc() {
368 let mut gc: GC<TestObjectCell> = GC::new_with_memory_threshold(1024);
370
371 println!("Initial allocated memory: {} bytes", gc.allocated_memory());
372
373 let mut objects = Vec::new();
375 for i in 0..50 {
376 let obj = gc.create(TestObjectCell {
377 0: RefCell::new(TestObject { value: None }),
378 });
379 objects.push(obj);
380
381 println!("After creating object {}: allocated={} bytes, object_count={}",
382 i + 1, gc.allocated_memory(), gc.object_count());
383
384 if gc.allocated_memory() > 1024 {
385 break;
386 }
387 }
388
389 println!("Before collection: allocated={} bytes, object_count={}",
390 gc.allocated_memory(), gc.object_count());
391
392 objects.clear();
394
395 gc.collect();
397
398 println!("After collection: allocated={} bytes, object_count={}",
399 gc.allocated_memory(), gc.object_count());
400 }
401
402 #[test]
403 fn test_combined_thresholds_gc() {
404 let mut gc: GC<TestObjectCell> = GC::new_with_thresholds(50, 2048); println!("Testing combined thresholds: 50% or 2KB");
408
409 let obj1 = gc.create(TestObjectCell {
410 0: RefCell::new(TestObject { value: None }),
411 });
412
413 println!("Memory threshold: {:?}", gc.memory_threshold());
414 println!("Allocated memory: {} bytes", gc.allocated_memory());
415 println!("Object count: {}", gc.object_count());
416
417 let _keep_ref = obj1;
419 }
420}