use super::work_bucket::WorkBucketStage;
use super::*;
use crate::plan::GcStatus;
use crate::plan::ObjectsClosure;
use crate::plan::VectorObjectQueue;
use crate::util::*;
use crate::vm::edge_shape::Edge;
use crate::vm::*;
use crate::*;
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
pub struct ScheduleCollection;
impl<VM: VMBinding> GCWork<VM> for ScheduleCollection {
fn do_work(&mut self, worker: &mut GCWorker<VM>, mmtk: &'static MMTK<VM>) {
mmtk.plan.schedule_collection(worker.scheduler());
}
}
impl<VM: VMBinding> CoordinatorWork<VM> for ScheduleCollection {}
pub struct Prepare<C: GCWorkContext> {
pub plan: &'static C::PlanType,
}
impl<C: GCWorkContext> Prepare<C> {
pub fn new(plan: &'static C::PlanType) -> Self {
Self { plan }
}
}
impl<C: GCWorkContext + 'static> GCWork<C::VM> for Prepare<C> {
fn do_work(&mut self, worker: &mut GCWorker<C::VM>, mmtk: &'static MMTK<C::VM>) {
trace!("Prepare Global");
#[allow(clippy::cast_ref_to_mut)]
let plan_mut: &mut C::PlanType = unsafe { &mut *(self.plan as *const _ as *mut _) };
plan_mut.prepare(worker.tls);
for mutator in <C::VM as VMBinding>::VMActivePlan::mutators() {
mmtk.scheduler.work_buckets[WorkBucketStage::Prepare]
.add(PrepareMutator::<C::VM>::new(mutator));
}
for w in &mmtk.scheduler.worker_group.workers_shared {
let result = w.designated_work.push(Box::new(PrepareCollector));
debug_assert!(result.is_ok());
}
}
}
pub struct PrepareMutator<VM: VMBinding> {
pub mutator: &'static mut Mutator<VM>,
}
impl<VM: VMBinding> PrepareMutator<VM> {
pub fn new(mutator: &'static mut Mutator<VM>) -> Self {
Self { mutator }
}
}
impl<VM: VMBinding> GCWork<VM> for PrepareMutator<VM> {
fn do_work(&mut self, worker: &mut GCWorker<VM>, _mmtk: &'static MMTK<VM>) {
trace!("Prepare Mutator");
self.mutator.prepare(worker.tls);
}
}
#[derive(Default)]
pub struct PrepareCollector;
impl<VM: VMBinding> GCWork<VM> for PrepareCollector {
fn do_work(&mut self, worker: &mut GCWorker<VM>, mmtk: &'static MMTK<VM>) {
trace!("Prepare Collector");
worker.get_copy_context_mut().prepare();
mmtk.plan.prepare_worker(worker);
}
}
pub struct Release<C: GCWorkContext> {
pub plan: &'static C::PlanType,
}
impl<C: GCWorkContext> Release<C> {
pub fn new(plan: &'static C::PlanType) -> Self {
Self { plan }
}
}
impl<C: GCWorkContext + 'static> GCWork<C::VM> for Release<C> {
fn do_work(&mut self, worker: &mut GCWorker<C::VM>, mmtk: &'static MMTK<C::VM>) {
trace!("Release Global");
<C::VM as VMBinding>::VMCollection::vm_release();
#[allow(clippy::cast_ref_to_mut)]
let plan_mut: &mut C::PlanType = unsafe { &mut *(self.plan as *const _ as *mut _) };
plan_mut.release(worker.tls);
for mutator in <C::VM as VMBinding>::VMActivePlan::mutators() {
mmtk.scheduler.work_buckets[WorkBucketStage::Release]
.add(ReleaseMutator::<C::VM>::new(mutator));
}
for w in &mmtk.scheduler.worker_group.workers_shared {
let result = w.designated_work.push(Box::new(ReleaseCollector));
debug_assert!(result.is_ok());
}
}
}
pub struct ReleaseMutator<VM: VMBinding> {
pub mutator: &'static mut Mutator<VM>,
}
impl<VM: VMBinding> ReleaseMutator<VM> {
pub fn new(mutator: &'static mut Mutator<VM>) -> Self {
Self { mutator }
}
}
impl<VM: VMBinding> GCWork<VM> for ReleaseMutator<VM> {
fn do_work(&mut self, worker: &mut GCWorker<VM>, _mmtk: &'static MMTK<VM>) {
trace!("Release Mutator");
self.mutator.release(worker.tls);
}
}
#[derive(Default)]
pub struct ReleaseCollector;
impl<VM: VMBinding> GCWork<VM> for ReleaseCollector {
fn do_work(&mut self, worker: &mut GCWorker<VM>, _mmtk: &'static MMTK<VM>) {
trace!("Release Collector");
worker.get_copy_context_mut().release();
}
}
#[derive(Default)]
pub struct StopMutators<ScanEdges: ProcessEdgesWork>(PhantomData<ScanEdges>);
impl<ScanEdges: ProcessEdgesWork> StopMutators<ScanEdges> {
pub fn new() -> Self {
Self(PhantomData)
}
}
impl<E: ProcessEdgesWork> GCWork<E::VM> for StopMutators<E> {
fn do_work(&mut self, worker: &mut GCWorker<E::VM>, mmtk: &'static MMTK<E::VM>) {
if <E::VM as VMBinding>::VMCollection::COORDINATOR_ONLY_STW && !worker.is_coordinator() {
mmtk.scheduler
.add_coordinator_work(StopMutators::<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 {
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,
);
}
}
if <E::VM as VMBinding>::VMScanning::SINGLE_THREAD_MUTATOR_SCANNING {
mmtk.scheduler.work_buckets[WorkBucketStage::Prepare]
.add(ScanStackRoots::<E>::new());
} else {
for mutator in <E::VM as VMBinding>::VMActivePlan::mutators() {
mmtk.scheduler.work_buckets[WorkBucketStage::Prepare]
.add(ScanStackRoot::<E>(mutator));
}
}
}
mmtk.scheduler.work_buckets[WorkBucketStage::Prepare].add(ScanVMSpecificRoots::<E>::new());
}
}
impl<E: ProcessEdgesWork> CoordinatorWork<E::VM> for StopMutators<E> {}
#[derive(Default)]
pub struct EndOfGC;
impl<VM: VMBinding> GCWork<VM> for EndOfGC {
fn do_work(&mut self, worker: &mut GCWorker<VM>, mmtk: &'static MMTK<VM>) {
info!("End of GC");
#[cfg(feature = "extreme_assertions")]
if crate::util::edge_logger::should_check_duplicate_edges(&*mmtk.plan) {
mmtk.edge_logger.reset();
}
if <VM as VMBinding>::VMCollection::COORDINATOR_ONLY_STW {
assert!(worker.is_coordinator(),
"VM only allows coordinator to resume mutators, but the current worker is not the coordinator.");
}
mmtk.plan.base().set_gc_status(GcStatus::NotInGC);
mmtk.plan.base().reset_collection_trigger();
<VM as VMBinding>::VMCollection::resume_mutators(worker.tls);
}
}
impl<VM: VMBinding> CoordinatorWork<VM> for EndOfGC {}
#[derive(Default)]
pub struct VMProcessWeakRefs<E: ProcessEdgesWork>(PhantomData<E>);
impl<E: ProcessEdgesWork> VMProcessWeakRefs<E> {
pub fn new() -> Self {
Self(PhantomData)
}
}
impl<E: ProcessEdgesWork> GCWork<E::VM> for VMProcessWeakRefs<E> {
fn do_work(&mut self, worker: &mut GCWorker<E::VM>, _mmtk: &'static MMTK<E::VM>) {
trace!("ProcessWeakRefs");
<E::VM as VMBinding>::VMCollection::process_weak_refs(worker); }
}
#[derive(Default)]
pub struct ScanStackRoots<Edges: ProcessEdgesWork>(PhantomData<Edges>);
impl<E: ProcessEdgesWork> ScanStackRoots<E> {
pub fn new() -> Self {
Self(PhantomData)
}
}
impl<E: ProcessEdgesWork> GCWork<E::VM> for ScanStackRoots<E> {
fn do_work(&mut self, worker: &mut GCWorker<E::VM>, mmtk: &'static MMTK<E::VM>) {
trace!("ScanStackRoots");
let factory = ProcessEdgesWorkRootsWorkFactory::<E>::new(mmtk);
<E::VM as VMBinding>::VMScanning::scan_thread_roots(worker.tls, factory);
<E::VM as VMBinding>::VMScanning::notify_initial_thread_scan_complete(false, worker.tls);
for mutator in <E::VM as VMBinding>::VMActivePlan::mutators() {
mutator.flush();
}
mmtk.plan.common().base.set_gc_status(GcStatus::GcProper);
}
}
pub struct ScanStackRoot<Edges: ProcessEdgesWork>(pub &'static mut Mutator<Edges::VM>);
impl<E: ProcessEdgesWork> GCWork<E::VM> for ScanStackRoot<E> {
fn do_work(&mut self, worker: &mut GCWorker<E::VM>, mmtk: &'static MMTK<E::VM>) {
trace!("ScanStackRoot for mutator {:?}", self.0.get_tls());
let base = &mmtk.plan.base();
let mutators = <E::VM as VMBinding>::VMActivePlan::number_of_mutators();
let factory = ProcessEdgesWorkRootsWorkFactory::<E>::new(mmtk);
<E::VM as VMBinding>::VMScanning::scan_thread_root(
worker.tls,
unsafe { &mut *(self.0 as *mut _) },
factory,
);
self.0.flush();
if mmtk.plan.base().inform_stack_scanned(mutators) {
<E::VM as VMBinding>::VMScanning::notify_initial_thread_scan_complete(
false, worker.tls,
);
base.set_gc_status(GcStatus::GcProper);
}
}
}
#[derive(Default)]
pub struct ScanVMSpecificRoots<Edges: ProcessEdgesWork>(PhantomData<Edges>);
impl<E: ProcessEdgesWork> ScanVMSpecificRoots<E> {
pub fn new() -> Self {
Self(PhantomData)
}
}
impl<E: ProcessEdgesWork> GCWork<E::VM> for ScanVMSpecificRoots<E> {
fn do_work(&mut self, worker: &mut GCWorker<E::VM>, mmtk: &'static MMTK<E::VM>) {
trace!("ScanStaticRoots");
let factory = ProcessEdgesWorkRootsWorkFactory::<E>::new(mmtk);
<E::VM as VMBinding>::VMScanning::scan_vm_specific_roots(worker.tls, factory);
}
}
pub struct ProcessEdgesBase<VM: VMBinding> {
pub edges: Vec<VM::VMEdge>,
pub nodes: VectorObjectQueue,
mmtk: &'static MMTK<VM>,
worker: *mut GCWorker<VM>,
pub roots: bool,
}
unsafe impl<VM: VMBinding> Send for ProcessEdgesBase<VM> {}
impl<VM: VMBinding> ProcessEdgesBase<VM> {
pub fn new(edges: Vec<VM::VMEdge>, roots: bool, mmtk: &'static MMTK<VM>) -> Self {
#[cfg(feature = "extreme_assertions")]
if crate::util::edge_logger::should_check_duplicate_edges(&*mmtk.plan) {
for edge in &edges {
mmtk.edge_logger.log_edge(*edge);
}
}
Self {
edges,
nodes: VectorObjectQueue::new(),
mmtk,
worker: std::ptr::null_mut(),
roots,
}
}
pub fn set_worker(&mut self, worker: &mut GCWorker<VM>) {
self.worker = worker;
}
#[inline]
pub fn worker(&self) -> &'static mut GCWorker<VM> {
unsafe { &mut *self.worker }
}
#[inline]
pub fn mmtk(&self) -> &'static MMTK<VM> {
self.mmtk
}
#[inline]
pub fn plan(&self) -> &'static dyn Plan<VM = VM> {
&*self.mmtk.plan
}
#[inline]
pub fn pop_nodes(&mut self) -> Vec<ObjectReference> {
self.nodes.take()
}
}
pub type EdgeOf<E> = <<E as ProcessEdgesWork>::VM as VMBinding>::VMEdge;
pub trait ProcessEdgesWork:
Send + 'static + Sized + DerefMut + Deref<Target = ProcessEdgesBase<Self::VM>>
{
type VM: VMBinding;
type ScanObjectsWorkType: ScanObjectsWork<Self::VM>;
const CAPACITY: usize = 4096;
const OVERWRITE_REFERENCE: bool = true;
const SCAN_OBJECTS_IMMEDIATELY: bool = true;
fn new(edges: Vec<EdgeOf<Self>>, roots: bool, mmtk: &'static MMTK<Self::VM>) -> Self;
fn trace_object(&mut self, object: ObjectReference) -> ObjectReference;
#[cfg(feature = "sanity")]
fn cache_roots_for_sanity_gc(&mut self) {
assert!(self.roots);
self.mmtk()
.sanity_checker
.lock()
.unwrap()
.add_roots(self.edges.clone());
}
#[inline]
fn start_or_dispatch_scan_work(&mut self, work_packet: impl GCWork<Self::VM>) {
if Self::SCAN_OBJECTS_IMMEDIATELY {
self.worker().do_work(work_packet);
} else {
self.mmtk.scheduler.work_buckets[WorkBucketStage::Closure].add(work_packet);
}
}
fn create_scan_work(
&self,
nodes: Vec<ObjectReference>,
roots: bool,
) -> Self::ScanObjectsWorkType;
#[cold]
fn flush(&mut self) {
let nodes = self.pop_nodes();
if !nodes.is_empty() {
self.start_or_dispatch_scan_work(self.create_scan_work(nodes, false));
}
}
#[inline]
fn process_edge(&mut self, slot: EdgeOf<Self>) {
let object = slot.load();
let new_object = self.trace_object(object);
if Self::OVERWRITE_REFERENCE {
slot.store(new_object);
}
}
#[inline]
fn process_edges(&mut self) {
for i in 0..self.edges.len() {
self.process_edge(self.edges[i])
}
}
}
impl<E: ProcessEdgesWork> GCWork<E::VM> for E {
#[inline]
fn do_work(&mut self, worker: &mut GCWorker<E::VM>, _mmtk: &'static MMTK<E::VM>) {
trace!("ProcessEdgesWork");
self.set_worker(worker);
self.process_edges();
if !self.nodes.is_empty() {
self.flush();
}
#[cfg(feature = "sanity")]
if self.roots {
self.cache_roots_for_sanity_gc();
}
trace!("ProcessEdgesWork End");
}
}
pub struct SFTProcessEdges<VM: VMBinding> {
pub base: ProcessEdgesBase<VM>,
}
impl<VM: VMBinding> ProcessEdgesWork for SFTProcessEdges<VM> {
type VM = VM;
type ScanObjectsWorkType = ScanObjects<Self>;
fn new(edges: Vec<EdgeOf<Self>>, roots: bool, mmtk: &'static MMTK<VM>) -> Self {
let base = ProcessEdgesBase::new(edges, roots, mmtk);
Self { base }
}
#[inline]
fn trace_object(&mut self, object: ObjectReference) -> ObjectReference {
use crate::policy::sft::GCWorkerMutRef;
use crate::policy::sft_map::SFTMap;
if object.is_null() {
return object;
}
let worker = GCWorkerMutRef::new(self.worker());
let sft = unsafe { crate::mmtk::SFT_MAP.get_unchecked(object.to_address::<VM>()) };
sft.sft_trace_object(&mut self.base.nodes, object, worker)
}
#[inline(always)]
fn create_scan_work(&self, nodes: Vec<ObjectReference>, roots: bool) -> ScanObjects<Self> {
ScanObjects::<Self>::new(nodes, false, roots)
}
}
struct ProcessEdgesWorkRootsWorkFactory<E: ProcessEdgesWork> {
mmtk: &'static MMTK<E::VM>,
}
impl<E: ProcessEdgesWork> Clone for ProcessEdgesWorkRootsWorkFactory<E> {
fn clone(&self) -> Self {
Self { mmtk: self.mmtk }
}
}
impl<E: ProcessEdgesWork> RootsWorkFactory<EdgeOf<E>> for ProcessEdgesWorkRootsWorkFactory<E> {
fn create_process_edge_roots_work(&mut self, edges: Vec<EdgeOf<E>>) {
crate::memory_manager::add_work_packet(
self.mmtk,
WorkBucketStage::Closure,
E::new(edges, true, self.mmtk),
);
}
fn create_process_node_roots_work(&mut self, nodes: Vec<ObjectReference>) {
assert!(
!self.mmtk.plan.constraints().moves_objects,
"Attempted to add node roots when using a plan that moves objects. Plan: {:?}",
*self.mmtk.options.plan
);
let process_edges_work = E::new(vec![], true, self.mmtk);
let work = process_edges_work.create_scan_work(nodes, true);
crate::memory_manager::add_work_packet(self.mmtk, WorkBucketStage::Closure, work);
}
}
impl<E: ProcessEdgesWork> ProcessEdgesWorkRootsWorkFactory<E> {
fn new(mmtk: &'static MMTK<E::VM>) -> Self {
Self { mmtk }
}
}
impl<VM: VMBinding> Deref for SFTProcessEdges<VM> {
type Target = ProcessEdgesBase<VM>;
#[inline]
fn deref(&self) -> &Self::Target {
&self.base
}
}
impl<VM: VMBinding> DerefMut for SFTProcessEdges<VM> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.base
}
}
pub trait ScanObjectsWork<VM: VMBinding>: GCWork<VM> + Sized {
type E: ProcessEdgesWork<VM = VM>;
fn roots(&self) -> bool;
fn post_scan_object(&self, object: ObjectReference);
fn make_another(&self, buffer: Vec<ObjectReference>) -> Self;
fn do_work_common(
&self,
buffer: &[ObjectReference],
worker: &mut GCWorker<<Self::E as ProcessEdgesWork>::VM>,
mmtk: &'static MMTK<<Self::E as ProcessEdgesWork>::VM>,
) {
let tls = worker.tls;
let scanned_root_objects = self.roots().then(|| {
let mut process_edges_work = Self::E::new(vec![], false, mmtk);
for object in buffer.iter().copied() {
let new_object = process_edges_work.trace_object(object);
debug_assert_eq!(
object, new_object,
"Object moved while tracing root unmovable root object: {} -> {}",
object, new_object
);
}
process_edges_work.nodes.take()
});
let objects_to_scan = scanned_root_objects.as_deref().unwrap_or(buffer);
let mut scan_later = vec![];
{
let mut closure = ObjectsClosure::<Self::E>::new(worker);
for object in objects_to_scan.iter().copied() {
if <VM as VMBinding>::VMScanning::support_edge_enqueuing(tls, object) {
<VM as VMBinding>::VMScanning::scan_object(tls, object, &mut closure);
self.post_scan_object(object);
} else {
scan_later.push(object);
}
}
}
if !scan_later.is_empty() {
let mut process_edges_work = Self::E::new(vec![], false, mmtk);
let mut closure = |object| process_edges_work.trace_object(object);
for object in scan_later.iter().copied() {
<VM as VMBinding>::VMScanning::scan_object_and_trace_edges(
tls,
object,
&mut closure,
);
self.post_scan_object(object);
}
if !process_edges_work.nodes.is_empty() {
let next_nodes = process_edges_work.nodes.take();
let make_packet = |nodes| {
let work_packet = self.make_another(nodes);
memory_manager::add_work_packet(mmtk, WorkBucketStage::Closure, work_packet);
};
if next_nodes.len() <= Self::E::CAPACITY {
make_packet(next_nodes);
} else {
for chunk in next_nodes.chunks(Self::E::CAPACITY) {
make_packet(chunk.into());
}
}
}
}
}
}
pub struct ScanObjects<Edges: ProcessEdgesWork> {
buffer: Vec<ObjectReference>,
#[allow(unused)]
concurrent: bool,
roots: bool,
phantom: PhantomData<Edges>,
}
impl<Edges: ProcessEdgesWork> ScanObjects<Edges> {
pub fn new(buffer: Vec<ObjectReference>, concurrent: bool, roots: bool) -> Self {
Self {
buffer,
concurrent,
roots,
phantom: PhantomData,
}
}
}
impl<VM: VMBinding, E: ProcessEdgesWork<VM = VM>> ScanObjectsWork<VM> for ScanObjects<E> {
type E = E;
fn roots(&self) -> bool {
self.roots
}
#[inline(always)]
fn post_scan_object(&self, _object: ObjectReference) {
}
fn make_another(&self, buffer: Vec<ObjectReference>) -> Self {
Self::new(buffer, self.concurrent, false)
}
}
impl<E: ProcessEdgesWork> GCWork<E::VM> for ScanObjects<E> {
fn do_work(&mut self, worker: &mut GCWorker<E::VM>, mmtk: &'static MMTK<E::VM>) {
trace!("ScanObjects");
self.do_work_common(&self.buffer, worker, mmtk);
trace!("ScanObjects End");
}
}
use crate::mmtk::MMTK;
use crate::plan::Plan;
use crate::plan::PlanTraceObject;
use crate::policy::gc_work::TraceKind;
pub struct PlanProcessEdges<
VM: VMBinding,
P: Plan<VM = VM> + PlanTraceObject<VM>,
const KIND: TraceKind,
> {
plan: &'static P,
base: ProcessEdgesBase<VM>,
}
impl<VM: VMBinding, P: PlanTraceObject<VM> + Plan<VM = VM>, const KIND: TraceKind> ProcessEdgesWork
for PlanProcessEdges<VM, P, KIND>
{
type VM = VM;
type ScanObjectsWorkType = PlanScanObjects<Self, P>;
fn new(edges: Vec<EdgeOf<Self>>, roots: bool, mmtk: &'static MMTK<VM>) -> Self {
let base = ProcessEdgesBase::new(edges, roots, mmtk);
let plan = base.plan().downcast_ref::<P>().unwrap();
Self { plan, base }
}
#[inline(always)]
fn create_scan_work(
&self,
nodes: Vec<ObjectReference>,
roots: bool,
) -> Self::ScanObjectsWorkType {
PlanScanObjects::<Self, P>::new(self.plan, nodes, false, roots)
}
#[inline(always)]
fn trace_object(&mut self, object: ObjectReference) -> ObjectReference {
if object.is_null() {
return object;
}
let worker = self.worker();
self.plan
.trace_object::<VectorObjectQueue, KIND>(&mut self.base.nodes, object, worker)
}
#[inline]
fn process_edge(&mut self, slot: EdgeOf<Self>) {
let object = slot.load();
let new_object = self.trace_object(object);
if P::may_move_objects::<KIND>() {
slot.store(new_object);
}
}
}
impl<VM: VMBinding, P: PlanTraceObject<VM> + Plan<VM = VM>, const KIND: TraceKind> Deref
for PlanProcessEdges<VM, P, KIND>
{
type Target = ProcessEdgesBase<VM>;
#[inline]
fn deref(&self) -> &Self::Target {
&self.base
}
}
impl<VM: VMBinding, P: PlanTraceObject<VM> + Plan<VM = VM>, const KIND: TraceKind> DerefMut
for PlanProcessEdges<VM, P, KIND>
{
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.base
}
}
pub struct PlanScanObjects<E: ProcessEdgesWork, P: Plan<VM = E::VM> + PlanTraceObject<E::VM>> {
plan: &'static P,
buffer: Vec<ObjectReference>,
#[allow(dead_code)]
concurrent: bool,
roots: bool,
phantom: PhantomData<E>,
}
impl<E: ProcessEdgesWork, P: Plan<VM = E::VM> + PlanTraceObject<E::VM>> PlanScanObjects<E, P> {
pub fn new(
plan: &'static P,
buffer: Vec<ObjectReference>,
concurrent: bool,
roots: bool,
) -> Self {
Self {
plan,
buffer,
concurrent,
roots,
phantom: PhantomData,
}
}
}
impl<E: ProcessEdgesWork, P: Plan<VM = E::VM> + PlanTraceObject<E::VM>> ScanObjectsWork<E::VM>
for PlanScanObjects<E, P>
{
type E = E;
fn roots(&self) -> bool {
self.roots
}
#[inline(always)]
fn post_scan_object(&self, object: ObjectReference) {
self.plan.post_scan_object(object);
}
fn make_another(&self, buffer: Vec<ObjectReference>) -> Self {
Self::new(self.plan, buffer, self.concurrent, false)
}
}
impl<E: ProcessEdgesWork, P: Plan<VM = E::VM> + PlanTraceObject<E::VM>> GCWork<E::VM>
for PlanScanObjects<E, P>
{
fn do_work(&mut self, worker: &mut GCWorker<E::VM>, mmtk: &'static MMTK<E::VM>) {
trace!("PlanScanObjects");
self.do_work_common(&self.buffer, worker, mmtk);
trace!("PlanScanObjects End");
}
}