-
Notifications
You must be signed in to change notification settings - Fork 80
Adding support for red/black roots #706
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 8 commits
d00e4c6
00a33f2
a062970
dd0a28b
b56af7d
b574a75
66e2709
38d2f6e
b86b321
8e99228
c013513
2ded32d
235f83a
13accdb
b73d892
57f5dcb
0dedd14
a915c22
72bc39d
3fe039d
c1feb58
86ffdcf
23090bb
45209c0
dc0582b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,22 +3,29 @@ use super::mutator::ALLOCATOR_MAPPING; | |
| use crate::plan::global::BasePlan; | ||
| use crate::plan::global::CommonPlan; | ||
| use crate::plan::global::GcStatus; | ||
| use crate::plan::mutator_context::MutatorContext; | ||
| use crate::plan::AllocationSemantics; | ||
| use crate::plan::Plan; | ||
| use crate::plan::PlanConstraints; | ||
| use crate::policy::immix::{TRACE_KIND_DEFRAG, TRACE_KIND_FAST}; | ||
| use crate::policy::immix::{TRACE_KIND_DEFRAG, TRACE_KIND_FAST, TRACE_KIND_IMMOVABLE}; | ||
| use crate::policy::space::Space; | ||
| use crate::scheduler::gc_work::PlanProcessEdges; | ||
| use crate::scheduler::gc_work::ScanVMImmovableRoots; | ||
| use crate::scheduler::*; | ||
| use crate::util::alloc::allocators::AllocatorSelector; | ||
| use crate::util::copy::*; | ||
| use crate::util::heap::layout::heap_layout::Mmapper; | ||
| use crate::util::heap::layout::heap_layout::VMMap; | ||
| use crate::util::heap::HeapMeta; | ||
| use crate::util::metadata::side_metadata::SideMetadataContext; | ||
| use crate::util::metadata::side_metadata::SideMetadataSanity; | ||
| use crate::util::metadata::side_metadata::{SideMetadataContext, SideMetadataSanity}; | ||
| use crate::util::options::Options; | ||
| use crate::vm::ActivePlan; | ||
| use crate::vm::Collection; | ||
| use crate::vm::Scanning; | ||
| use crate::vm::VMBinding; | ||
| use crate::MMTK; | ||
| use crate::{policy::immix::ImmixSpace, util::opaque_pointer::VMWorkerThread}; | ||
| use std::marker::PhantomData; | ||
| use std::sync::atomic::AtomicBool; | ||
| use std::sync::Arc; | ||
|
|
||
|
|
@@ -94,7 +101,13 @@ impl<VM: VMBinding> Plan for Immix<VM> { | |
| // The blocks are not identical, clippy is wrong. Probably it does not recognize the constant type parameter. | ||
| #[allow(clippy::if_same_then_else)] | ||
| if in_defrag { | ||
| scheduler.schedule_common_work::<ImmixGCWorkContext<VM, TRACE_KIND_DEFRAG>>(self); | ||
| schedule_stop_mutator_scan_immobile_roots::< | ||
| VM, | ||
| ImmixGCWorkContext<VM, TRACE_KIND_IMMOVABLE>, | ||
| >(scheduler, self); | ||
| schedule_remaining_work::<VM, ImmixGCWorkContext<VM, TRACE_KIND_DEFRAG>>( | ||
|
||
| scheduler, self, | ||
| ); | ||
| } else { | ||
| scheduler.schedule_common_work::<ImmixGCWorkContext<VM, TRACE_KIND_FAST>>(self); | ||
| } | ||
|
|
@@ -133,6 +146,138 @@ impl<VM: VMBinding> Plan for Immix<VM> { | |
| } | ||
| } | ||
|
|
||
| /// Stop all mutators and scan immovable roots | ||
| /// | ||
| /// Schedule a `ScanVMImmovableRoots` immediately after a mutator is paused | ||
| /// | ||
| /// TODO: Smaller work granularity | ||
| #[derive(Default)] | ||
| pub struct StopMutatorScanImmovable<ScanEdges: ProcessEdgesWork>(PhantomData<ScanEdges>); | ||
|
|
||
| impl<ScanEdges: ProcessEdgesWork> StopMutatorScanImmovable<ScanEdges> { | ||
| pub fn new() -> Self { | ||
| Self(PhantomData) | ||
| } | ||
| } | ||
|
|
||
| impl<E: ProcessEdgesWork> CoordinatorWork<E::VM> for StopMutatorScanImmovable<E> {} | ||
|
|
||
| impl<E: ProcessEdgesWork> GCWork<E::VM> for StopMutatorScanImmovable<E> { | ||
| fn do_work(&mut self, worker: &mut GCWorker<E::VM>, mmtk: &'static MMTK<E::VM>) { | ||
| // If the VM requires that only the coordinator thread can stop the world, | ||
| // we delegate the work to the coordinator. | ||
| if <E::VM as VMBinding>::VMCollection::COORDINATOR_ONLY_STW && !worker.is_coordinator() { | ||
| mmtk.scheduler | ||
| .add_coordinator_work(StopMutatorScanImmovable::<E>::new(), worker); | ||
| return; | ||
| } | ||
|
|
||
| trace!("stop_all_mutators start"); | ||
| mmtk.plan.base().prepare_for_stack_scanning(); | ||
| <E::VM as VMBinding>::VMCollection::stop_all_mutators(worker.tls, |mutator| { | ||
| mmtk.scheduler.work_buckets[WorkBucketStage::Prepare].add(ScanStackRoot::<E>(mutator)); | ||
| }); | ||
| trace!("stop_all_mutators end"); | ||
| mmtk.scheduler.notify_mutators_paused(mmtk); | ||
| if <E::VM as VMBinding>::VMScanning::SCAN_MUTATORS_IN_SAFEPOINT { | ||
| // Prepare mutators if necessary | ||
| // FIXME: This test is probably redundant. JikesRVM requires to call `prepare_mutator` once after mutators are paused | ||
| if !mmtk.plan.base().stacks_prepared() { | ||
| for mutator in <E::VM as VMBinding>::VMActivePlan::mutators() { | ||
| <E::VM as VMBinding>::VMCollection::prepare_mutator( | ||
| worker.tls, | ||
| mutator.get_tls(), | ||
| mutator, | ||
| ); | ||
| } | ||
| } | ||
| // Scan immovable roots with immovable trace | ||
| mmtk.scheduler.work_buckets[WorkBucketStage::Prepare].add(ScanVMImmovableRoots::< | ||
|
||
| PlanProcessEdges<E::VM, Immix<E::VM>, TRACE_KIND_IMMOVABLE>, | ||
| >::new()); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| fn schedule_stop_mutator_scan_immobile_roots<VM: VMBinding, C: GCWorkContext<VM = VM> + 'static>( | ||
| scheduler: &GCWorkScheduler<VM>, | ||
| _plan: &'static C::PlanType, | ||
| ) { | ||
| // Stop & scan mutators (mutator scanning can happen before STW) | ||
| scheduler.work_buckets[WorkBucketStage::Unconstrained] | ||
| .add(StopMutatorScanImmovable::<C::ProcessEdgesWorkType>::new()); | ||
| } | ||
|
|
||
| fn schedule_remaining_work<VM: VMBinding, C: GCWorkContext<VM = VM> + 'static>( | ||
| scheduler: &GCWorkScheduler<VM>, | ||
| plan: &'static C::PlanType, | ||
| ) { | ||
| use crate::scheduler::gc_work::*; | ||
| // Scan mutators (mutator scanning can happen before STW) | ||
| scheduler.work_buckets[WorkBucketStage::Prepare] | ||
| .add(ScanMutators::<C::ProcessEdgesWorkType>::new()); | ||
|
|
||
| // Prepare global/collectors/mutators | ||
| scheduler.work_buckets[WorkBucketStage::Prepare].add(Prepare::<C>::new(plan)); | ||
|
|
||
| // Release global/collectors/mutators | ||
| scheduler.work_buckets[WorkBucketStage::Release].add(Release::<C>::new(plan)); | ||
|
|
||
| // Analysis GC work | ||
| #[cfg(feature = "analysis")] | ||
| { | ||
| use crate::util::analysis::GcHookWork; | ||
| scheduler.work_buckets[WorkBucketStage::Unconstrained].add(GcHookWork); | ||
| } | ||
|
|
||
| // Sanity | ||
| #[cfg(feature = "sanity")] | ||
| { | ||
| use crate::util::sanity::sanity_checker::ScheduleSanityGC; | ||
| scheduler.work_buckets[WorkBucketStage::Final] | ||
| .add(ScheduleSanityGC::<C::PlanType>::new(plan)); | ||
| } | ||
|
|
||
| // Reference processing | ||
| if !*plan.base().options.no_reference_types { | ||
| use crate::util::reference_processor::{ | ||
| PhantomRefProcessing, SoftRefProcessing, WeakRefProcessing, | ||
| }; | ||
| scheduler.work_buckets[WorkBucketStage::SoftRefClosure] | ||
| .add(SoftRefProcessing::<C::ProcessEdgesWorkType>::new()); | ||
| scheduler.work_buckets[WorkBucketStage::WeakRefClosure] | ||
| .add(WeakRefProcessing::<C::ProcessEdgesWorkType>::new()); | ||
| scheduler.work_buckets[WorkBucketStage::PhantomRefClosure] | ||
| .add(PhantomRefProcessing::<C::ProcessEdgesWorkType>::new()); | ||
|
|
||
| // VM-specific weak ref processing | ||
| scheduler.work_buckets[WorkBucketStage::WeakRefClosure] | ||
| .add(VMProcessWeakRefs::<C::ProcessEdgesWorkType>::new()); | ||
|
|
||
| use crate::util::reference_processor::RefForwarding; | ||
| if plan.constraints().needs_forward_after_liveness { | ||
| scheduler.work_buckets[WorkBucketStage::RefForwarding] | ||
| .add(RefForwarding::<C::ProcessEdgesWorkType>::new()); | ||
| } | ||
|
|
||
| use crate::util::reference_processor::RefEnqueue; | ||
| scheduler.work_buckets[WorkBucketStage::Release].add(RefEnqueue::<VM>::new()); | ||
| } | ||
|
|
||
| // Finalization | ||
| if !*plan.base().options.no_finalizer { | ||
| use crate::util::finalizable_processor::{Finalization, ForwardFinalization}; | ||
| // finalization | ||
| scheduler.work_buckets[WorkBucketStage::FinalRefClosure] | ||
| .add(Finalization::<C::ProcessEdgesWorkType>::new()); | ||
| // forward refs | ||
| if plan.constraints().needs_forward_after_liveness { | ||
| scheduler.work_buckets[WorkBucketStage::FinalizableForwarding] | ||
| .add(ForwardFinalization::<C::ProcessEdgesWorkType>::new()); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl<VM: VMBinding> Immix<VM> { | ||
| pub fn new( | ||
| vm_map: &'static VMMap, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -115,3 +115,45 @@ impl<'a, E: ProcessEdgesWork> Drop for ObjectsClosure<'a, E> { | |
| self.flush(); | ||
| } | ||
| } | ||
|
|
||
| /// A transitive closure visitor to collect all the edges of an object during ClosureImmovable. | ||
| pub struct ImmovableObjectsClosure<'a, E: ProcessEdgesWork> { | ||
| buffer: VectorQueue<EdgeOf<E>>, | ||
| worker: &'a mut GCWorker<E::VM>, | ||
| } | ||
|
|
||
| impl<'a, E: ProcessEdgesWork> ImmovableObjectsClosure<'a, E> { | ||
| pub fn new(worker: &'a mut GCWorker<E::VM>) -> Self { | ||
| Self { | ||
| buffer: VectorQueue::new(), | ||
| worker, | ||
| } | ||
| } | ||
|
|
||
| fn flush(&mut self) { | ||
| let buf = self.buffer.take(); | ||
| if !buf.is_empty() { | ||
| self.worker.add_work( | ||
| WorkBucketStage::ClosureImmovable, | ||
|
||
| E::new(buf, false, self.worker.mmtk), | ||
| ); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl<'a, E: ProcessEdgesWork> EdgeVisitor<EdgeOf<E>> for ImmovableObjectsClosure<'a, E> { | ||
| #[inline(always)] | ||
| fn visit_edge(&mut self, slot: EdgeOf<E>) { | ||
| self.buffer.push(slot); | ||
| if self.buffer.is_full() { | ||
| self.flush(); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl<'a, E: ProcessEdgesWork> Drop for ImmovableObjectsClosure<'a, E> { | ||
| #[inline(always)] | ||
| fn drop(&mut self) { | ||
| self.flush(); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. I recommend adding a field to
PlanConstraintsto indicate whether a plan supports pinning. For example:supports_pinning: boolorallows_pinning: bool. Any plan that hasmoves_object = falseshould also havesupports_pinning = true. By doing this, we can adddebug_assert!orassert!here and panic early.