1use super::super::LXR;
2use super::ProcessEdgesBase;
3use crate::plan::concurrent::Pause;
4use crate::plan::VectorQueue;
5use crate::policy::immix::block::Block;
6use crate::policy::space::Space;
7use crate::scheduler::RootKind;
8use crate::util::copy::CopySemantics;
9use crate::util::linear_scan::UnstraddlableRegion;
10use crate::util::rc::RefCountHelper;
11use crate::util::{ObjectReference, VMThread};
12use crate::vm::slot::Slot;
13use crate::{
14 plan::ObjectQueue,
15 scheduler::{GCWork, GCWorker, WorkBucketStage},
16 vm::*,
17 MMTK,
18};
19use atomic::Ordering;
20use std::ops::{Deref, DerefMut};
21use std::sync::Arc;
22
23pub struct LXRConcurrentTraceObjects<VM: VMBinding> {
24 plan: &'static LXR<VM>,
25 objects: Option<Vec<ObjectReference>>,
27 objects_arc: Option<Arc<Vec<ObjectReference>>>,
28 next_objects: VectorQueue<ObjectReference>,
30 rc: RefCountHelper<VM>,
31 worker: *mut GCWorker<VM>,
32}
33
34impl<VM: VMBinding> LXRConcurrentTraceObjects<VM> {
35 const SATB_BUFFER_SIZE: usize = 8192;
36
37 pub fn new(objects: Vec<ObjectReference>, mmtk: &'static MMTK<VM>) -> Self {
38 let plan = mmtk.get_plan().downcast_ref::<LXR<VM>>().unwrap();
39 super::super::NUM_CONCURRENT_TRACING_PACKETS.fetch_add(1, Ordering::SeqCst);
40 Self {
41 plan,
42 objects: Some(objects),
43 objects_arc: None,
44 next_objects: VectorQueue::default(),
45 rc: RefCountHelper::NEW,
46 worker: std::ptr::null_mut(),
47 }
48 }
49
50 pub fn new_arc(objects: Arc<Vec<ObjectReference>>, mmtk: &'static MMTK<VM>) -> Self {
51 let plan = mmtk.get_plan().downcast_ref::<LXR<VM>>().unwrap();
52 super::super::NUM_CONCURRENT_TRACING_PACKETS.fetch_add(1, Ordering::SeqCst);
53 Self {
54 plan,
55 objects: None,
56 objects_arc: Some(objects),
57 next_objects: VectorQueue::default(),
58 rc: RefCountHelper::NEW,
59 worker: std::ptr::null_mut(),
60 }
61 }
62
63 #[cold]
64 fn flush(&mut self) {
65 if !self.next_objects.is_empty() {
66 let objects = self.next_objects.take();
67 let worker = unsafe { &mut *self.worker };
68 debug_assert!(self.plan.cm_enabled());
69 let w = Self::new(objects, worker.mmtk);
70 worker.add_work(WorkBucketStage::ConcurrentResumable, w);
71 }
72 }
73
74 fn trace_object(&mut self, object: ObjectReference) -> ObjectReference {
75 if self.rc.count(object) == 0 {
76 return object;
77 }
78 if self.plan.immix_space.in_space(object) {
79 self.plan
80 .immix_space
81 .trace_object_without_moving_rc(self, object);
82 } else {
83 self.plan.los().trace_object(self, object);
84 }
85 object
86 }
87
88 fn trace_objects(&mut self, objects: &[ObjectReference]) {
89 for o in objects {
90 self.trace_object(*o);
91 }
92 }
93
94 fn scan_and_enqueue<const CHECK_REMSET: bool>(&mut self, object: ObjectReference) {
95 object.iterate_fields::<VM, _>(unsafe { (*self.worker).tls }.0, |s| {
96 let Some(t) = s.load() else {
97 return;
98 };
99 if super::super::MATURE_EVACUATION && CHECK_REMSET && self.plan.in_defrag(t) {
100 self.plan.mature_evac_remset.record(s, t, self.plan);
101 }
102 self.next_objects.push(t);
103 if self.next_objects.len() > Self::SATB_BUFFER_SIZE {
104 self.flush();
105 }
106 });
107 }
108}
109
110impl<VM: VMBinding> ObjectQueue for LXRConcurrentTraceObjects<VM> {
111 fn enqueue(&mut self, object: ObjectReference) {
112 if cfg!(feature = "sanity") {
113 assert!(
114 object.to_raw_address().is_mapped(),
115 "Invalid obj {:?}: address is not mapped",
116 object
117 );
118 }
119 let should_check_remset = !self.plan.in_defrag(object);
120 if should_check_remset {
121 self.scan_and_enqueue::<true>(object)
122 } else {
123 self.scan_and_enqueue::<false>(object)
124 }
125 }
126}
127
128unsafe impl<VM: VMBinding> Send for LXRConcurrentTraceObjects<VM> {}
129
130impl<VM: VMBinding> GCWork<VM> for LXRConcurrentTraceObjects<VM> {
131 fn do_work(&mut self, worker: &mut GCWorker<VM>, mmtk: &'static MMTK<VM>) {
132 self.worker = worker;
133 debug_assert!(!mmtk.scheduler.work_buckets[WorkBucketStage::RCProcessIncs].is_open());
134 if let Some(objects) = self.objects.take() {
136 self.trace_objects(&objects)
137 } else if let Some(objects) = self.objects_arc.take() {
138 self.trace_objects(&objects)
139 }
140 let pause_opt = self.plan.current_pause();
141 if pause_opt == Some(Pause::FinalMark) || pause_opt.is_none() {
142 let mut next_objects = vec![];
143 while !self.next_objects.is_empty() {
144 let pause_opt = self.plan.current_pause();
145 if !(pause_opt == Some(Pause::FinalMark) || pause_opt.is_none()) {
146 break;
147 }
148 next_objects.clear();
149 self.next_objects.swap(&mut next_objects);
150 self.trace_objects(&next_objects);
151 }
152 }
153 self.flush();
154 super::super::NUM_CONCURRENT_TRACING_PACKETS.fetch_sub(1, Ordering::SeqCst);
156 debug_assert!(!mmtk.scheduler.work_buckets[WorkBucketStage::RCProcessIncs].is_open());
157 }
158}
159
160pub struct ProcessModBufSATB {
161 nodes: Option<Vec<ObjectReference>>,
162 nodes_arc: Option<Arc<Vec<ObjectReference>>>,
163}
164
165impl ProcessModBufSATB {
166 pub fn new(nodes: Vec<ObjectReference>) -> Self {
167 Self {
169 nodes: Some(nodes),
170 nodes_arc: None,
171 }
172 }
173 pub fn new_arc(nodes: Arc<Vec<ObjectReference>>) -> Self {
174 Self {
176 nodes: None,
177 nodes_arc: Some(nodes),
178 }
179 }
180}
181
182impl<VM: VMBinding> GCWork<VM> for ProcessModBufSATB {
183 fn do_work(&mut self, worker: &mut GCWorker<VM>, mmtk: &'static MMTK<VM>) {
184 let mut w = if let Some(nodes) = self.nodes.take() {
185 if nodes.is_empty() {
186 return;
187 }
188 if cfg!(any(feature = "sanity", debug_assertions)) {
189 for o in &nodes {
190 assert!(
191 o.to_raw_address().is_mapped(),
192 "Invalid object {:?}: address is not mapped",
193 o
194 );
195 }
196 }
197 LXRConcurrentTraceObjects::new(nodes, mmtk)
198 } else if let Some(nodes) = self.nodes_arc.take() {
199 if nodes.is_empty() {
200 return;
201 }
202 if cfg!(any(feature = "sanity", debug_assertions)) {
203 for o in &*nodes {
204 assert!(
205 o.to_raw_address().is_mapped(),
206 "Invalid object {:?}: address is not mapped",
207 o
208 );
209 }
210 }
211 LXRConcurrentTraceObjects::new_arc(nodes, mmtk)
212 } else {
213 return;
214 };
215
216 let current_pause = mmtk
217 .get_plan()
218 .downcast_ref::<LXR<VM>>()
219 .unwrap()
220 .current_pause();
221 if current_pause != Some(Pause::FinalMark) {
222 worker.scheduler().work_buckets[WorkBucketStage::ConcurrentResumable].add(w);
223 } else {
224 GCWork::do_work(&mut w, worker, mmtk);
225 }
226 }
227}
228
229pub struct LXRStopTheWorldProcessEdges<VM: VMBinding, const FULL_GC: bool> {
230 lxr: &'static LXR<VM>,
231 pause: Pause,
232 base: ProcessEdgesBase<VM>,
233 forwarded_roots: Vec<ObjectReference>,
234 next_slots: VectorQueue<VM::VMSlot>,
235 next_slot_count: u32,
236 remset_recorded_slots: bool,
237 should_record_forwarded_roots: bool,
238}
239
240impl<VM: VMBinding, const FULL_GC: bool> LXRStopTheWorldProcessEdges<VM, FULL_GC> {
241 const OVERWRITE_REFERENCE: bool = super::super::MATURE_EVACUATION;
242
243 pub fn new_remset(slots: Vec<VM::VMSlot>, mmtk: &'static MMTK<VM>) -> Self {
244 let mut me = Self::new(slots, false, mmtk, WorkBucketStage::Closure);
245 me.remset_recorded_slots = true;
246 me
247 }
248
249 pub fn new(
250 slots: Vec<VM::VMSlot>,
251 roots: bool,
252 mmtk: &'static MMTK<VM>,
253 bucket: WorkBucketStage,
254 ) -> Self {
255 let base = ProcessEdgesBase::new(slots, roots, mmtk, bucket);
256 let lxr = base.plan().downcast_ref::<LXR<VM>>().unwrap();
257 Self {
258 lxr,
259 base,
260 pause: Pause::RefCount,
261 forwarded_roots: vec![],
262 next_slots: VectorQueue::new(),
263 next_slot_count: 0,
264 remset_recorded_slots: false,
265 should_record_forwarded_roots: false,
266 }
267 }
268
269 #[cold]
270 fn flush(&mut self) {
271 if !self.next_slots.is_empty() {
272 let slots = self.next_slots.take();
273 let w = Self::new(slots, false, self.mmtk(), self.bucket);
274 self.worker()
275 .add_boxed_work(WorkBucketStage::Unconstrained, Box::new(w));
276 }
277 assert!(self.nodes.is_empty());
278 self.next_slot_count = 0;
279 }
280
281 fn process_slots(&mut self) {
282 self.should_record_forwarded_roots = self.roots
283 && !self
284 .root_kind
285 .map(|r| r.should_skip_decs())
286 .unwrap_or_default();
287 self.pause = self.lxr.current_pause().unwrap();
288 if self.should_record_forwarded_roots {
289 self.forwarded_roots.reserve(self.slots.len());
290 }
291 let slots = std::mem::take(&mut self.slots);
292 if self.roots && self.root_kind == Some(RootKind::Weak) {
293 self.process_slots_impl::<true, false>(&slots);
294 } else if self.remset_recorded_slots {
295 self.process_slots_impl::<false, true>(&slots);
296 } else {
297 self.process_slots_impl::<false, false>(&slots);
298 }
299 self.roots = false;
300 self.remset_recorded_slots = false;
301 let should_record_forwarded_roots = self.should_record_forwarded_roots;
302 self.should_record_forwarded_roots = false;
303 let mut slots = vec![];
304 while !self.next_slots.is_empty() {
305 self.next_slot_count = 0;
306 slots.clear();
307 self.next_slots.swap(&mut slots);
308 self.process_slots_impl::<false, false>(&slots);
309 }
310 self.flush();
311 if should_record_forwarded_roots {
312 let roots = std::mem::take(&mut self.forwarded_roots);
313 self.lxr.curr_roots.read().unwrap().push(roots);
314 }
315 }
316}
317
318impl<VM: VMBinding, const FULL_GC: bool> GCWork<VM> for LXRStopTheWorldProcessEdges<VM, FULL_GC> {
319 fn do_work(&mut self, worker: &mut GCWorker<VM>, _mmtk: &'static MMTK<VM>) {
320 self.set_worker(worker);
321 self.process_slots();
322 if !self.nodes.is_empty() {
323 self.flush();
324 }
325 }
326}
327
328impl<VM: VMBinding, const FULL_GC: bool> LXRStopTheWorldProcessEdges<VM, FULL_GC> {
329 #[inline]
330 fn full_gc_trace_object<const WEAK_ROOT: bool>(
331 &mut self,
332 object: ObjectReference,
333 ) -> ObjectReference {
334 debug_assert!(FULL_GC);
335 debug_assert!(object.is_in_any_space());
336 debug_assert!(object.to_raw_address().is_aligned_to(8));
337 if WEAK_ROOT && !Block::containing(object).is_defrag_source() {
339 return object;
340 }
341 let x = if self.lxr.immix_space.in_space(object) {
342 let pause = self.pause;
343 let worker = self.worker();
344 self.lxr.immix_space.rc_trace_object(
345 self,
346 object,
347 CopySemantics::DefaultCopy,
348 pause,
349 true,
350 worker,
351 )
352 } else {
353 self.lxr.los().trace_object(self, object)
354 };
355 if self.should_record_forwarded_roots {
356 self.forwarded_roots.push(x)
357 }
358 x
359 }
360
361 #[inline]
362 fn mature_evac_trace_object<const WEAK_ROOT: bool, const REMSET: bool>(
363 &mut self,
364 object: ObjectReference,
365 ) -> ObjectReference {
366 debug_assert!(!FULL_GC);
367 if REMSET && (!object.is_in_any_space() || !object.to_raw_address().is_aligned_to(8)) {
370 return object;
371 }
372 if self.lxr.rc.count(object) == 0 {
373 return object;
374 }
375 if WEAK_ROOT && !Block::containing(object).is_defrag_source() {
376 return object;
377 }
378 debug_assert!(object.is_in_any_space(), "Invalid {:?}", object);
379 debug_assert!(
380 object.to_raw_address().is_aligned_to(8),
381 "Invalid {:?} remset={}",
382 object,
383 self.remset_recorded_slots
384 );
385 let object = object.get_forwarded_object().unwrap_or(object);
386 let new_object = if self.lxr.immix_space.in_space(object) {
387 if self.lxr.rc.object_is_in_straddle_line(object) {
388 return object;
389 }
390 let pause = self.pause;
391 let worker = self.worker();
392 self.lxr.immix_space.rc_trace_object(
393 self,
394 object,
395 CopySemantics::DefaultCopy,
396 pause,
397 true,
398 worker,
399 )
400 } else {
401 self.lxr.los().trace_object(self, object)
402 };
403 if self.should_record_forwarded_roots {
404 self.forwarded_roots.push(new_object)
405 }
406 new_object
407 }
408
409 #[inline]
410 fn __process_slot<const WEAK_ROOT: bool, const REMSET: bool>(&mut self, slot: VM::VMSlot) {
411 let Some(object) = slot.load() else {
412 return;
413 };
414 let new_object = if !FULL_GC {
415 self.mature_evac_trace_object::<WEAK_ROOT, REMSET>(object)
416 } else {
417 self.full_gc_trace_object::<WEAK_ROOT>(object)
418 };
419 if Self::OVERWRITE_REFERENCE && new_object != object {
420 slot.store(new_object);
421 }
422 }
423
424 fn process_slots_impl<const WEAK_ROOT: bool, const REMSET: bool>(
425 &mut self,
426 slots: &[VM::VMSlot],
427 ) {
428 for s in slots {
429 self.__process_slot::<WEAK_ROOT, REMSET>(*s);
430 }
431 }
432}
433
434impl<VM: VMBinding, const FULL_GC: bool> ObjectQueue for LXRStopTheWorldProcessEdges<VM, FULL_GC> {
435 fn enqueue(&mut self, object: ObjectReference) {
436 let limit: usize = if FULL_GC { 8192 } else { 1024 };
437 object.iterate_fields::<VM, _>(VMThread::UNINITIALIZED, |s| {
439 let Some(o) = s.load() else {
440 return;
441 };
442 if self.lxr.is_marked(o) && !self.lxr.in_defrag(o) {
443 return;
444 }
445 self.next_slots.push(s);
446 self.next_slot_count += 1;
447 if self.next_slot_count as usize >= limit {
448 self.flush();
449 }
450 });
451 }
452}
453
454impl<VM: VMBinding, const FULL_GC: bool> Deref for LXRStopTheWorldProcessEdges<VM, FULL_GC> {
455 type Target = ProcessEdgesBase<VM>;
456 fn deref(&self) -> &Self::Target {
457 &self.base
458 }
459}
460
461impl<VM: VMBinding, const FULL_GC: bool> DerefMut for LXRStopTheWorldProcessEdges<VM, FULL_GC> {
462 fn deref_mut(&mut self) -> &mut Self::Target {
463 &mut self.base
464 }
465}