mmtk/plan/lxr/gc_work/
mod.rs

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