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 if !self.lxr.is_rc_object(o) {
452 return None;
453 }
454 let new = self.process_inc_and_evacuate(worker, o, depth);
456 if K != EDGE_KIND_ROOT || add_root_to_remset {
458 self.record_mature_evac_remset(s, new);
459 }
460 if new != o {
461 s.store(new)
462 }
463 Some(new)
464 }
465
466 fn process_incs<const K: EdgeKind>(
467 &mut self,
468 worker: &mut GCWorker<VM>,
469 mut incs: AddressBuffer<'_, VM::VMSlot>,
470 depth: u32,
471 add_root_to_remset: bool,
472 ) -> Option<Vec<ObjectReference>> {
473 if K == EDGE_KIND_ROOT {
474 if std::mem::size_of::<VM::VMSlot>() == std::mem::size_of::<ObjectReference>()
479 && std::mem::align_of::<VM::VMSlot>() == std::mem::align_of::<ObjectReference>()
480 {
481 let roots = incs.as_mut_ptr() as *mut ObjectReference;
482 let mut num_roots = 0usize;
483 for s in incs.iter() {
484 if let Some(new) = self.process_slot::<K>(worker, *s, depth, add_root_to_remset)
485 {
486 unsafe {
487 roots.add(num_roots).write(new);
488 }
489 num_roots += 1;
490 }
491 }
492 return if num_roots != 0 {
493 let cap = incs.capacity();
494 std::mem::forget(incs); let roots =
496 unsafe { Vec::<ObjectReference>::from_raw_parts(roots, num_roots, cap) };
497 Some(roots)
498 } else {
499 None
500 };
501 }
502
503 let mut roots = Vec::with_capacity(incs.len());
505 for s in incs.iter() {
506 if let Some(new) = self.process_slot::<K>(worker, *s, depth, add_root_to_remset) {
507 roots.push(new);
508 }
509 }
510 if roots.is_empty() {
511 None
512 } else {
513 Some(roots)
514 }
515 } else {
516 for s in incs.iter() {
517 self.process_slot::<K>(worker, *s, depth, false);
518 }
519 None
520 }
521 }
522}
523
524pub type EdgeKind = u8;
525pub const EDGE_KIND_ROOT: u8 = 0;
526pub const EDGE_KIND_NURSERY: u8 = 1;
527pub const EDGE_KIND_MATURE: u8 = 2;
528
529enum AddressBuffer<'a, S: Slot> {
530 Owned(Vec<S>),
531 Ref(&'a mut Vec<S>),
532}
533
534impl<S: Slot> Deref for AddressBuffer<'_, S> {
535 type Target = Vec<S>;
536 fn deref(&self) -> &Self::Target {
537 match self {
538 Self::Owned(x) => x,
539 Self::Ref(x) => x,
540 }
541 }
542}
543
544impl<S: Slot> DerefMut for AddressBuffer<'_, S> {
545 fn deref_mut(&mut self) -> &mut Self::Target {
546 match self {
547 Self::Owned(x) => x,
548 Self::Ref(x) => x,
549 }
550 }
551}
552
553impl<VM: VMBinding, const KIND: EdgeKind> GCWork<VM> for ProcessIncs<VM, KIND> {
554 fn do_work(&mut self, worker: &mut GCWorker<VM>, mmtk: &'static MMTK<VM>) {
555 self.lxr = mmtk.get_plan().downcast_ref::<LXR<VM>>().unwrap();
556 self.pause = self.lxr.current_pause().unwrap();
557 self.in_cm = self.lxr.concurrent_work_in_progress();
558 if NO_EVAC.load(Ordering::Relaxed) {
559 self.no_evac = true;
560 } else {
561 let over_space = mmtk.get_plan().get_used_pages()
562 - mmtk.get_plan().get_collection_reserved_pages()
563 > mmtk.get_plan().get_total_pages();
564 if over_space {
565 self.no_evac = true;
566 NO_EVAC.store(true, Ordering::Relaxed);
567 }
568 }
569 let root_slots = if KIND == EDGE_KIND_ROOT
571 && (self.pause == Pause::FinalMark || self.pause == Pause::Full)
572 {
573 self.incs.clone()
574 } else {
575 vec![]
576 };
577 let roots = {
578 let incs = std::mem::take(&mut self.incs);
579 self.process_incs::<KIND>(worker, AddressBuffer::Owned(incs), self.depth, false)
580 };
581 if let Some(roots) = &roots {
582 if self.lxr.cm_enabled()
583 && self.pause == Pause::InitialMark
584 && !self.root_kind.unwrap().should_skip_mark_and_decs()
585 {
586 if cfg!(any(feature = "sanity", debug_assertions)) {
587 for r in roots {
588 assert!(
589 r.to_raw_address().is_mapped(),
590 "Invalid object {:?}: address is not mapped",
591 r
592 );
593 }
594 }
595 worker.scheduler().work_buckets[WorkBucketStage::ConcurrentResumable]
596 .add(LXRConcurrentTraceObjects::new(roots.clone(), mmtk));
597 }
598 }
599 if self.pause == Pause::FinalMark || self.pause == Pause::Full {
602 if !root_slots.is_empty() && self.root_kind != Some(RootKind::Weak) {
603 if self.pause == Pause::FinalMark {
604 let mut w = LXRStopTheWorldProcessEdges::<_, false>::new(
605 root_slots,
606 true,
607 mmtk,
608 WorkBucketStage::Closure,
609 );
610 w.root_kind = self.root_kind;
611 worker.add_work(WorkBucketStage::Closure, w)
612 } else {
613 let mut w = LXRStopTheWorldProcessEdges::<_, true>::new(
614 root_slots,
615 true,
616 mmtk,
617 WorkBucketStage::Closure,
618 );
619 w.root_kind = self.root_kind;
620 worker.add_work(WorkBucketStage::Closure, w)
621 };
622 }
623 } else if let Some(roots) = roots {
624 if !self.root_kind.unwrap().should_skip_decs() {
625 self.lxr.curr_roots.read().unwrap().push(roots);
626 }
627 }
628 let mut depth = self.depth;
630 let mut incs = vec![];
631 let split_size = *self.lxr.base().options.lxr_min_packet_split_size;
634 let split_depth = *self.lxr.base().options.lxr_min_packet_split_depth;
636 while !self.new_incs.is_empty() {
637 self.new_incs_count = 0;
638 depth += 1;
639 incs.clear();
640 self.new_incs.swap(&mut incs);
641 if depth as usize >= split_depth && incs.len() > split_size {
642 let (a, b) = incs.split_at(incs.len() / 2);
643 let mut w = ProcessIncs::<VM, EDGE_KIND_NURSERY>::new(b.to_vec(), self.lxr);
644 w.depth = depth;
645 worker.add_work(WorkBucketStage::Unconstrained, w);
646 incs = a.to_vec();
647 }
648 if !incs.is_empty() {
649 self.process_incs::<EDGE_KIND_NURSERY>(
650 worker,
651 AddressBuffer::Ref(&mut incs),
652 depth,
653 false,
654 );
655 }
656 }
657 self.survival_ratio_predictor_local.sync();
658 }
659}
660
661pub struct ProcessDecs<VM: VMBinding> {
662 decs: Option<Vec<ObjectReference>>,
664 decs_arc: Option<Arc<Vec<ObjectReference>>>,
665 new_decs: VectorQueue<ObjectReference>,
667 counter: LazySweepingJobsCounter,
668 mark_objects: VectorQueue<ObjectReference>,
669 mark_dead_objects: bool,
670 mature_sweeping_in_progress: bool,
671 rc: RefCountHelper<VM>,
672}
673
674impl<VM: VMBinding> ProcessDecs<VM> {
675 pub fn new(decs: Vec<ObjectReference>, counter: LazySweepingJobsCounter) -> Self {
676 Self {
677 decs: Some(decs),
678 decs_arc: None,
679 new_decs: VectorQueue::default(),
680 counter,
681 mark_objects: VectorQueue::default(),
682 mark_dead_objects: false,
683 mature_sweeping_in_progress: false,
684 rc: RefCountHelper::NEW,
685 }
686 }
687
688 pub fn new_arc(decs: Arc<Vec<ObjectReference>>, counter: LazySweepingJobsCounter) -> Self {
689 Self {
690 decs: None,
691 decs_arc: Some(decs),
692 new_decs: VectorQueue::default(),
693 counter,
694 mark_objects: VectorQueue::default(),
695 mark_dead_objects: false,
696 mature_sweeping_in_progress: false,
697 rc: RefCountHelper::NEW,
698 }
699 }
700
701 fn recursive_dec(&mut self, worker: &mut GCWorker<VM>, o: ObjectReference) {
702 self.new_decs.push(o);
703 if self.new_decs.is_full() {
704 self.flush(worker)
705 }
706 }
707
708 fn new_work(&self, worker: &mut GCWorker<VM>, w: ProcessDecs<VM>) {
709 worker.add_work(WorkBucketStage::Unconstrained, w);
710 }
711
712 fn flush(&mut self, worker: &mut GCWorker<VM>) {
713 let mmtk = worker.mmtk;
714 if !self.new_decs.is_empty() {
715 let new_decs = self.new_decs.take();
716 self.new_work(
717 worker,
718 ProcessDecs::new(new_decs, self.counter.clone_with_decs()),
719 );
720 }
721 if !self.mark_objects.is_empty() {
722 let objects = self.mark_objects.take();
723 let w = LXRConcurrentTraceObjects::new(objects, mmtk);
724 if LAZY_DECREMENTS {
725 worker.add_work(WorkBucketStage::Unconstrained, w);
726 } else {
727 worker.scheduler().work_buckets[WorkBucketStage::ConcurrentResumable].add(w);
728 }
729 }
730 }
731
732 #[cold]
733 fn process_dead_object(
734 &mut self,
735 worker: &mut GCWorker<VM>,
736 o: ObjectReference,
737 lxr: &LXR<VM>,
738 ) -> bool {
739 if self.mark_dead_objects {
740 lxr.mark(o);
741 }
742 let tls = worker.tls.0;
744 o.iterate_fields::<VM, _>(tls, |slot| {
745 if let Some(x) = slot.load() {
746 let rc = self.rc.count(x);
748 if rc != MAX_REF_COUNT && rc != 0 {
749 self.recursive_dec(worker, x);
750 }
751 if self.mark_dead_objects && !lxr.is_marked(x) {
752 if cfg!(any(feature = "sanity", debug_assertions)) {
753 assert!(
754 x.to_raw_address().is_mapped(),
755 "Invalid object {:?}.{:?} -> {:?}: address is not mapped",
756 o,
757 slot,
758 x
759 );
760 }
761 self.mark_objects.push(x);
762 if self.mark_objects.is_full() {
763 self.flush(worker);
764 }
765 }
766 }
767 });
768 let in_ix_space = lxr.immix_space.in_space(o);
769 let in_los = !in_ix_space && lxr.los().in_space(o);
770 debug_assert!(
771 in_ix_space || in_los,
772 "{:?} is not reference counted, so its count can never reach zero",
773 o
774 );
775 if in_ix_space {
776 #[cfg(feature = "vo_bit")]
780 crate::util::metadata::vo_bit::unset_vo_bit(o);
781
782 self.rc.unmark_straddle_object(o);
783 }
784 if RefCountHelper::<VM>::SANITY {
785 unsafe { o.to_raw_address().store(0xdeadusize) };
786 }
787 if in_ix_space {
788 let block = Block::containing(o);
789 lxr.add_to_possibly_dead_mature_blocks(block, false);
790 false
791 } else {
792 in_los
793 }
794 }
795
796 fn process_decs(&mut self, worker: &mut GCWorker<VM>, decs: &[ObjectReference], lxr: &LXR<VM>) {
797 for o in decs.iter() {
798 if self.rc.is_dead_or_stuck(*o)
799 || (self.mature_sweeping_in_progress && !lxr.is_marked(*o))
800 {
801 continue;
802 }
803 let o = if MATURE_EVACUATION && object_forwarding::is_forwarded::<VM>(*o) {
804 object_forwarding::read_forwarding_pointer::<VM>(*o)
805 } else {
806 *o
807 };
808 let mut dead = false;
809 let mut is_los = false;
810 let mut already_run = false;
811 let result = self.rc.clone().fetch_update(o, |c| {
812 if already_run {
813 log::warn!("fetch_update is re-run! o: {o}");
814 } else {
815 already_run = true;
816 }
817 if c == 1 && !dead {
818 dead = true;
819 is_los = self.process_dead_object(worker, o, lxr);
820 }
821 debug_assert!(c <= MAX_REF_COUNT);
822 if c == 0 || c == MAX_REF_COUNT {
823 None } else {
825 Some(c - 1)
826 }
827 });
828 if result == Ok(1) && is_los {
829 lxr.los().rc_free(o);
830 }
831 }
832 }
833}
834
835impl<VM: VMBinding> GCWork<VM> for ProcessDecs<VM> {
836 fn do_work(&mut self, worker: &mut GCWorker<VM>, mmtk: &'static MMTK<VM>) {
837 let lxr = mmtk.get_plan().downcast_ref::<LXR<VM>>().unwrap();
838 self.mark_dead_objects = if LAZY_DECREMENTS {
839 lxr.concurrent_work_in_progress() && lxr.previous_pause() != Some(Pause::InitialMark)
840 } else {
841 lxr.concurrent_work_in_progress() && lxr.current_pause() != Some(Pause::InitialMark)
842 };
843 self.mature_sweeping_in_progress = if LAZY_DECREMENTS {
844 lxr.previous_pause() == Some(Pause::FinalMark)
845 || lxr.current_pause() == Some(Pause::Full)
846 } else {
847 lxr.current_pause() == Some(Pause::FinalMark)
848 || lxr.current_pause() == Some(Pause::Full)
849 };
850 if let Some(decs) = std::mem::take(&mut self.decs) {
851 self.process_decs(worker, &decs, lxr);
852 } else if let Some(decs) = std::mem::take(&mut self.decs_arc) {
853 self.process_decs(worker, &decs, lxr);
854 }
855 let mut decs = vec![];
856 while !self.new_decs.is_empty() {
857 decs.clear();
858 self.new_decs.swap(&mut decs);
859 self.process_decs(worker, &decs, lxr);
860 }
861 self.flush(worker);
862 }
863}
864
865pub struct CollectSlotRoots<VM: VMBinding> {
866 base: ProcessEdgesBase<VM>,
867}
868
869impl<VM: VMBinding> CollectSlotRoots<VM> {
870 pub fn new(
871 slots: Vec<VM::VMSlot>,
872 roots: bool,
873 mmtk: &'static MMTK<VM>,
874 bucket: WorkBucketStage,
875 ) -> Self {
876 debug_assert!(roots);
877 let base = ProcessEdgesBase::new(slots, roots, mmtk, bucket);
878 Self { base }
879 }
880}
881
882impl<VM: VMBinding> GCWork<VM> for CollectSlotRoots<VM> {
883 fn do_work(&mut self, worker: &mut GCWorker<VM>, _mmtk: &'static MMTK<VM>) {
884 self.set_worker(worker);
885 if !self.slots.is_empty() {
886 let lxr = self.mmtk().get_plan().downcast_ref::<LXR<VM>>().unwrap();
887 let roots = std::mem::take(&mut self.slots);
888 let mut w = ProcessIncs::<_, EDGE_KIND_ROOT>::new(roots, lxr);
889 w.root_kind = self.root_kind;
890 GCWork::do_work(&mut w, self.worker(), self.mmtk());
891 }
892 }
893}
894
895impl<VM: VMBinding> Deref for CollectSlotRoots<VM> {
896 type Target = ProcessEdgesBase<VM>;
897 fn deref(&self) -> &Self::Target {
898 &self.base
899 }
900}
901
902impl<VM: VMBinding> DerefMut for CollectSlotRoots<VM> {
903 fn deref_mut(&mut self) -> &mut Self::Target {
904 &mut self.base
905 }
906}
907
908#[cfg(feature = "non_moving_root")]
909pub struct CollectNodeRoots<VM: VMBinding> {
912 nodes: Vec<ObjectReference>,
913 _p: PhantomData<VM>,
914}
915
916#[cfg(feature = "non_moving_root")]
917impl<VM: VMBinding> CollectNodeRoots<VM> {
918 pub fn new(nodes: Vec<ObjectReference>) -> Self {
919 Self {
920 nodes,
921 _p: PhantomData,
922 }
923 }
924}
925
926#[cfg(feature = "non_moving_root")]
927impl<VM: VMBinding> GCWork<VM> for CollectNodeRoots<VM> {
928 fn do_work(&mut self, worker: &mut GCWorker<VM>, mmtk: &'static MMTK<VM>) {
929 if !self.nodes.is_empty() {
930 let lxr = mmtk.get_plan().downcast_ref::<LXR<VM>>().unwrap();
931 let roots = std::mem::take(&mut self.nodes);
932 let mut w = ProcessIncs::<_, EDGE_KIND_ROOT>::new(vec![], lxr);
933 w.root_kind = Some(RootKind::Strong);
935 w.do_work_root_nodes(roots, worker, mmtk);
936 }
937 }
938}