mmtk/plan/generational/
gc_work.rs

1use atomic::Ordering;
2
3use crate::plan::PlanTraceObject;
4use crate::plan::VectorObjectQueue;
5use crate::policy::gc_work::TraceKind;
6use crate::scheduler::{gc_work::*, GCWork, GCWorker, WorkBucketStage};
7use crate::util::ObjectReference;
8use crate::vm::slot::{MemorySlice, Slot};
9use crate::vm::*;
10use crate::MMTK;
11use std::marker::PhantomData;
12use std::ops::{Deref, DerefMut};
13
14use super::global::GenerationalPlanExt;
15
16/// Process edges for a nursery GC. This type is provided if a generational plan does not use
17/// [`crate::scheduler::gc_work::SFTProcessEdges`]. If a plan uses `SFTProcessEdges`,
18/// it does not need to use this type.
19pub struct GenNurseryProcessEdges<
20    VM: VMBinding,
21    P: GenerationalPlanExt<VM> + PlanTraceObject<VM>,
22    const KIND: TraceKind,
23> {
24    plan: &'static P,
25    base: ProcessEdgesBase<VM>,
26}
27
28impl<VM: VMBinding, P: GenerationalPlanExt<VM> + PlanTraceObject<VM>, const KIND: TraceKind>
29    ProcessEdgesWork for GenNurseryProcessEdges<VM, P, KIND>
30{
31    type VM = VM;
32    type ScanObjectsWorkType = PlanScanObjects<Self, P>;
33
34    fn new(
35        slots: Vec<SlotOf<Self>>,
36        roots: bool,
37        mmtk: &'static MMTK<VM>,
38        bucket: WorkBucketStage,
39    ) -> Self {
40        let base = ProcessEdgesBase::new(slots, roots, mmtk, bucket);
41        let plan = base.plan().downcast_ref().unwrap();
42        Self { plan, base }
43    }
44
45    fn trace_object(&mut self, object: ObjectReference) -> ObjectReference {
46        // We cannot borrow `self` twice in a call, so we extract `worker` as a local variable.
47        let worker = self.worker();
48        self.plan.trace_object_nursery::<VectorObjectQueue, KIND>(
49            &mut self.base.nodes,
50            object,
51            worker,
52        )
53    }
54
55    fn process_slot(&mut self, slot: SlotOf<Self>) {
56        let Some(object) = slot.load() else {
57            // Skip slots that are not holding an object reference.
58            return;
59        };
60        let new_object = self.trace_object(object);
61        debug_assert!(!self.plan.is_object_in_nursery(new_object));
62        // Note: If `object` is a mature object, `trace_object` will not call `space.trace_object`,
63        // but will still return `object`.  In that case, we don't need to write it back.
64        if new_object != object {
65            slot.store(new_object);
66        }
67    }
68
69    fn create_scan_work(&self, nodes: Vec<ObjectReference>) -> Self::ScanObjectsWorkType {
70        PlanScanObjects::new(self.plan, nodes, false, self.bucket)
71    }
72}
73
74impl<VM: VMBinding, P: GenerationalPlanExt<VM> + PlanTraceObject<VM>, const KIND: TraceKind> Deref
75    for GenNurseryProcessEdges<VM, P, KIND>
76{
77    type Target = ProcessEdgesBase<VM>;
78    fn deref(&self) -> &Self::Target {
79        &self.base
80    }
81}
82
83impl<VM: VMBinding, P: GenerationalPlanExt<VM> + PlanTraceObject<VM>, const KIND: TraceKind>
84    DerefMut for GenNurseryProcessEdges<VM, P, KIND>
85{
86    fn deref_mut(&mut self) -> &mut Self::Target {
87        &mut self.base
88    }
89}
90
91/// The modbuf contains a list of objects in mature space(s) that
92/// may contain pointers to the nursery space.
93/// This work packet scans the recorded objects and forwards pointers if necessary.
94pub struct ProcessModBuf<E: ProcessEdgesWork> {
95    modbuf: Vec<ObjectReference>,
96    phantom: PhantomData<E>,
97}
98
99impl<E: ProcessEdgesWork> ProcessModBuf<E> {
100    pub fn new(modbuf: Vec<ObjectReference>) -> Self {
101        debug_assert!(!modbuf.is_empty());
102        Self {
103            modbuf,
104            phantom: PhantomData,
105        }
106    }
107}
108
109impl<E: ProcessEdgesWork> GCWork<E::VM> for ProcessModBuf<E> {
110    fn do_work(&mut self, worker: &mut GCWorker<E::VM>, mmtk: &'static MMTK<E::VM>) {
111        // Process and scan modbuf only if the current GC is a nursery GC
112        let gen = mmtk.get_plan().generational().unwrap();
113        if gen.is_current_gc_nursery() {
114            // Flip the per-object unlogged bits to "unlogged" state.
115            for obj in &self.modbuf {
116                debug_assert!(
117                    !gen.is_object_in_nursery(*obj),
118                    "{} was logged but is not mature. Dumping process memory maps:\n{}",
119                    *obj,
120                    crate::util::memory::get_process_memory_maps(),
121                );
122                <E::VM as VMBinding>::VMObjectModel::GLOBAL_LOG_BIT_SPEC.store_atomic::<E::VM, u8>(
123                    *obj,
124                    1,
125                    None,
126                    Ordering::SeqCst,
127                );
128            }
129            // Scan objects in the modbuf and forward pointers
130            let modbuf = std::mem::take(&mut self.modbuf);
131            GCWork::do_work(
132                &mut ScanObjects::<E>::new(modbuf, false, WorkBucketStage::Closure),
133                worker,
134                mmtk,
135            )
136        }
137    }
138}
139
140/// The array-copy modbuf contains a list of array slices in mature space(s) that
141/// may contain pointers to the nursery space.
142/// This work packet forwards and updates each entry in the recorded slices.
143pub struct ProcessRegionModBuf<E: ProcessEdgesWork> {
144    /// A list of `(start_address, bytes)` tuple.
145    modbuf: Vec<<E::VM as VMBinding>::VMMemorySlice>,
146    phantom: PhantomData<E>,
147}
148
149impl<E: ProcessEdgesWork> ProcessRegionModBuf<E> {
150    pub fn new(modbuf: Vec<<E::VM as VMBinding>::VMMemorySlice>) -> Self {
151        Self {
152            modbuf,
153            phantom: PhantomData,
154        }
155    }
156}
157
158impl<E: ProcessEdgesWork> GCWork<E::VM> for ProcessRegionModBuf<E> {
159    fn do_work(&mut self, worker: &mut GCWorker<E::VM>, mmtk: &'static MMTK<E::VM>) {
160        // Scan modbuf only if the current GC is a nursery GC
161        if mmtk
162            .get_plan()
163            .generational()
164            .unwrap()
165            .is_current_gc_nursery()
166        {
167            // Collect all the entries in all the slices
168            let mut slots = vec![];
169            for slice in &self.modbuf {
170                for slot in slice.iter_slots() {
171                    slots.push(slot);
172                }
173            }
174            // Forward entries
175            GCWork::do_work(
176                &mut E::new(slots, false, mmtk, WorkBucketStage::Closure),
177                worker,
178                mmtk,
179            )
180        }
181    }
182}