@@ -126,22 +126,29 @@ struct AnnihilateStats {
126
126
n_bytes_freed : uint
127
127
}
128
128
129
- unsafe fn each_live_alloc ( f : & fn ( box : * mut BoxRepr , uniq : bool ) -> bool ) {
129
+ unsafe fn each_live_alloc ( read_next_before : bool ,
130
+ f : & fn ( box : * mut BoxRepr , uniq : bool ) -> bool ) {
131
+ //! Walks the internal list of allocations
132
+
130
133
use managed;
131
134
132
135
let task: * Task = transmute ( rustrt:: rust_get_task ( ) ) ;
133
136
let box = ( * task) . boxed_region . live_allocs ;
134
137
let mut box: * mut BoxRepr = transmute ( copy box) ;
135
138
while box != mut_null ( ) {
136
- let next = transmute ( copy ( * box) . header . next ) ;
139
+ let next_before = transmute ( copy ( * box) . header . next ) ;
137
140
let uniq =
138
141
( * box) . header . ref_count == managed:: raw:: RC_MANAGED_UNIQUE ;
139
142
140
143
if ! f ( box, uniq) {
141
144
break
142
145
}
143
146
144
- box = next
147
+ if read_next_before {
148
+ box = next_before;
149
+ } else {
150
+ box = transmute ( copy ( * box) . header . next ) ;
151
+ }
145
152
}
146
153
}
147
154
@@ -173,7 +180,10 @@ pub unsafe fn annihilate() {
173
180
} ;
174
181
175
182
// Pass 1: Make all boxes immortal.
176
- for each_live_alloc |box, uniq| {
183
+ //
184
+ // In this pass, nothing gets freed, so it does not matter whether
185
+ // we read the next field before or after the callback.
186
+ for each_live_alloc( true ) |box, uniq| {
177
187
stats. n_total_boxes += 1 ;
178
188
if uniq {
179
189
stats. n_unique_boxes += 1 ;
@@ -183,7 +193,11 @@ pub unsafe fn annihilate() {
183
193
}
184
194
185
195
// Pass 2: Drop all boxes.
186
- for each_live_alloc |box, uniq| {
196
+ //
197
+ // In this pass, unique-managed boxes may get freed, but not
198
+ // managed boxes, so we must read the `next` field *after* the
199
+ // callback, as the original value may have been freed.
200
+ for each_live_alloc( false ) |box, uniq| {
187
201
if !uniq {
188
202
let tydesc: * TypeDesc = transmute ( copy ( * box) . header . type_desc ) ;
189
203
let drop_glue: DropGlue = transmute ( ( ( * tydesc) . drop_glue , 0 ) ) ;
@@ -192,7 +206,12 @@ pub unsafe fn annihilate() {
192
206
}
193
207
194
208
// Pass 3: Free all boxes.
195
- for each_live_alloc |box, uniq| {
209
+ //
210
+ // In this pass, managed boxes may get freed (but not
211
+ // unique-managed boxes, though I think that none of those are
212
+ // left), so we must read the `next` field before, since it will
213
+ // not be valid after.
214
+ for each_live_alloc( true ) |box, uniq| {
196
215
if !uniq {
197
216
stats. n_bytes_freed +=
198
217
( * ( ( * box) . header . type_desc ) ) . size
0 commit comments