Skip to content

Commit bf14a6d

Browse files
committed
Clarify allocation ownership model
1 parent 9b05709 commit bf14a6d

File tree

1 file changed

+52
-60
lines changed

1 file changed

+52
-60
lines changed

gc/src/gc.rs

+52-60
Original file line numberDiff line numberDiff line change
@@ -14,25 +14,24 @@ const USED_SPACE_RATIO: f64 = 0.7;
1414
struct GcState {
1515
bytes_allocated: usize,
1616
threshold: usize,
17-
boxes_start: Option<NonNull<GcBox<dyn Trace>>>,
17+
boxes_start: Option<Box<GcBox<dyn Trace>>>,
1818
}
1919

2020
impl Drop for GcState {
2121
fn drop(&mut self) {
22-
unsafe {
23-
{
24-
let mut p = &self.boxes_start;
25-
while let Some(node) = *p {
26-
Finalize::finalize(&(*node.as_ptr()).data);
27-
p = &(*node.as_ptr()).header.next;
28-
}
29-
}
22+
let mut head = &self.boxes_start;
23+
while let Some(ref node) = *head {
24+
Finalize::finalize(&node.data);
25+
head = &node.header.next;
26+
}
3027

31-
let _guard = DropGuard::new();
32-
while let Some(node) = self.boxes_start {
33-
let node = Box::from_raw(node.as_ptr());
34-
self.boxes_start = node.header.next;
35-
}
28+
// Drop all allocations in the singly-linked list.
29+
// This could be done with `self.boxes_start = None;`,
30+
// but that might lead to a large number of recursive drops.
31+
let _guard = DropGuard::new();
32+
let mut head = self.boxes_start.take();
33+
while let Some(node) = head {
34+
head = node.header.next;
3635
}
3736
}
3837
}
@@ -68,7 +67,7 @@ pub(crate) struct GcBoxHeader {
6867
// We are using a word word bool - there is a full 63 bits of unused data :(
6968
// XXX: Should be able to store marked in the high bit of roots?
7069
roots: Cell<usize>,
71-
next: Option<NonNull<GcBox<dyn Trace>>>,
70+
next: Option<Box<GcBox<dyn Trace>>>,
7271
marked: Cell<bool>,
7372
}
7473

@@ -98,22 +97,23 @@ impl<T: Trace> GcBox<T> {
9897
}
9998
}
10099

101-
let gcbox = Box::into_raw(Box::new(GcBox {
100+
let gcbox = Box::new(GcBox {
102101
header: GcBoxHeader {
103102
roots: Cell::new(1),
104103
marked: Cell::new(false),
105104
next: st.boxes_start.take(),
106105
},
107106
data: value,
108-
}));
107+
});
108+
let ptr = NonNull::from(&*gcbox);
109109

110-
st.boxes_start = Some(unsafe { NonNull::new_unchecked(gcbox) });
110+
st.boxes_start = Some(gcbox);
111111

112112
// We allocated some bytes! Let's record it
113113
st.bytes_allocated += mem::size_of::<GcBox<T>>();
114114

115115
// Return the pointer to the newly allocated data
116-
unsafe { NonNull::new_unchecked(gcbox) }
116+
ptr
117117
})
118118
}
119119
}
@@ -152,62 +152,54 @@ impl<T: Trace + ?Sized> GcBox<T> {
152152

153153
/// Collects garbage.
154154
fn collect_garbage(st: &mut GcState) {
155-
struct Unmarked {
156-
incoming: *mut Option<NonNull<GcBox<dyn Trace>>>,
157-
this: NonNull<GcBox<dyn Trace>>,
158-
}
159-
unsafe fn mark(head: &mut Option<NonNull<GcBox<dyn Trace>>>) -> Vec<Unmarked> {
155+
unsafe fn mark(head: &Option<Box<GcBox<dyn Trace>>>) {
160156
// Walk the tree, tracing and marking the nodes
161-
let mut mark_head = *head;
162-
while let Some(node) = mark_head {
163-
if (*node.as_ptr()).header.roots.get() > 0 {
164-
(*node.as_ptr()).trace_inner();
157+
let mut mark_head = head;
158+
while let Some(ref node) = *mark_head {
159+
if node.header.roots.get() > 0 {
160+
node.trace_inner();
165161
}
166162

167-
mark_head = (*node.as_ptr()).header.next;
163+
mark_head = &node.header.next;
168164
}
165+
}
169166

170-
// Collect a vector of all of the nodes which were not marked,
171-
// and unmark the ones which were.
172-
let mut unmarked = Vec::new();
167+
unsafe fn sweep(
168+
head: &mut Option<Box<GcBox<dyn Trace>>>,
169+
bytes_allocated: &mut usize,
170+
) {
171+
let _guard = DropGuard::new();
172+
173+
// Collect the unmarked nodes from the allocation list into a vector.
174+
// Also unmark the nodes which were marked, to prepare for the next GC.
175+
let mut unmarked = None;
173176
let mut unmark_head = head;
174-
while let Some(node) = *unmark_head {
175-
if (*node.as_ptr()).header.marked.get() {
176-
(*node.as_ptr()).header.marked.set(false);
177+
while let Some(mut node) = unmark_head.take() {
178+
if node.header.marked.get() {
179+
node.header.marked.set(false);
180+
// `get_or_insert()` will always re-insert `node`.
181+
// It is just used to get a reference to the next node pointer.
182+
unmark_head = &mut unmark_head.get_or_insert(node).header.next;
177183
} else {
178-
unmarked.push(Unmarked {
179-
incoming: unmark_head,
180-
this: node,
181-
});
184+
// Finalize the node's contents
185+
node.value().finalize_glue();
186+
// Move `node` from the allocation list to the unmarked list
187+
*unmark_head = node.header.next;
188+
node.header.next = unmarked;
189+
unmarked = Some(node);
182190
}
183-
unmark_head = &mut (*node.as_ptr()).header.next;
184191
}
185-
unmarked
186-
}
187192

188-
unsafe fn sweep(finalized: Vec<Unmarked>, bytes_allocated: &mut usize) {
189-
let _guard = DropGuard::new();
190-
for node in finalized.into_iter().rev() {
191-
if (*node.this.as_ptr()).header.marked.get() {
192-
continue;
193-
}
194-
let incoming = node.incoming;
195-
let mut node = Box::from_raw(node.this.as_ptr());
193+
while let Some(node) = unmarked {
196194
*bytes_allocated -= mem::size_of_val::<GcBox<_>>(&*node);
197-
*incoming = node.header.next.take();
195+
unmarked = node.header.next;
196+
// `node` is dropped here, freeing the allocation
198197
}
199198
}
200199

201200
unsafe {
202-
let unmarked = mark(&mut st.boxes_start);
203-
if unmarked.is_empty() {
204-
return;
205-
}
206-
for node in &unmarked {
207-
Trace::finalize_glue(&(*node.this.as_ptr()).data);
208-
}
209-
mark(&mut st.boxes_start);
210-
sweep(unmarked, &mut st.bytes_allocated);
201+
mark(&st.boxes_start);
202+
sweep(&mut st.boxes_start, &mut st.bytes_allocated);
211203
}
212204
}
213205

0 commit comments

Comments
 (0)