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