1use ::std::cell::{Cell, RefCell};
2use ::std::mem::{size_of, size_of_val};
3use ::std::ptr::NonNull;
4use gc::Gc;
5use gc_alloc_err::GcAllocErr;
6use gc_box::GcBox;
7use trace::Trace;
8
9#[derive(Debug)]
15pub struct GcScope<'gc> {
16 state: RefCell<GcState<'gc>>,
17}
18
19impl<'gc> GcScope<'gc> {
20 pub fn new() -> GcScope<'gc> {
21 GcScope { state: RefCell::new(GcState::new()) }
22 }
23
24 pub fn alloc<T: Trace + 'gc>(&'gc self, value: T) -> Result<Gc<'gc, T>, GcAllocErr> {
26 unsafe { value.unroot() }
27 self.state.borrow_mut()
28 .alloc(value)
29 .map(|ptr| Gc::new(ptr))
30 }
31
32 pub fn collect_garbage(&self) {
33 self.state.borrow_mut().collect_garbage()
34 }
35}
36
37#[derive(Debug)]
38struct GcState<'gc> {
39 pub(crate) allocated_bytes: usize,
40 pub(crate) boxes: Option<NonNull<GcBox<'gc, dyn Trace>>>,
43}
44
45impl<'gc> GcState<'gc> {
46 pub(crate) fn new() -> GcState<'gc> {
47 GcState {
48 allocated_bytes: 0,
49 boxes: None,
50 }
51 }
52
53 pub(crate) fn alloc<T: Trace + 'gc>(&mut self, value: T) -> Result<NonNull<GcBox<'gc, T>>, GcAllocErr> {
55 let gc_box_ptr: *mut GcBox<T> = Box::into_raw(Box::new(GcBox {
57 roots: Cell::new(1),
58 marked: Cell::new(false),
59 next: self.boxes,
60 value: value,
61 }));
62 self.allocated_bytes += size_of::<GcBox<T>>();
63 let box_ptr: NonNull<GcBox<T>> = unsafe { NonNull::new_unchecked(gc_box_ptr) };
65 self.boxes = Some(box_ptr);
66 Ok(unsafe { NonNull::new_unchecked(gc_box_ptr) })
67 }
68
69 pub(crate) fn collect_garbage(&mut self) {
70 {
71 let mut next_gc_box_ptr = self.boxes;
73 while let Some(gc_box_ptr) = next_gc_box_ptr {
74 let gc_box: &GcBox<dyn Trace> = unsafe { gc_box_ptr.as_ref() };
75 if gc_box.roots.get() > 0 {
76 gc_box.mark_box();
77 }
78 next_gc_box_ptr = gc_box.next;
79 }
80 }
81
82 let mut unmarked: Vec<*mut GcBox<dyn Trace>> = Vec::new();
83 unsafe {
84 let mut next_gc_box_ptr_ref = &mut self.boxes;
86 while let Some(gc_box_ptr) = *next_gc_box_ptr_ref {
87 let gc_box_ptr = gc_box_ptr.as_ptr();
88 if (*gc_box_ptr).marked.get() {
89 (*gc_box_ptr).marked.set(false);
90 next_gc_box_ptr_ref = &mut (*gc_box_ptr).next;
91 } else {
92 *next_gc_box_ptr_ref = (*gc_box_ptr).next;
93 unmarked.push(gc_box_ptr);
94 }
95 }
96 }
97
98 for gc_box_ptr in unmarked.iter() {
99 let gc_box = unsafe { Box::from_raw(*gc_box_ptr) };
100 self.allocated_bytes = self.allocated_bytes.checked_sub(size_of_val::<GcBox<_>>(gc_box.as_ref())).unwrap()
101 }
103 }
104}
105
106unsafe impl<#[may_dangle] 'gc> Drop for GcState<'gc> {
107 fn drop(&mut self) {
108 let mut cur_box = self.boxes;
109 while let Some(gc_box_ptr) = cur_box {
110 let gc_box = unsafe { Box::from_raw(gc_box_ptr.as_ptr()) };
111 cur_box = (*gc_box).next;
112 }
114 }
115}