@@ -14,25 +14,24 @@ const USED_SPACE_RATIO: f64 = 0.7;
14
14
struct GcState {
15
15
bytes_allocated : usize ,
16
16
threshold : usize ,
17
- boxes_start : Option < NonNull < GcBox < dyn Trace > > > ,
17
+ boxes_start : Option < Box < GcBox < dyn Trace > > > ,
18
18
}
19
19
20
20
impl Drop for GcState {
21
21
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
+ }
30
27
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 ( mut node) = head {
34
+ head = node. header . next . take ( ) ;
36
35
}
37
36
}
38
37
}
@@ -68,7 +67,7 @@ pub(crate) struct GcBoxHeader {
68
67
// We are using a word word bool - there is a full 63 bits of unused data :(
69
68
// XXX: Should be able to store marked in the high bit of roots?
70
69
roots : Cell < usize > ,
71
- next : Option < NonNull < GcBox < dyn Trace > > > ,
70
+ next : Option < Box < GcBox < dyn Trace > > > ,
72
71
marked : Cell < bool > ,
73
72
}
74
73
@@ -98,22 +97,23 @@ impl<T: Trace> GcBox<T> {
98
97
}
99
98
}
100
99
101
- let gcbox = Box :: into_raw ( Box :: new ( GcBox {
100
+ let gcbox = Box :: new ( GcBox {
102
101
header : GcBoxHeader {
103
102
roots : Cell :: new ( 1 ) ,
104
103
marked : Cell :: new ( false ) ,
105
104
next : st. boxes_start . take ( ) ,
106
105
} ,
107
106
data : value,
108
- } ) ) ;
107
+ } ) ;
108
+ let ptr = NonNull :: from ( & * gcbox) ;
109
109
110
- st. boxes_start = Some ( unsafe { NonNull :: new_unchecked ( gcbox) } ) ;
110
+ st. boxes_start = Some ( gcbox) ;
111
111
112
112
// We allocated some bytes! Let's record it
113
113
st. bytes_allocated += mem:: size_of :: < GcBox < T > > ( ) ;
114
114
115
115
// Return the pointer to the newly allocated data
116
- unsafe { NonNull :: new_unchecked ( gcbox ) }
116
+ ptr
117
117
} )
118
118
}
119
119
}
@@ -152,49 +152,43 @@ impl<T: Trace + ?Sized> GcBox<T> {
152
152
153
153
/// Collects garbage.
154
154
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 (
156
+ head : & mut Option < Box < GcBox < dyn Trace > > > ,
157
+ ) -> Vec < Box < GcBox < dyn Trace > > > {
160
158
// 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 ( ) ;
159
+ let mut mark_head = & * head;
160
+ while let Some ( ref node) = * mark_head {
161
+ if node. header . roots . get ( ) > 0 {
162
+ node. trace_inner ( ) ;
165
163
}
166
164
167
- mark_head = ( * node. as_ptr ( ) ) . header . next ;
165
+ mark_head = & node. header . next ;
168
166
}
169
167
170
- // Collect a vector of all of the nodes which were not marked,
171
- // and unmark the ones which were.
168
+ // Collect the unmarked nodes from the allocation list into a vector.
169
+ // Also unmark the nodes which were marked, to prepare for the next GC .
172
170
let mut unmarked = Vec :: new ( ) ;
173
171
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 ) ;
172
+ while let Some ( mut node) = unmark_head. take ( ) {
173
+ if node. header . marked . get ( ) {
174
+ node. header . marked . set ( false ) ;
175
+ // `get_or_insert()` will always re-insert `node`.
176
+ // It is just used to get a reference to the next node pointer.
177
+ unmark_head = & mut unmark_head. get_or_insert ( node) . header . next ;
177
178
} else {
178
- unmarked. push ( Unmarked {
179
- incoming : unmark_head,
180
- this : node,
181
- } ) ;
179
+ // Replace the pointer to `node` with a pointer to the next node
180
+ * unmark_head = node. header . next . take ( ) ;
181
+ unmarked. push ( node) ;
182
182
}
183
- unmark_head = & mut ( * node. as_ptr ( ) ) . header . next ;
184
183
}
185
184
unmarked
186
185
}
187
186
188
- unsafe fn sweep ( finalized : Vec < Unmarked > , bytes_allocated : & mut usize ) {
187
+ unsafe fn sweep ( finalized : Vec < Box < GcBox < dyn Trace > > > , bytes_allocated : & mut usize ) {
189
188
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 ( ) ) ;
189
+ for node in finalized {
196
190
* bytes_allocated -= mem:: size_of_val :: < GcBox < _ > > ( & * node) ;
197
- * incoming = node. header . next . take ( ) ;
191
+ // ` node` is dropped here, freeing the allocation
198
192
}
199
193
}
200
194
@@ -204,9 +198,8 @@ fn collect_garbage(st: &mut GcState) {
204
198
return ;
205
199
}
206
200
for node in & unmarked {
207
- Trace :: finalize_glue ( & ( * node. this . as_ptr ( ) ) . data ) ;
201
+ Trace :: finalize_glue ( & node. data ) ;
208
202
}
209
- mark ( & mut st. boxes_start ) ;
210
203
sweep ( unmarked, & mut st. bytes_allocated ) ;
211
204
}
212
205
}
0 commit comments