1use crate::plan::concurrent::global::ConcurrentPlan;
2use crate::plan::concurrent::Pause;
3use crate::plan::tracing::{PlanTrace, Trace};
4use crate::plan::PlanTraceObject;
5use crate::policy::gc_work::TraceKind;
6use crate::scheduler::{gc_work::RootKind, GCWork, GCWorker, WorkBucketStage};
7use crate::util::{scanning_helper, ObjectReference};
8use crate::vm::slot::Slot;
9use crate::vm::{RootsKind, RootsWorkFactory, VMBinding};
10use crate::MMTK;
11
12use std::collections::VecDeque;
13use std::marker::PhantomData;
14
15pub struct ConcurrentTraceObjects<
16 VM: VMBinding,
17 P: ConcurrentPlan<VM = VM> + PlanTraceObject<VM>,
18 const KIND: TraceKind,
19> {
20 initial_objects: Vec<ObjectReference>,
22 already_marked: bool,
24 phantom_data: PhantomData<(VM, P)>,
25}
26
27impl<VM: VMBinding, P: ConcurrentPlan<VM = VM> + PlanTraceObject<VM>, const KIND: TraceKind>
28 ConcurrentTraceObjects<VM, P, KIND>
29{
30 const SATB_BUFFER_SIZE: usize = 8192;
31 const CONCURRENT_TRACE_OVERFLOW: usize = Self::SATB_BUFFER_SIZE * 2;
32
33 pub fn new(initial_objects: Vec<ObjectReference>, already_marked: bool) -> Self {
34 Self {
35 initial_objects,
36 already_marked,
37 phantom_data: PhantomData,
38 }
39 }
40}
41
42unsafe impl<VM: VMBinding, P: ConcurrentPlan<VM = VM> + PlanTraceObject<VM>, const KIND: TraceKind>
43 Send for ConcurrentTraceObjects<VM, P, KIND>
44{
45}
46
47impl<VM: VMBinding, P: ConcurrentPlan<VM = VM> + PlanTraceObject<VM>, const KIND: TraceKind>
48 GCWork<VM> for ConcurrentTraceObjects<VM, P, KIND>
49{
50 fn do_work(&mut self, worker: &mut GCWorker<VM>, mmtk: &'static MMTK<VM>) {
51 let tls = worker.tls;
52 let trace = PlanTrace::<P, KIND>::from_mmtk(mmtk);
53
54 let initial_objects = std::mem::take(&mut self.initial_objects);
56 let num_initial_objects = initial_objects.len();
57 let mut num_queued_objects = 0;
58
59 let mut queue = VecDeque::new();
61 if self.already_marked {
62 queue.extend(initial_objects);
64 } else {
65 for object in initial_objects {
67 trace.trace_object(worker, object, &mut |enqueued_object| {
68 debug_assert_eq!(enqueued_object, object);
69 queue.push_back(enqueued_object);
70 num_queued_objects += 1;
71 });
72 }
73 }
74
75 while let Some(object) = queue.pop_back() {
77 scanning_helper::visit_children_non_moving::<VM>(tls, object, &mut |child| {
78 trace.trace_object(worker, child, &mut |enqueued_child| {
79 debug_assert_eq!(enqueued_child, child);
80 queue.push_back(enqueued_child);
81 num_queued_objects += 1;
82 })
83 });
84 trace.post_scan_object(object);
85
86 if queue.len() >= Self::CONCURRENT_TRACE_OVERFLOW {
87 let offloaded_objects = queue.drain(..Self::SATB_BUFFER_SIZE).collect();
88 let w = Self::new(offloaded_objects, true);
89 let stage = if mmtk.get_plan().concurrent().unwrap().current_pause()
93 == Some(Pause::FinalMark)
94 {
95 WorkBucketStage::FinishConcurrentWork
96 } else {
97 WorkBucketStage::Concurrent
98 };
99 worker.add_work(stage, w);
100 }
101 }
102
103 probe!(
104 mmtk,
105 concurrent_trace_objects,
106 num_initial_objects,
107 num_queued_objects
108 );
109 }
110}
111
112pub struct ProcessModBufSATB<
113 VM: VMBinding,
114 P: ConcurrentPlan<VM = VM> + PlanTraceObject<VM>,
115 const KIND: TraceKind,
116> {
117 nodes: Option<Vec<ObjectReference>>,
118 _p: std::marker::PhantomData<(VM, P)>,
119}
120
121unsafe impl<VM: VMBinding, P: ConcurrentPlan<VM = VM> + PlanTraceObject<VM>, const KIND: TraceKind>
122 Send for ProcessModBufSATB<VM, P, KIND>
123{
124}
125
126impl<VM: VMBinding, P: ConcurrentPlan<VM = VM> + PlanTraceObject<VM>, const KIND: TraceKind>
127 ProcessModBufSATB<VM, P, KIND>
128{
129 pub fn new(nodes: Vec<ObjectReference>) -> Self {
130 Self {
131 nodes: Some(nodes),
132 _p: std::marker::PhantomData,
133 }
134 }
135}
136
137impl<VM: VMBinding, P: ConcurrentPlan<VM = VM> + PlanTraceObject<VM>, const KIND: TraceKind>
138 GCWork<VM> for ProcessModBufSATB<VM, P, KIND>
139{
140 fn do_work(&mut self, worker: &mut GCWorker<VM>, mmtk: &'static MMTK<VM>) {
141 let mut w = if let Some(nodes) = self.nodes.take() {
142 if nodes.is_empty() {
143 return;
144 }
145
146 ConcurrentTraceObjects::<VM, P, KIND>::new(
147 nodes, false, )
149 } else {
150 return;
151 };
152 GCWork::do_work(&mut w, worker, mmtk);
153 }
154}
155
156pub(crate) struct ConcurrentMarkingRootsWorkFactory<
161 VM: VMBinding,
162 P: ConcurrentPlan<VM = VM> + PlanTraceObject<VM>,
163 const KIND: TraceKind,
164> {
165 pub(crate) mmtk: &'static MMTK<VM>,
166 phantom_data: PhantomData<P>,
167}
168
169impl<VM: VMBinding, P: ConcurrentPlan<VM = VM> + PlanTraceObject<VM>, const KIND: TraceKind> Clone
170 for ConcurrentMarkingRootsWorkFactory<VM, P, KIND>
171{
172 fn clone(&self) -> Self {
173 Self {
174 mmtk: self.mmtk,
175 phantom_data: PhantomData,
176 }
177 }
178}
179
180unsafe impl<VM: VMBinding, P: ConcurrentPlan<VM = VM> + PlanTraceObject<VM>, const KIND: TraceKind>
181 Send for ConcurrentMarkingRootsWorkFactory<VM, P, KIND>
182{
183}
184
185impl<VM: VMBinding, P: ConcurrentPlan<VM = VM> + PlanTraceObject<VM>, const KIND: TraceKind>
186 ConcurrentMarkingRootsWorkFactory<VM, P, KIND>
187{
188 pub(crate) fn new(mmtk: &'static MMTK<VM>) -> Self {
189 Self {
190 mmtk,
191 phantom_data: PhantomData,
192 }
193 }
194
195 fn debug_assert_initial_mark(&self) {
196 let pause = self.mmtk.get_plan().concurrent().unwrap().current_pause();
197
198 debug_assert_eq!(
199 pause,
200 Some(Pause::InitialMark),
201 "Concurrent marking only scans roots during InitialMark."
202 );
203 }
204
205 fn create_and_schedule_root_nodes_work(&mut self, nodes: Vec<ObjectReference>) {
206 let mmtk = self.mmtk;
207 let work_packet = ConcurrentTraceObjects::<VM, P, KIND>::new(nodes, false);
208 mmtk.scheduler.work_buckets[WorkBucketStage::Concurrent].add_no_notify(work_packet);
209 }
210}
211
212impl<VM: VMBinding, P: ConcurrentPlan<VM = VM> + PlanTraceObject<VM>, const KIND: TraceKind>
213 RootsWorkFactory<VM::VMSlot> for ConcurrentMarkingRootsWorkFactory<VM, P, KIND>
214{
215 fn create_process_roots_work_with_root_kind(
216 &mut self,
217 slots: Vec<VM::VMSlot>,
218 _kind: RootKind,
219 ) {
220 probe!(mmtk, roots, RootsKind::NORMAL, slots.len());
221
222 self.debug_assert_initial_mark();
223
224 let nodes = slots
229 .iter()
230 .flat_map(|slot| slot.load())
231 .collect::<Vec<_>>();
232
233 #[cfg(feature = "sanity")]
236 self.mmtk
237 .sanity_checker
238 .lock()
239 .unwrap()
240 .add_root_nodes(nodes.clone());
241
242 self.create_and_schedule_root_nodes_work(nodes);
243 }
244
245 #[cfg(feature = "non_moving_root")]
246 fn create_process_pinning_roots_work(&mut self, nodes: Vec<ObjectReference>) {
247 probe!(mmtk, roots, RootsKind::PINNING, nodes.len());
248
249 self.debug_assert_initial_mark();
250
251 #[cfg(feature = "sanity")]
252 self.mmtk
253 .sanity_checker
254 .lock()
255 .unwrap()
256 .add_root_nodes(nodes.clone());
257
258 self.create_and_schedule_root_nodes_work(nodes);
259 }
260
261 #[cfg(feature = "non_moving_root")]
262 fn create_process_tpinning_roots_work(&mut self, nodes: Vec<ObjectReference>) {
263 probe!(mmtk, roots, RootsKind::TPINNING, nodes.len());
264
265 self.debug_assert_initial_mark();
266
267 #[cfg(feature = "sanity")]
268 self.mmtk
269 .sanity_checker
270 .lock()
271 .unwrap()
272 .add_root_nodes(nodes.clone());
273
274 self.create_and_schedule_root_nodes_work(nodes);
275 }
276}