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