1
- use crate :: trace:: { Finalize , Trace } ;
1
+ use crate :: trace:: Trace ;
2
2
use std:: cell:: { Cell , RefCell } ;
3
3
use std:: mem;
4
4
use std:: ptr:: NonNull ;
@@ -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
+ node. data . finalize ( ) ;
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 ( node) = head {
34
+ head = node. header . next ;
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,62 +152,54 @@ 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 ( head : & Option < Box < GcBox < dyn Trace > > > ) {
160
156
// 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 ( ) ;
165
161
}
166
162
167
- mark_head = ( * node. as_ptr ( ) ) . header . next ;
163
+ mark_head = & node. header . next ;
168
164
}
165
+ }
169
166
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 ;
173
176
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 ;
177
183
} 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) ;
182
190
}
183
- unmark_head = & mut ( * node. as_ptr ( ) ) . header . next ;
184
191
}
185
- unmarked
186
- }
187
192
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 {
196
194
* 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
198
197
}
199
198
}
200
199
201
200
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 ) ;
211
203
}
212
204
}
213
205
0 commit comments