mmtk/plan/lxr/gc_work/
mod.rs

1use super::global::LXR;
2use super::{MATURE_EVACUATION, NURSERY_EVACUATION};
3use crate::plan::tracing::UnsupportedTrace;
4use crate::plan::VectorObjectQueue;
5use crate::scheduler::gc_work::RootKind;
6use crate::scheduler::{GCWorker, WorkBucketStage};
7use crate::util::ObjectReference;
8use crate::vm::{RootsWorkFactory, VMBinding};
9use crate::{Plan, MMTK};
10use std::marker::PhantomData;
11
12pub mod mature_evac;
13pub mod mature_sweeping;
14pub mod nursery_sweeping;
15pub mod prepare;
16pub mod rc;
17pub mod tracing;
18
19use rc::{CollectNodeRoots, CollectSlotRoots};
20
21/// Common base fields shared by LXR's custom root/closure work packets.
22///
23/// This used to be `crate::scheduler::gc_work::ProcessEdgesBase`. After upstream replaced
24/// `ProcessEdgesWork` with the stateless `Trace` API, LXR keeps its own work-packet based
25/// closures, so this helper lives locally in the LXR plan.
26pub struct ProcessEdgesBase<VM: VMBinding> {
27    pub slots: Vec<VM::VMSlot>,
28    pub nodes: VectorObjectQueue,
29    mmtk: &'static MMTK<VM>,
30    // Use raw pointer for fast pointer dereferencing, instead of using `Option<&'static mut GCWorker<VM>>`.
31    // Because a copying gc will dereference this pointer at least once for every object copy.
32    worker: *mut GCWorker<VM>,
33    pub roots: bool,
34    pub root_kind: Option<RootKind>,
35    pub bucket: WorkBucketStage,
36}
37
38unsafe impl<VM: VMBinding> Send for ProcessEdgesBase<VM> {}
39
40impl<VM: VMBinding> ProcessEdgesBase<VM> {
41    pub fn new(
42        slots: Vec<VM::VMSlot>,
43        roots: bool,
44        mmtk: &'static MMTK<VM>,
45        bucket: WorkBucketStage,
46    ) -> Self {
47        #[cfg(feature = "extreme_assertions")]
48        if crate::util::slot_logger::should_check_duplicate_slots(mmtk.get_plan()) {
49            for slot in &slots {
50                // log slot, panic if already logged
51                mmtk.slot_logger.log_slot(*slot);
52            }
53        }
54        Self {
55            slots,
56            nodes: VectorObjectQueue::new(),
57            mmtk,
58            worker: std::ptr::null_mut(),
59            roots,
60            root_kind: if roots { Some(RootKind::Strong) } else { None },
61            bucket,
62        }
63    }
64
65    pub fn set_worker(&mut self, worker: &mut GCWorker<VM>) {
66        self.worker = worker;
67    }
68
69    pub fn worker(&self) -> &'static mut GCWorker<VM> {
70        unsafe { &mut *self.worker }
71    }
72
73    pub fn mmtk(&self) -> &'static MMTK<VM> {
74        self.mmtk
75    }
76
77    pub fn plan(&self) -> &'static dyn Plan<VM = VM> {
78        self.mmtk.get_plan()
79    }
80}
81
82/// The [`crate::scheduler::GCWorkContext`] for LXR.
83///
84/// LXR does not use the generic `Trace`-based closures. Instead it schedules its own custom work
85/// packets. The `DefaultTrace`/`PinningTrace` members are therefore set to [`UnsupportedTrace`],
86/// and root scanning is routed through [`LXRRootsWorkFactory`].
87pub struct LXRGCWorkContext<VM: VMBinding>(PhantomData<VM>);
88
89impl<VM: VMBinding> crate::scheduler::GCWorkContext for LXRGCWorkContext<VM> {
90    type VM = VM;
91    type PlanType = LXR<VM>;
92    type DefaultTrace = UnsupportedTrace<VM>;
93    type PinningTrace = UnsupportedTrace<VM>;
94
95    fn make_roots_work_factory(
96        mmtk: &'static MMTK<VM>,
97    ) -> impl RootsWorkFactory<<VM as VMBinding>::VMSlot> {
98        LXRRootsWorkFactory::new(mmtk)
99    }
100}
101
102/// The [`RootsWorkFactory`] used by LXR.
103///
104/// Roots reported as slots are reference-counted through [`CollectSlotRoots`] (which spawns
105/// `ProcessIncs`) in `RCProcessIncs`; roots reported as objects go through
106/// [`CollectNodeRoots`] one stage earlier, in `RCProcessIncsNonMoving`, so that nothing can move
107/// them. See the two methods below.
108pub struct LXRRootsWorkFactory<VM: VMBinding> {
109    mmtk: &'static MMTK<VM>,
110}
111
112impl<VM: VMBinding> Clone for LXRRootsWorkFactory<VM> {
113    fn clone(&self) -> Self {
114        Self { mmtk: self.mmtk }
115    }
116}
117
118impl<VM: VMBinding> LXRRootsWorkFactory<VM> {
119    fn new(mmtk: &'static MMTK<VM>) -> Self {
120        Self { mmtk }
121    }
122}
123
124impl<VM: VMBinding> RootsWorkFactory<VM::VMSlot> for LXRRootsWorkFactory<VM> {
125    fn create_process_roots_work_with_root_kind(&mut self, slots: Vec<VM::VMSlot>, kind: RootKind) {
126        // Slot roots may be evacuated, so they run in `RCProcessIncs`, one stage after the
127        // `root_scanning_stage` that discovered them. See `WorkBucketStage::RCProcessIncsNonMoving`.
128        let stage = WorkBucketStage::RCProcessIncs;
129        let mut w = CollectSlotRoots::new(slots, true, self.mmtk, stage);
130        w.root_kind = Some(kind);
131        crate::memory_manager::add_work_packet(self.mmtk, stage, w);
132    }
133
134    fn create_process_pinning_roots_work(&mut self, nodes: Vec<ObjectReference>) {
135        if nodes.is_empty() {
136            return;
137        }
138        crate::memory_manager::add_work_packet(
139            self.mmtk,
140            WorkBucketStage::RCProcessIncsNonMoving,
141            CollectNodeRoots::<VM>::new(nodes),
142        );
143    }
144
145    fn create_process_tpinning_roots_work(&mut self, nodes: Vec<ObjectReference>) {
146        // Transitive pinning is not supported.
147        // Not sure if we can support it for LXR, as RC collections have no notion of transitive closure.
148        if NURSERY_EVACUATION || MATURE_EVACUATION {
149            unimplemented!(
150                "LXR does not support transitive pinning roots unless evacuation is compiled out"
151            );
152        }
153        self.create_process_pinning_roots_work(nodes);
154    }
155}