1- use crate :: trace:: { Finalize , Trace } ;
1+ use crate :: trace:: Trace ;
22use std:: cell:: { Cell , RefCell } ;
33use std:: mem;
44use std:: ptr:: NonNull ;
@@ -14,25 +14,24 @@ const USED_SPACE_RATIO: f64 = 0.7;
1414struct 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
2020impl 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+ node. data . finalize ( ) ;
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.
154154fn 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