diff --git a/src/plan/generational/copying/global.rs b/src/plan/generational/copying/global.rs index 711d894f1d..f2f084bc1d 100644 --- a/src/plan/generational/copying/global.rs +++ b/src/plan/generational/copying/global.rs @@ -123,12 +123,9 @@ impl Plan for GenCopy { if full_heap { self.fromspace().release(); } + } - // TODO: Refactor so that we set the next_gc_full_heap in gen.release(). Currently have to fight with Rust borrow checker - // NOTE: We have to take care that the `Gen::should_next_gc_be_full_heap()` function is - // called _after_ all spaces have been released (including ones in `gen`) as otherwise we - // may get incorrect results since the function uses values such as available pages that - // will change dependant on which spaces have been released + fn end_of_gc(&mut self, _tls: VMWorkerThread) { self.gen .set_next_gc_full_heap(Gen::should_next_gc_be_full_heap(self)); } diff --git a/src/plan/generational/immix/global.rs b/src/plan/generational/immix/global.rs index 5d8865a93d..025c6855c7 100644 --- a/src/plan/generational/immix/global.rs +++ b/src/plan/generational/immix/global.rs @@ -165,12 +165,9 @@ impl Plan for GenImmix { } self.last_gc_was_full_heap .store(full_heap, Ordering::Relaxed); + } - // TODO: Refactor so that we set the next_gc_full_heap in gen.release(). Currently have to fight with Rust borrow checker - // NOTE: We have to take care that the `Gen::should_next_gc_be_full_heap()` function is - // called _after_ all spaces have been released (including ones in `gen`) as otherwise we - // may get incorrect results since the function uses values such as available pages that - // will change dependant on which spaces have been released + fn end_of_gc(&mut self, _tls: VMWorkerThread) { self.gen .set_next_gc_full_heap(Gen::should_next_gc_be_full_heap(self)); } diff --git a/src/plan/global.rs b/src/plan/global.rs index b9ecd538aa..f0d8d0248d 100644 --- a/src/plan/global.rs +++ b/src/plan/global.rs @@ -221,17 +221,22 @@ pub trait Plan: 'static + Sync + Downcast { } /// Prepare the plan before a GC. This is invoked in an initial step in the GC. - /// This is invoked once per GC by one worker thread. 'tls' is the worker thread that executes this method. + /// This is invoked once per GC by one worker thread. `tls` is the worker thread that executes this method. fn prepare(&mut self, tls: VMWorkerThread); /// Prepare a worker for a GC. Each worker has its own prepare method. This hook is for plan-specific /// per-worker preparation. This method is invoked once per worker by the worker thread passed as the argument. fn prepare_worker(&self, _worker: &mut GCWorker) {} - /// Release the plan after a GC. This is invoked at the end of a GC when most GC work is finished. - /// This is invoked once per GC by one worker thread. 'tls' is the worker thread that executes this method. + /// Release the plan after transitive closure. A plan can implement this method to call each policy's release, + /// or create any work packet that should be done in release. + /// This is invoked once per GC by one worker thread. `tls` is the worker thread that executes this method. fn release(&mut self, tls: VMWorkerThread); + /// Inform the plan about the end of a GC. It is guaranteed that there is no further work for this GC. + /// This is invoked once per GC by one worker thread. `tls` is the worker thread that executes this method. + fn end_of_gc(&mut self, _tls: VMWorkerThread) {} + /// Ask the plan if they would trigger a GC. If MMTk is in charge of triggering GCs, this method is called /// periodically during allocation. However, MMTk may delegate the GC triggering decision to the runtime, /// in which case, this method may not be called. This method returns true to trigger a collection. diff --git a/src/scheduler/gc_work.rs b/src/scheduler/gc_work.rs index b7025f8f1e..f2324addff 100644 --- a/src/scheduler/gc_work.rs +++ b/src/scheduler/gc_work.rs @@ -223,6 +223,11 @@ impl GCWork for EndOfGC { fn do_work(&mut self, worker: &mut GCWorker, mmtk: &'static MMTK) { info!("End of GC"); + // We assume this is the only running work packet that accesses plan at the point of execution + #[allow(clippy::cast_ref_to_mut)] + let plan_mut: &mut dyn Plan = unsafe { &mut *(&*mmtk.plan as *const _ as *mut _) }; + plan_mut.end_of_gc(worker.tls); + #[cfg(feature = "extreme_assertions")] if crate::util::edge_logger::should_check_duplicate_edges(&*mmtk.plan) { // reset the logging info at the end of each GC