1use super::super::LazySweepingJobsCounter;
2use super::super::SurvivalRatioPredictorLocal;
3use super::super::LXR;
4use super::super::{LAZY_DECREMENTS, MATURE_EVACUATION, NO_EVAC, NURSERY_EVACUATION};
5use super::tracing::LXRConcurrentTraceObjects;
6use super::tracing::LXRStopTheWorldProcessEdges;
7use super::tracing::LXRStopTheWorldProcessNodes;
8use super::tracing::ProcessModBufSATB;
9use super::ProcessEdgesBase;
10use crate::plan::VectorQueue;
11use crate::policy::immix::block::BlockState;
12use crate::scheduler::gc_work::RootKind;
13use crate::util::copy::CopySemantics;
14use crate::util::linear_scan::UnstraddlableRegion;
15use crate::util::metadata::side_metadata::SideMetadataSpec;
16use crate::util::rc::*;
17use crate::vm::slot::Slot;
18use crate::{
19 plan::concurrent::global::ConcurrentPlan,
20 plan::concurrent::Pause,
21 plan::global::Plan,
22 policy::{immix::block::Block, space::Space},
23 scheduler::{GCWork, GCWorker, WorkBucketStage},
24 util::{metadata::side_metadata, object_forwarding, ObjectReference},
25 vm::*,
26 MMTK,
27};
28use atomic::Ordering;
29use std::marker::PhantomData;
30use std::ops::{Deref, DerefMut};
31use std::sync::Arc;
32
33pub struct ProcessIncs<VM: VMBinding, const KIND: EdgeKind> {
34 incs: Vec<VM::VMSlot>,
36 new_incs: VectorQueue<VM::VMSlot>,
38 new_incs_count: u32,
39 pause: Pause,
40 in_cm: bool,
41 no_evac: bool,
42 pub root_kind: Option<RootKind>,
43 depth: u32,
44 lxr: &'static LXR<VM>,
45 rc: RefCountHelper<VM>,
46 survival_ratio_predictor_local: SurvivalRatioPredictorLocal,
47}
48
49unsafe impl<VM: VMBinding, const KIND: EdgeKind> Send for ProcessIncs<VM, KIND> {}
50
51impl<VM: VMBinding, const KIND: EdgeKind> ProcessIncs<VM, KIND> {
52 const CAPACITY: usize = 1024;
53 const UNLOG_BITS: SideMetadataSpec = *VM::VMObjectModel::GLOBAL_FIELD_UNLOG_BIT_SPEC
54 .as_spec()
55 .extract_side_spec();
56
57 fn __default(lxr: &'static LXR<VM>) -> Self {
58 Self {
59 incs: vec![],
60 new_incs: VectorQueue::default(),
61 new_incs_count: 0,
62 lxr,
63 pause: Pause::RefCount,
64 in_cm: false,
65 no_evac: false,
66 depth: 1,
67 rc: RefCountHelper::NEW,
68 root_kind: None,
69 survival_ratio_predictor_local: SurvivalRatioPredictorLocal::default(),
70 }
71 }
72
73 fn add_new_slot(&mut self, worker: &mut GCWorker<VM>, s: VM::VMSlot) {
74 self.new_incs.push(s);
75 self.new_incs_count += 1;
76 if self.new_incs_count as usize >= Self::CAPACITY {
77 self.flush(worker);
78 }
79 }
80
81 pub fn new(incs: Vec<VM::VMSlot>, lxr: &'static LXR<VM>) -> Self {
82 Self {
83 incs,
84 ..Self::__default(lxr)
85 }
86 }
87
88 fn process_root_nodes(
91 &mut self,
92 worker: &mut GCWorker<VM>,
93 nodes: Vec<ObjectReference>,
94 ) -> (Vec<ObjectReference>, Vec<ObjectReference>) {
95 let mut roots = Vec::with_capacity(nodes.len());
96 let mut uncounted = vec![];
97 for o in nodes {
98 if !self.lxr.is_rc_object(o) {
99 uncounted.push(o);
100 continue;
101 }
102 let los = self.lxr.los().in_space(o);
103 if self.inc(o) {
104 self.promote(worker, o, false, los, 0);
106 }
107 roots.push(o);
108 }
109 self.flush(worker);
112 (roots, uncounted)
113 }
114
115 pub fn do_work_root_nodes(
119 &mut self,
120 nodes: Vec<ObjectReference>,
121 worker: &mut GCWorker<VM>,
122 mmtk: &'static MMTK<VM>,
123 ) {
124 debug_assert_eq!(KIND, EDGE_KIND_ROOT);
125 let lxr = self.lxr;
126 self.pause = lxr.current_pause().unwrap();
127 self.in_cm = lxr.concurrent_work_in_progress();
128 let (roots, uncounted) = self.process_root_nodes(worker, nodes);
129 let mut to_trace = roots.clone();
130 to_trace.extend_from_slice(&uncounted);
131 if to_trace.is_empty() {
132 return;
133 }
134 if lxr.cm_enabled() && self.in_cm {
137 worker.add_work(
138 WorkBucketStage::FinishConcurrentWork,
139 ProcessModBufSATB::new(to_trace.clone()),
140 );
141 }
142 match self.pause {
145 Pause::Full => worker.add_work(
146 WorkBucketStage::PinningRootsTrace,
147 LXRStopTheWorldProcessNodes::<VM, true>::new(
148 to_trace,
149 mmtk,
150 WorkBucketStage::Closure,
151 ),
152 ),
153 Pause::FinalMark => worker.add_work(
154 WorkBucketStage::PinningRootsTrace,
155 LXRStopTheWorldProcessNodes::<VM, false>::new(
156 to_trace,
157 mmtk,
158 WorkBucketStage::Closure,
159 ),
160 ),
161 Pause::InitialMark if lxr.cm_enabled() => {
164 worker.scheduler().work_buckets[WorkBucketStage::ConcurrentResumable]
165 .add(LXRConcurrentTraceObjects::new(to_trace, mmtk));
166 }
167 _ => {}
169 }
170 if !roots.is_empty() {
172 lxr.curr_roots.read().unwrap().push(roots);
173 }
174 }
175
176 fn promote(
177 &mut self,
178 worker: &mut GCWorker<VM>,
179 o: ObjectReference,
180 copied: bool,
181 los: bool,
182 depth: u32,
183 ) {
184 let size = o.get_size::<VM>();
185
186 if !los {
187 let block = Block::containing(o);
188 let in_nursery_block = block.get_state() == BlockState::Nursery;
189 if !copied && in_nursery_block {
190 block.set_as_in_place_promoted();
191 }
192 self.rc.promote_with_size(o, size);
193 self.survival_ratio_predictor_local
194 .record_promotion(size, copied);
195 } else {
196 }
198 if self.in_cm || self.pause == Pause::FinalMark {
200 debug_assert!(self.lxr.is_marked(o), "{:?} is not marked", o);
201 }
202 self.scan_nursery_object(worker, o, los, !copied, depth, size);
203 }
204
205 fn record_mature_evac_remset2(
206 &mut self,
207 slot_in_defrag: bool,
208 s: VM::VMSlot,
209 o: ObjectReference,
210 ) {
211 if !(MATURE_EVACUATION && (self.in_cm || self.pause == Pause::FinalMark)) {
212 return;
213 }
214 if !slot_in_defrag && self.lxr.in_defrag(o) {
215 self.lxr.mature_evac_remset.record(s, o, self.lxr);
216 }
217 }
218
219 fn record_mature_evac_remset(&mut self, s: VM::VMSlot, o: ObjectReference) {
220 if !(MATURE_EVACUATION && (self.in_cm || self.pause == Pause::FinalMark)) {
221 return;
222 }
223 self.record_mature_evac_remset2(self.lxr.address_in_defrag(s.to_address()), s, o);
224 }
225
226 fn scan_nursery_object(
227 &mut self,
228 worker: &mut GCWorker<VM>,
229 o: ObjectReference,
230 los: bool,
231 in_place_promotion: bool,
232 _depth: u32,
233 size: usize,
234 ) {
235 let heap_bytes_per_unlog_byte = if VM::VMObjectModel::COMPRESSED_PTR_ENABLED {
236 32usize
237 } else {
238 64
239 };
240 if los {
241 let start =
242 side_metadata::address_to_meta_address(&Self::UNLOG_BITS, o.to_raw_address())
243 .to_mut_ptr::<u8>();
244 let limit = side_metadata::address_to_meta_address(
245 &Self::UNLOG_BITS,
246 (o.to_raw_address() + size).align_up(heap_bytes_per_unlog_byte),
247 )
248 .to_mut_ptr::<u8>();
249 unsafe {
250 let bytes = limit.offset_from(start) as usize;
251 std::ptr::write_bytes(start, 0xffu8, bytes);
252 }
253 o.to_raw_address().unlog_field_relaxed::<VM>();
254 } else if in_place_promotion {
255 let header_size = if VM::VMObjectModel::COMPRESSED_PTR_ENABLED {
256 12usize
257 } else {
258 16
259 };
260 let step = heap_bytes_per_unlog_byte << 2;
261 let end = o.to_raw_address() + size;
262 let aligned_end = end.align_up(step);
263 let cursor = o.to_raw_address() + header_size;
264 let mut cursor = cursor.align_down(step);
265 let mut meta = side_metadata::address_to_meta_address(&Self::UNLOG_BITS, cursor);
266 while cursor < aligned_end {
267 unsafe { meta.store(0xffffffffu32) }
268 meta += 4usize;
269 cursor += step;
270 }
271 };
272 #[cfg(feature = "lxr_object_log")]
274 VM::VMObjectModel::GLOBAL_LOG_BIT_SPEC.mark_as_unlogged::<VM>(o, Ordering::SeqCst);
275 let obj_in_defrag = !los && Block::in_defrag_block(o);
276 let tls = worker.tls.0;
277 o.iterate_fields::<VM, _>(tls, |slot| {
278 let Some(target) = slot.load() else {
279 return;
280 };
281 debug_assert!(
282 target.to_raw_address().is_mapped(),
283 "Unmapped obj {:?}.{:?} -> {:?}",
284 o,
285 slot,
286 target
287 );
288 debug_assert!(
289 target.is_in_any_space(),
290 "Unmapped obj {:?}.{:?} -> {:?}",
291 o,
292 slot,
293 target
294 );
295 let rc = self.rc.count(target);
296 if rc == 0 {
297 self.add_new_slot(worker, slot);
298 } else {
299 if rc != crate::util::rc::MAX_REF_COUNT {
300 let _ = self.rc.inc(target);
301 }
302 self.record_mature_evac_remset2(obj_in_defrag, slot, target);
303 }
304 });
305 }
306
307 #[cold]
308 fn flush(&mut self, worker: &mut GCWorker<VM>) {
309 if !self.new_incs.is_empty() {
310 let new_incs = self.new_incs.take();
311 let mut w = ProcessIncs::<VM, EDGE_KIND_NURSERY>::new(new_incs, self.lxr);
312 w.depth += 1;
313 worker.add_work(WorkBucketStage::Unconstrained, w);
314 }
315 self.new_incs_count = 0;
316 }
317
318 fn inc(&self, o: ObjectReference) -> bool {
320 self.rc.inc(o) == Ok(0)
321 }
322
323 fn dont_evacuate(&self, o: ObjectReference, los: bool) -> bool {
324 if los {
325 return true;
326 }
327 if self.rc.count(o) != 0 {
329 return true;
330 }
331 if Block::containing(o).get_state() != BlockState::Nursery {
333 return true;
334 }
335 if cfg!(debug_assertions) {
336 let cls = unsafe { (o.to_raw_address() + 8usize).load::<u32>() };
337 assert!(cls != 0, "ERROR {:?} rc={}", o, self.rc.count(o));
338 }
339 false
340 }
341
342 fn process_inc_and_evacuate(
343 &mut self,
344 worker: &mut GCWorker<VM>,
345 o: ObjectReference,
346 depth: u32,
347 ) -> ObjectReference {
348 let los = self.lxr.los().in_space(o);
349 if NURSERY_EVACUATION && !los && object_forwarding::is_forwarded_or_being_forwarded::<VM>(o)
350 {
351 while object_forwarding::is_being_forwarded::<VM>(o) {
352 std::hint::spin_loop();
353 }
354 let new = if object_forwarding::is_forwarded::<VM>(o) {
355 object_forwarding::read_forwarding_pointer::<VM>(o)
356 } else {
357 o
358 };
359 let promoted = self.inc(new);
360 if promoted && new == o {
361 self.promote(worker, o, false, los, depth);
362 }
363 return new;
364 }
365 if !NURSERY_EVACUATION || self.dont_evacuate(o, los) {
366 if self.inc(o) {
367 self.promote(worker, o, false, los, depth);
368 }
369 return o;
370 }
371 let forwarding_status = object_forwarding::attempt_to_forward::<VM>(o);
372 if object_forwarding::state_is_forwarded_or_being_forwarded(forwarding_status) {
373 let new = object_forwarding::spin_and_get_forwarded_object::<VM>(o, forwarding_status);
375 self.inc(new);
376 new
377 } else {
378 let is_nursery = self.rc.count(o) == 0;
379 if is_nursery && !self.no_evac {
380 let new = object_forwarding::try_forward_object::<VM>(
382 o,
383 CopySemantics::DefaultCopy,
384 worker.get_copy_context_mut(),
385 |_new| {
386 #[cfg(feature = "vo_bit")]
387 {
388 crate::util::metadata::vo_bit::set_vo_bit(_new);
390 crate::util::metadata::vo_bit::unset_vo_bit(o);
395 }
396 },
397 );
398 if let Some(new) = new {
399 self.inc(new);
400 self.promote(worker, new, true, false, depth);
401 new
402 } else {
403 warn!("to-space overflow");
404 let promoted = self.inc(o);
406 object_forwarding::clear_forwarding_bits::<VM>(o);
407 if promoted {
408 self.promote(worker, o, false, los, depth);
409 }
410 NO_EVAC.store(true, Ordering::Relaxed);
411 self.no_evac = true;
412 o
413 }
414 } else {
415 let promoted = self.inc(o);
417 object_forwarding::clear_forwarding_bits::<VM>(o);
418 if promoted {
419 self.promote(worker, o, false, los, depth);
420 }
421 o
422 }
423 }
424 }
425
426 fn unlog_and_load_rc_object<const K: EdgeKind>(
428 &mut self,
429 s: VM::VMSlot,
430 ) -> Option<ObjectReference> {
431 let o = s.load();
432 if K == EDGE_KIND_MATURE {
434 s.to_address().unlog_field_relaxed::<VM>();
435 }
436 o
437 }
438
439 fn process_slot<const K: EdgeKind>(
440 &mut self,
441 worker: &mut GCWorker<VM>,
442 s: VM::VMSlot,
443 depth: u32,
444 add_root_to_remset: bool,
445 ) -> Option<ObjectReference> {
446 let o = match self.unlog_and_load_rc_object::<K>(s) {
447 Some(o) => o,
448 _ => {
449 return None;
450 }
451 };
452 let new = self.process_inc_and_evacuate(worker, o, depth);
454 if K != EDGE_KIND_ROOT || add_root_to_remset {
456 self.record_mature_evac_remset(s, new);
457 }
458 if new != o {
459 s.store(new)
460 }
461 Some(new)
462 }
463
464 fn process_incs<const K: EdgeKind>(
465 &mut self,
466 worker: &mut GCWorker<VM>,
467 mut incs: AddressBuffer<'_, VM::VMSlot>,
468 depth: u32,
469 add_root_to_remset: bool,
470 ) -> Option<Vec<ObjectReference>> {
471 if K == EDGE_KIND_ROOT {
472 if std::mem::size_of::<VM::VMSlot>() == std::mem::size_of::<ObjectReference>()
477 && std::mem::align_of::<VM::VMSlot>() == std::mem::align_of::<ObjectReference>()
478 {
479 let roots = incs.as_mut_ptr() as *mut ObjectReference;
480 let mut num_roots = 0usize;
481 for s in incs.iter() {
482 if let Some(new) = self.process_slot::<K>(worker, *s, depth, add_root_to_remset)
483 {
484 unsafe {
485 roots.add(num_roots).write(new);
486 }
487 num_roots += 1;
488 }
489 }
490 return if num_roots != 0 {
491 let cap = incs.capacity();
492 std::mem::forget(incs); let roots =
494 unsafe { Vec::<ObjectReference>::from_raw_parts(roots, num_roots, cap) };
495 Some(roots)
496 } else {
497 None
498 };
499 }
500
501 let mut roots = Vec::with_capacity(incs.len());
503 for s in incs.iter() {
504 if let Some(new) = self.process_slot::<K>(worker, *s, depth, add_root_to_remset) {
505 roots.push(new);
506 }
507 }
508 if roots.is_empty() {
509 None
510 } else {
511 Some(roots)
512 }
513 } else {
514 for s in incs.iter() {
515 self.process_slot::<K>(worker, *s, depth, false);
516 }
517 None
518 }
519 }
520}
521
522pub type EdgeKind = u8;
523pub const EDGE_KIND_ROOT: u8 = 0;
524pub const EDGE_KIND_NURSERY: u8 = 1;
525pub const EDGE_KIND_MATURE: u8 = 2;
526
527enum AddressBuffer<'a, S: Slot> {
528 Owned(Vec<S>),
529 Ref(&'a mut Vec<S>),
530}
531
532impl<S: Slot> Deref for AddressBuffer<'_, S> {
533 type Target = Vec<S>;
534 fn deref(&self) -> &Self::Target {
535 match self {
536 Self::Owned(x) => x,
537 Self::Ref(x) => x,
538 }
539 }
540}
541
542impl<S: Slot> DerefMut for AddressBuffer<'_, S> {
543 fn deref_mut(&mut self) -> &mut Self::Target {
544 match self {
545 Self::Owned(x) => x,
546 Self::Ref(x) => x,
547 }
548 }
549}
550
551impl<VM: VMBinding, const KIND: EdgeKind> GCWork<VM> for ProcessIncs<VM, KIND> {
552 fn do_work(&mut self, worker: &mut GCWorker<VM>, mmtk: &'static MMTK<VM>) {
553 self.lxr = mmtk.get_plan().downcast_ref::<LXR<VM>>().unwrap();
554 self.pause = self.lxr.current_pause().unwrap();
555 self.in_cm = self.lxr.concurrent_work_in_progress();
556 if NO_EVAC.load(Ordering::Relaxed) {
557 self.no_evac = true;
558 } else {
559 let over_space = mmtk.get_plan().get_used_pages()
560 - mmtk.get_plan().get_collection_reserved_pages()
561 > mmtk.get_plan().get_total_pages();
562 if over_space {
563 self.no_evac = true;
564 NO_EVAC.store(true, Ordering::Relaxed);
565 }
566 }
567 let root_slots = if KIND == EDGE_KIND_ROOT
569 && (self.pause == Pause::FinalMark || self.pause == Pause::Full)
570 {
571 self.incs.clone()
572 } else {
573 vec![]
574 };
575 let roots = {
576 let incs = std::mem::take(&mut self.incs);
577 self.process_incs::<KIND>(worker, AddressBuffer::Owned(incs), self.depth, false)
578 };
579 if let Some(roots) = roots {
580 if self.lxr.cm_enabled()
581 && self.pause == Pause::InitialMark
582 && !self.root_kind.unwrap().should_skip_mark_and_decs()
583 {
584 if cfg!(any(feature = "sanity", debug_assertions)) {
585 for r in &roots {
586 assert!(
587 r.to_raw_address().is_mapped(),
588 "Invalid object {:?}: address is not mapped",
589 r
590 );
591 }
592 }
593 worker.scheduler().work_buckets[WorkBucketStage::ConcurrentResumable]
594 .add(LXRConcurrentTraceObjects::new(roots.clone(), mmtk));
595 }
596 if self.pause == Pause::FinalMark || self.pause == Pause::Full {
597 if !root_slots.is_empty() && self.root_kind != Some(RootKind::Weak) {
598 if self.pause == Pause::FinalMark {
599 let mut w = LXRStopTheWorldProcessEdges::<_, false>::new(
600 root_slots,
601 true,
602 mmtk,
603 WorkBucketStage::Closure,
604 );
605 w.root_kind = self.root_kind;
606 worker.add_work(WorkBucketStage::Closure, w)
607 } else {
608 let mut w = LXRStopTheWorldProcessEdges::<_, true>::new(
609 root_slots,
610 true,
611 mmtk,
612 WorkBucketStage::Closure,
613 );
614 w.root_kind = self.root_kind;
615 worker.add_work(WorkBucketStage::Closure, w)
616 };
617 }
618 } else if !self.root_kind.unwrap().should_skip_decs() {
619 self.lxr.curr_roots.read().unwrap().push(roots);
620 }
621 }
622 let mut depth = self.depth;
624 let mut incs = vec![];
625 let split_size = *self.lxr.base().options.lxr_min_packet_split_size;
628 let split_depth = *self.lxr.base().options.lxr_min_packet_split_depth;
630 while !self.new_incs.is_empty() {
631 self.new_incs_count = 0;
632 depth += 1;
633 incs.clear();
634 self.new_incs.swap(&mut incs);
635 if depth as usize >= split_depth && incs.len() > split_size {
636 let (a, b) = incs.split_at(incs.len() / 2);
637 let mut w = ProcessIncs::<VM, EDGE_KIND_NURSERY>::new(b.to_vec(), self.lxr);
638 w.depth = depth;
639 worker.add_work(WorkBucketStage::Unconstrained, w);
640 incs = a.to_vec();
641 }
642 if !incs.is_empty() {
643 self.process_incs::<EDGE_KIND_NURSERY>(
644 worker,
645 AddressBuffer::Ref(&mut incs),
646 depth,
647 false,
648 );
649 }
650 }
651 self.survival_ratio_predictor_local.sync();
652 }
653}
654
655pub struct ProcessDecs<VM: VMBinding> {
656 decs: Option<Vec<ObjectReference>>,
658 decs_arc: Option<Arc<Vec<ObjectReference>>>,
659 new_decs: VectorQueue<ObjectReference>,
661 counter: LazySweepingJobsCounter,
662 mark_objects: VectorQueue<ObjectReference>,
663 mark_dead_objects: bool,
664 mature_sweeping_in_progress: bool,
665 rc: RefCountHelper<VM>,
666}
667
668impl<VM: VMBinding> ProcessDecs<VM> {
669 pub fn new(decs: Vec<ObjectReference>, counter: LazySweepingJobsCounter) -> Self {
670 Self {
671 decs: Some(decs),
672 decs_arc: None,
673 new_decs: VectorQueue::default(),
674 counter,
675 mark_objects: VectorQueue::default(),
676 mark_dead_objects: false,
677 mature_sweeping_in_progress: false,
678 rc: RefCountHelper::NEW,
679 }
680 }
681
682 pub fn new_arc(decs: Arc<Vec<ObjectReference>>, counter: LazySweepingJobsCounter) -> Self {
683 Self {
684 decs: None,
685 decs_arc: Some(decs),
686 new_decs: VectorQueue::default(),
687 counter,
688 mark_objects: VectorQueue::default(),
689 mark_dead_objects: false,
690 mature_sweeping_in_progress: false,
691 rc: RefCountHelper::NEW,
692 }
693 }
694
695 fn recursive_dec(&mut self, worker: &mut GCWorker<VM>, o: ObjectReference) {
696 self.new_decs.push(o);
697 if self.new_decs.is_full() {
698 self.flush(worker)
699 }
700 }
701
702 fn new_work(&self, worker: &mut GCWorker<VM>, w: ProcessDecs<VM>) {
703 worker.add_work(WorkBucketStage::Unconstrained, w);
704 }
705
706 fn flush(&mut self, worker: &mut GCWorker<VM>) {
707 let mmtk = worker.mmtk;
708 if !self.new_decs.is_empty() {
709 let new_decs = self.new_decs.take();
710 self.new_work(
711 worker,
712 ProcessDecs::new(new_decs, self.counter.clone_with_decs()),
713 );
714 }
715 if !self.mark_objects.is_empty() {
716 let objects = self.mark_objects.take();
717 let w = LXRConcurrentTraceObjects::new(objects, mmtk);
718 if LAZY_DECREMENTS {
719 worker.add_work(WorkBucketStage::Unconstrained, w);
720 } else {
721 worker.scheduler().work_buckets[WorkBucketStage::ConcurrentResumable].add(w);
722 }
723 }
724 }
725
726 #[cold]
727 fn process_dead_object(
728 &mut self,
729 worker: &mut GCWorker<VM>,
730 o: ObjectReference,
731 lxr: &LXR<VM>,
732 ) -> bool {
733 if self.mark_dead_objects {
734 lxr.mark(o);
735 }
736 let tls = worker.tls.0;
738 o.iterate_fields::<VM, _>(tls, |slot| {
739 if let Some(x) = slot.load() {
740 let rc = self.rc.count(x);
742 if rc != MAX_REF_COUNT && rc != 0 {
743 self.recursive_dec(worker, x);
744 }
745 if self.mark_dead_objects && !lxr.is_marked(x) {
746 if cfg!(any(feature = "sanity", debug_assertions)) {
747 assert!(
748 x.to_raw_address().is_mapped(),
749 "Invalid object {:?}.{:?} -> {:?}: address is not mapped",
750 o,
751 slot,
752 x
753 );
754 }
755 self.mark_objects.push(x);
756 if self.mark_objects.is_full() {
757 self.flush(worker);
758 }
759 }
760 }
761 });
762 let in_ix_space = lxr.immix_space.in_space(o);
763 if in_ix_space {
764 #[cfg(feature = "vo_bit")]
768 crate::util::metadata::vo_bit::unset_vo_bit(o);
769
770 self.rc.unmark_straddle_object(o);
771 }
772 if RefCountHelper::<VM>::SANITY {
773 unsafe { o.to_raw_address().store(0xdeadusize) };
774 }
775 if in_ix_space {
776 let block = Block::containing(o);
777 lxr.add_to_possibly_dead_mature_blocks(block, false);
778 false
779 } else {
780 true
781 }
782 }
783
784 fn process_decs(&mut self, worker: &mut GCWorker<VM>, decs: &[ObjectReference], lxr: &LXR<VM>) {
785 for o in decs.iter() {
786 if self.rc.is_dead_or_stuck(*o)
787 || (self.mature_sweeping_in_progress && !lxr.is_marked(*o))
788 {
789 continue;
790 }
791 let o = if MATURE_EVACUATION && object_forwarding::is_forwarded::<VM>(*o) {
792 object_forwarding::read_forwarding_pointer::<VM>(*o)
793 } else {
794 *o
795 };
796 let mut dead = false;
797 let mut is_los = false;
798 let mut already_run = false;
799 let result = self.rc.clone().fetch_update(o, |c| {
800 if already_run {
801 log::warn!("fetch_update is re-run! o: {o}");
802 } else {
803 already_run = true;
804 }
805 if c == 1 && !dead {
806 dead = true;
807 is_los = self.process_dead_object(worker, o, lxr);
808 }
809 debug_assert!(c <= MAX_REF_COUNT);
810 if c == 0 || c == MAX_REF_COUNT {
811 None } else {
813 Some(c - 1)
814 }
815 });
816 if result == Ok(1) && is_los {
817 lxr.los().rc_free(o);
818 }
819 }
820 }
821}
822
823impl<VM: VMBinding> GCWork<VM> for ProcessDecs<VM> {
824 fn do_work(&mut self, worker: &mut GCWorker<VM>, mmtk: &'static MMTK<VM>) {
825 let lxr = mmtk.get_plan().downcast_ref::<LXR<VM>>().unwrap();
826 self.mark_dead_objects = if LAZY_DECREMENTS {
827 lxr.concurrent_work_in_progress() && lxr.previous_pause() != Some(Pause::InitialMark)
828 } else {
829 lxr.concurrent_work_in_progress() && lxr.current_pause() != Some(Pause::InitialMark)
830 };
831 self.mature_sweeping_in_progress = if LAZY_DECREMENTS {
832 lxr.previous_pause() == Some(Pause::FinalMark)
833 || lxr.current_pause() == Some(Pause::Full)
834 } else {
835 lxr.current_pause() == Some(Pause::FinalMark)
836 || lxr.current_pause() == Some(Pause::Full)
837 };
838 if let Some(decs) = std::mem::take(&mut self.decs) {
839 self.process_decs(worker, &decs, lxr);
840 } else if let Some(decs) = std::mem::take(&mut self.decs_arc) {
841 self.process_decs(worker, &decs, lxr);
842 }
843 let mut decs = vec![];
844 while !self.new_decs.is_empty() {
845 decs.clear();
846 self.new_decs.swap(&mut decs);
847 self.process_decs(worker, &decs, lxr);
848 }
849 self.flush(worker);
850 }
851}
852
853pub struct CollectSlotRoots<VM: VMBinding> {
854 base: ProcessEdgesBase<VM>,
855}
856
857impl<VM: VMBinding> CollectSlotRoots<VM> {
858 pub fn new(
859 slots: Vec<VM::VMSlot>,
860 roots: bool,
861 mmtk: &'static MMTK<VM>,
862 bucket: WorkBucketStage,
863 ) -> Self {
864 debug_assert!(roots);
865 let base = ProcessEdgesBase::new(slots, roots, mmtk, bucket);
866 Self { base }
867 }
868}
869
870impl<VM: VMBinding> GCWork<VM> for CollectSlotRoots<VM> {
871 fn do_work(&mut self, worker: &mut GCWorker<VM>, _mmtk: &'static MMTK<VM>) {
872 self.set_worker(worker);
873 if !self.slots.is_empty() {
874 let lxr = self.mmtk().get_plan().downcast_ref::<LXR<VM>>().unwrap();
875 let roots = std::mem::take(&mut self.slots);
876 let mut w = ProcessIncs::<_, EDGE_KIND_ROOT>::new(roots, lxr);
877 w.root_kind = self.root_kind;
878 GCWork::do_work(&mut w, self.worker(), self.mmtk());
879 }
880 }
881}
882
883impl<VM: VMBinding> Deref for CollectSlotRoots<VM> {
884 type Target = ProcessEdgesBase<VM>;
885 fn deref(&self) -> &Self::Target {
886 &self.base
887 }
888}
889
890impl<VM: VMBinding> DerefMut for CollectSlotRoots<VM> {
891 fn deref_mut(&mut self) -> &mut Self::Target {
892 &mut self.base
893 }
894}
895
896pub struct CollectNodeRoots<VM: VMBinding> {
899 nodes: Vec<ObjectReference>,
900 _p: PhantomData<VM>,
901}
902
903impl<VM: VMBinding> CollectNodeRoots<VM> {
904 pub fn new(nodes: Vec<ObjectReference>) -> Self {
905 Self {
906 nodes,
907 _p: PhantomData,
908 }
909 }
910}
911
912impl<VM: VMBinding> GCWork<VM> for CollectNodeRoots<VM> {
913 fn do_work(&mut self, worker: &mut GCWorker<VM>, mmtk: &'static MMTK<VM>) {
914 if !self.nodes.is_empty() {
915 let lxr = mmtk.get_plan().downcast_ref::<LXR<VM>>().unwrap();
916 let roots = std::mem::take(&mut self.nodes);
917 let mut w = ProcessIncs::<_, EDGE_KIND_ROOT>::new(vec![], lxr);
918 w.root_kind = Some(RootKind::Strong);
920 w.do_work_root_nodes(roots, worker, mmtk);
921 }
922 }
923}