(
&self,
root: &R,
mut target_debt: f64,
early_stop: Option<EarlyStop>,
)
| 275 | } |
| 276 | |
| 277 | fn do_collection_inner<R: Collect + ?Sized>( |
| 278 | &self, |
| 279 | root: &R, |
| 280 | mut target_debt: f64, |
| 281 | early_stop: Option<EarlyStop>, |
| 282 | ) { |
| 283 | let mut entered = PhaseGuard::enter(self, None); |
| 284 | |
| 285 | if !(self.metrics.allocation_debt() > target_debt) { |
| 286 | entered.log_progress("GC: paused"); |
| 287 | return; |
| 288 | } |
| 289 | |
| 290 | loop { |
| 291 | match self.phase.get() { |
| 292 | Phase::Sleep => { |
| 293 | // Immediately enter the mark phase; no need to update metrics here. |
| 294 | entered.switch(Phase::Mark); |
| 295 | continue; |
| 296 | } |
| 297 | Phase::Mark => { |
| 298 | // We look for an object first in the normal gray queue, then the "gray again" |
| 299 | // queue. Objects from the normal gray queue count as regular work, but objects |
| 300 | // which are gray a second time have already been counted as work, so we don't |
| 301 | // double count them. Processing "gray again" objects later also gives them more |
| 302 | // time to be mutated again without triggering another write barrier. |
| 303 | let next_gray = if let Some(gc_box) = self.gray.borrow_mut().pop() { |
| 304 | self.metrics.mark_gc_traced(gc_box.header().size_of_box()); |
| 305 | Some(gc_box) |
| 306 | } else { |
| 307 | self.gray_again.borrow_mut().pop() |
| 308 | }; |
| 309 | |
| 310 | if let Some(gc_box) = next_gray { |
| 311 | // If we have an object in the gray queue, take one, trace it, and turn it |
| 312 | // black. |
| 313 | |
| 314 | // Our `Collect::trace` call may panic, and if it does the object will be |
| 315 | // lost from the gray queue but potentially incompletely traced. By catching |
| 316 | // a panic during `Arena::collect()`, this could lead to memory unsafety. |
| 317 | // |
| 318 | // So, if the `Collect::trace` call panics, we need to add the popped object |
| 319 | // back to the `gray_again` queue. If the panic is caught, this will maybe |
| 320 | // give it some time to not panic before attempting to collect it again, and |
| 321 | // also this doesn't invalidate the collection debt math. |
| 322 | struct DropGuard<'a> { |
| 323 | cx: &'a Context, |
| 324 | gc_box: GcBox, |
| 325 | } |
| 326 | |
| 327 | impl Drop for DropGuard<'_> { |
| 328 | fn drop(&mut self) { |
| 329 | self.cx.gray_again.borrow_mut().push(self.gc_box); |
| 330 | } |
| 331 | } |
| 332 | |
| 333 | let guard = DropGuard { cx: self, gc_box }; |
| 334 | debug_assert!(gc_box.header().is_live()); |
no test coverage detected