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::ProcessEdgesBase;
8use crate::plan::VectorQueue;
9use crate::policy::immix::block::BlockState;
10use crate::scheduler::gc_work::RootKind;
11use crate::util::copy::CopySemantics;
12use crate::util::linear_scan::UnstraddlableRegion;
13use crate::util::metadata::side_metadata::SideMetadataSpec;
14use crate::util::rc::*;
15use crate::vm::slot::Slot;
16use crate::{
17 plan::concurrent::global::ConcurrentPlan,
18 plan::concurrent::Pause,
19 policy::{immix::block::Block, space::Space},
20 scheduler::{GCWork, GCWorker, WorkBucketStage},
21 util::{metadata::side_metadata, object_forwarding, ObjectReference},
22 vm::*,
23 MMTK,
24};
25use atomic::Ordering;
26use std::ops::{Deref, DerefMut};
27use std::sync::Arc;
28
29pub struct ProcessIncs<VM: VMBinding, const KIND: EdgeKind> {
30 incs: Vec<VM::VMSlot>,
32 new_incs: VectorQueue<VM::VMSlot>,
34 new_incs_count: u32,
35 pause: Pause,
36 in_cm: bool,
37 no_evac: bool,
38 pub root_kind: Option<RootKind>,
39 depth: u32,
40 lxr: &'static LXR<VM>,
41 rc: RefCountHelper<VM>,
42 survival_ratio_predictor_local: SurvivalRatioPredictorLocal,
43}
44
45unsafe impl<VM: VMBinding, const KIND: EdgeKind> Send for ProcessIncs<VM, KIND> {}
46
47impl<VM: VMBinding, const KIND: EdgeKind> ProcessIncs<VM, KIND> {
48 const CAPACITY: usize = 1024;
49 const UNLOG_BITS: SideMetadataSpec = *VM::VMObjectModel::GLOBAL_FIELD_UNLOG_BIT_SPEC
50 .as_spec()
51 .extract_side_spec();
52
53 fn __default(lxr: &'static LXR<VM>) -> Self {
54 Self {
55 incs: vec![],
56 new_incs: VectorQueue::default(),
57 new_incs_count: 0,
58 lxr,
59 pause: Pause::RefCount,
60 in_cm: false,
61 no_evac: false,
62 depth: 1,
63 rc: RefCountHelper::NEW,
64 root_kind: None,
65 survival_ratio_predictor_local: SurvivalRatioPredictorLocal::default(),
66 }
67 }
68
69 fn add_new_slot(&mut self, worker: &mut GCWorker<VM>, s: VM::VMSlot) {
70 self.new_incs.push(s);
71 self.new_incs_count += 1;
72 if self.new_incs_count as usize >= Self::CAPACITY {
73 self.flush(worker);
74 }
75 }
76
77 pub fn new(incs: Vec<VM::VMSlot>, lxr: &'static LXR<VM>) -> Self {
78 Self {
79 incs,
80 ..Self::__default(lxr)
81 }
82 }
83
84 fn promote(
85 &mut self,
86 worker: &mut GCWorker<VM>,
87 o: ObjectReference,
88 copied: bool,
89 los: bool,
90 depth: u32,
91 ) {
92 let size = o.get_size::<VM>();
93
94 if !los {
95 let block = Block::containing(o);
96 let in_nursery_block = block.get_state() == BlockState::Nursery;
97 if !copied && in_nursery_block {
98 block.set_as_in_place_promoted();
99 }
100 self.rc.promote_with_size(o, size);
101 if copied {
102 self.survival_ratio_predictor_local
103 .record_copied_promotion(size);
104 }
105 } else {
106 }
108 if self.in_cm || self.pause == Pause::FinalMark {
110 debug_assert!(self.lxr.is_marked(o), "{:?} is not marked", o);
111 }
112 self.scan_nursery_object(worker, o, los, !copied, depth, size);
113 }
114
115 fn record_mature_evac_remset2(
116 &mut self,
117 slot_in_defrag: bool,
118 s: VM::VMSlot,
119 o: ObjectReference,
120 ) {
121 if !(MATURE_EVACUATION && (self.in_cm || self.pause == Pause::FinalMark)) {
122 return;
123 }
124 if !slot_in_defrag && self.lxr.in_defrag(o) {
125 self.lxr.mature_evac_remset.record(s, o, self.lxr);
126 }
127 }
128
129 fn record_mature_evac_remset(&mut self, s: VM::VMSlot, o: ObjectReference) {
130 if !(MATURE_EVACUATION && (self.in_cm || self.pause == Pause::FinalMark)) {
131 return;
132 }
133 self.record_mature_evac_remset2(self.lxr.address_in_defrag(s.to_address()), s, o);
134 }
135
136 fn scan_nursery_object(
137 &mut self,
138 worker: &mut GCWorker<VM>,
139 o: ObjectReference,
140 los: bool,
141 in_place_promotion: bool,
142 _depth: u32,
143 size: usize,
144 ) {
145 let heap_bytes_per_unlog_byte = if VM::VMObjectModel::COMPRESSED_PTR_ENABLED {
146 32usize
147 } else {
148 64
149 };
150 if los {
151 let start =
152 side_metadata::address_to_meta_address(&Self::UNLOG_BITS, o.to_raw_address())
153 .to_mut_ptr::<u8>();
154 let limit = side_metadata::address_to_meta_address(
155 &Self::UNLOG_BITS,
156 (o.to_raw_address() + size).align_up(heap_bytes_per_unlog_byte),
157 )
158 .to_mut_ptr::<u8>();
159 unsafe {
160 let bytes = limit.offset_from(start) as usize;
161 std::ptr::write_bytes(start, 0xffu8, bytes);
162 }
163 o.to_raw_address().unlog_field_relaxed::<VM>();
164 } else if in_place_promotion {
165 let header_size = if VM::VMObjectModel::COMPRESSED_PTR_ENABLED {
166 12usize
167 } else {
168 16
169 };
170 let step = heap_bytes_per_unlog_byte << 2;
171 let end = o.to_raw_address() + size;
172 let aligned_end = end.align_up(step);
173 let cursor = o.to_raw_address() + header_size;
174 let mut cursor = cursor.align_down(step);
175 let mut meta = side_metadata::address_to_meta_address(&Self::UNLOG_BITS, cursor);
176 while cursor < aligned_end {
177 unsafe { meta.store(0xffffffffu32) }
178 meta += 4usize;
179 cursor += step;
180 }
181 };
182 let obj_in_defrag = !los && Block::in_defrag_block(o);
183 let tls = worker.tls.0;
184 o.iterate_fields::<VM, _>(tls, |slot| {
185 let Some(target) = slot.load() else {
186 return;
187 };
188 debug_assert!(
189 target.to_raw_address().is_mapped(),
190 "Unmapped obj {:?}.{:?} -> {:?}",
191 o,
192 slot,
193 target
194 );
195 debug_assert!(
196 target.is_in_any_space(),
197 "Unmapped obj {:?}.{:?} -> {:?}",
198 o,
199 slot,
200 target
201 );
202 let rc = self.rc.count(target);
203 if rc == 0 {
204 self.add_new_slot(worker, slot);
205 } else {
206 if rc != crate::util::rc::MAX_REF_COUNT {
207 let _ = self.rc.inc(target);
208 }
209 self.record_mature_evac_remset2(obj_in_defrag, slot, target);
210 }
211 });
212 }
213
214 #[cold]
215 fn flush(&mut self, worker: &mut GCWorker<VM>) {
216 if !self.new_incs.is_empty() {
217 let new_incs = self.new_incs.take();
218 let mut w = ProcessIncs::<VM, EDGE_KIND_NURSERY>::new(new_incs, self.lxr);
219 w.depth += 1;
220 worker.add_work(WorkBucketStage::Unconstrained, w);
221 }
222 self.new_incs_count = 0;
223 }
224
225 fn inc(&self, o: ObjectReference) -> bool {
227 self.rc.inc(o) == Ok(0)
228 }
229
230 fn dont_evacuate(&self, o: ObjectReference, los: bool) -> bool {
231 if los {
232 return true;
233 }
234 if self.rc.count(o) != 0 {
236 return true;
237 }
238 if Block::containing(o).get_state() != BlockState::Nursery {
240 return true;
241 }
242 if cfg!(debug_assertions) {
243 let cls = unsafe { (o.to_raw_address() + 8usize).load::<u32>() };
244 assert!(cls != 0, "ERROR {:?} rc={}", o, self.rc.count(o));
245 }
246 false
247 }
248
249 fn process_inc_and_evacuate(
250 &mut self,
251 worker: &mut GCWorker<VM>,
252 o: ObjectReference,
253 depth: u32,
254 ) -> ObjectReference {
255 let los = self.lxr.los().in_space(o);
256 if NURSERY_EVACUATION && !los && object_forwarding::is_forwarded_or_being_forwarded::<VM>(o)
257 {
258 while object_forwarding::is_being_forwarded::<VM>(o) {
259 std::hint::spin_loop();
260 }
261 let new = if object_forwarding::is_forwarded::<VM>(o) {
262 object_forwarding::read_forwarding_pointer::<VM>(o)
263 } else {
264 o
265 };
266 let promoted = self.inc(new);
267 if promoted && new == o {
268 self.promote(worker, o, false, los, depth);
269 }
270 return new;
271 }
272 if !NURSERY_EVACUATION || self.dont_evacuate(o, los) {
273 if self.inc(o) {
274 self.promote(worker, o, false, los, depth);
275 }
276 return o;
277 }
278 let forwarding_status = object_forwarding::attempt_to_forward::<VM>(o);
279 if object_forwarding::state_is_forwarded_or_being_forwarded(forwarding_status) {
280 let new = object_forwarding::spin_and_get_forwarded_object::<VM>(o, forwarding_status);
282 self.inc(new);
283 new
284 } else {
285 let is_nursery = self.rc.count(o) == 0;
286 if is_nursery && !self.no_evac {
287 let new = object_forwarding::try_forward_object::<VM>(
289 o,
290 CopySemantics::DefaultCopy,
291 worker.get_copy_context_mut(),
292 |_new| {
293 #[cfg(feature = "vo_bit")]
294 {
295 crate::util::metadata::vo_bit::set_vo_bit(_new);
297 crate::util::metadata::vo_bit::unset_vo_bit(o);
302 }
303 },
304 );
305 if let Some(new) = new {
306 self.inc(new);
307 self.promote(worker, new, true, false, depth);
308 new
309 } else {
310 warn!("to-space overflow");
311 let promoted = self.inc(o);
313 object_forwarding::clear_forwarding_bits::<VM>(o);
314 if promoted {
315 self.promote(worker, o, false, los, depth);
316 }
317 NO_EVAC.store(true, Ordering::Relaxed);
318 self.no_evac = true;
319 o
320 }
321 } else {
322 let promoted = self.inc(o);
324 object_forwarding::clear_forwarding_bits::<VM>(o);
325 if promoted {
326 self.promote(worker, o, false, los, depth);
327 }
328 o
329 }
330 }
331 }
332
333 fn unlog_and_load_rc_object<const K: EdgeKind>(
335 &mut self,
336 s: VM::VMSlot,
337 ) -> Option<ObjectReference> {
338 let o = s.load();
339 if K == EDGE_KIND_MATURE {
341 s.to_address().unlog_field_relaxed::<VM>();
342 }
343 o
344 }
345
346 fn process_slot<const K: EdgeKind>(
347 &mut self,
348 worker: &mut GCWorker<VM>,
349 s: VM::VMSlot,
350 depth: u32,
351 add_root_to_remset: bool,
352 ) -> Option<ObjectReference> {
353 let o = match self.unlog_and_load_rc_object::<K>(s) {
354 Some(o) => o,
355 _ => {
356 return None;
357 }
358 };
359 let new = self.process_inc_and_evacuate(worker, o, depth);
361 if K != EDGE_KIND_ROOT || add_root_to_remset {
363 self.record_mature_evac_remset(s, new);
364 }
365 if new != o {
366 s.store(new)
367 }
368 Some(new)
369 }
370
371 fn process_incs<const K: EdgeKind>(
372 &mut self,
373 worker: &mut GCWorker<VM>,
374 mut incs: AddressBuffer<'_, VM::VMSlot>,
375 depth: u32,
376 add_root_to_remset: bool,
377 ) -> Option<Vec<ObjectReference>> {
378 if K == EDGE_KIND_ROOT {
379 let roots = incs.as_mut_ptr() as *mut ObjectReference;
380 let mut num_roots = 0usize;
381 for s in incs.iter() {
382 if let Some(new) = self.process_slot::<K>(worker, *s, depth, add_root_to_remset) {
383 unsafe {
384 roots.add(num_roots).write(new);
385 }
386 num_roots += 1;
387 }
388 }
389 if num_roots != 0 {
390 let cap = incs.capacity();
391 std::mem::forget(incs); let roots =
393 unsafe { Vec::<ObjectReference>::from_raw_parts(roots, num_roots, cap) };
394 Some(roots)
395 } else {
396 None
397 }
398 } else {
399 for s in incs.iter() {
400 self.process_slot::<K>(worker, *s, depth, false);
401 }
402 None
403 }
404 }
405}
406
407pub type EdgeKind = u8;
408pub const EDGE_KIND_ROOT: u8 = 0;
409pub const EDGE_KIND_NURSERY: u8 = 1;
410pub const EDGE_KIND_MATURE: u8 = 2;
411
412enum AddressBuffer<'a, S: Slot> {
413 Owned(Vec<S>),
414 Ref(&'a mut Vec<S>),
415}
416
417impl<S: Slot> Deref for AddressBuffer<'_, S> {
418 type Target = Vec<S>;
419 fn deref(&self) -> &Self::Target {
420 match self {
421 Self::Owned(x) => x,
422 Self::Ref(x) => x,
423 }
424 }
425}
426
427impl<S: Slot> DerefMut for AddressBuffer<'_, S> {
428 fn deref_mut(&mut self) -> &mut Self::Target {
429 match self {
430 Self::Owned(x) => x,
431 Self::Ref(x) => x,
432 }
433 }
434}
435
436impl<VM: VMBinding, const KIND: EdgeKind> GCWork<VM> for ProcessIncs<VM, KIND> {
437 fn do_work(&mut self, worker: &mut GCWorker<VM>, mmtk: &'static MMTK<VM>) {
438 self.lxr = mmtk.get_plan().downcast_ref::<LXR<VM>>().unwrap();
439 self.pause = self.lxr.current_pause().unwrap();
440 self.in_cm = self.lxr.concurrent_work_in_progress();
441 if NO_EVAC.load(Ordering::Relaxed) {
442 self.no_evac = true;
443 } else {
444 let over_space = mmtk.get_plan().get_used_pages()
445 - mmtk.get_plan().get_collection_reserved_pages()
446 > mmtk.get_plan().get_total_pages();
447 if over_space {
448 self.no_evac = true;
449 NO_EVAC.store(true, Ordering::Relaxed);
450 }
451 }
452 let root_slots = if KIND == EDGE_KIND_ROOT
454 && (self.pause == Pause::FinalMark || self.pause == Pause::Full)
455 {
456 self.incs.clone()
457 } else {
458 vec![]
459 };
460 let roots = {
461 let incs = std::mem::take(&mut self.incs);
462 self.process_incs::<KIND>(worker, AddressBuffer::Owned(incs), self.depth, false)
463 };
464 if let Some(roots) = roots {
465 if self.lxr.cm_enabled()
466 && self.pause == Pause::InitialMark
467 && !self.root_kind.unwrap().should_skip_mark_and_decs()
468 {
469 if cfg!(any(feature = "sanity", debug_assertions)) {
470 for r in &roots {
471 assert!(
472 r.to_raw_address().is_mapped(),
473 "Invalid object {:?}: address is not mapped",
474 r
475 );
476 }
477 }
478 worker.scheduler().work_buckets[WorkBucketStage::ConcurrentResumable]
479 .add(LXRConcurrentTraceObjects::new(roots.clone(), mmtk));
480 }
481 if self.pause == Pause::FinalMark || self.pause == Pause::Full {
482 if !root_slots.is_empty() && self.root_kind != Some(RootKind::Weak) {
483 if self.pause == Pause::FinalMark {
484 let mut w = LXRStopTheWorldProcessEdges::<_, false>::new(
485 root_slots,
486 true,
487 mmtk,
488 WorkBucketStage::Closure,
489 );
490 w.root_kind = self.root_kind;
491 worker.add_work(WorkBucketStage::Closure, w)
492 } else {
493 let mut w = LXRStopTheWorldProcessEdges::<_, true>::new(
494 root_slots,
495 true,
496 mmtk,
497 WorkBucketStage::Closure,
498 );
499 w.root_kind = self.root_kind;
500 worker.add_work(WorkBucketStage::Closure, w)
501 };
502 }
503 } else if !self.root_kind.unwrap().should_skip_decs() {
504 self.lxr.curr_roots.read().unwrap().push(roots);
505 }
506 }
507 let mut depth = self.depth;
509 let mut incs = vec![];
510 const ACTIVE_PACKET_SPLIT: bool = false;
511 while !self.new_incs.is_empty() {
512 self.new_incs_count = 0;
513 depth += 1;
514 incs.clear();
515 self.new_incs.swap(&mut incs);
516 if ACTIVE_PACKET_SPLIT && depth >= 16 && incs.len() > 1 {
517 let (a, b) = incs.split_at(incs.len() / 2);
518 let mut w = ProcessIncs::<VM, EDGE_KIND_NURSERY>::new(b.to_vec(), self.lxr);
519 w.depth = depth;
520 worker.add_work(WorkBucketStage::Unconstrained, w);
521 incs = a.to_vec();
522 }
523 if !incs.is_empty() {
524 self.process_incs::<EDGE_KIND_NURSERY>(
525 worker,
526 AddressBuffer::Ref(&mut incs),
527 depth,
528 false,
529 );
530 }
531 }
532 self.survival_ratio_predictor_local.sync();
533 }
534}
535
536pub struct ProcessDecs<VM: VMBinding> {
537 decs: Option<Vec<ObjectReference>>,
539 decs_arc: Option<Arc<Vec<ObjectReference>>>,
540 new_decs: VectorQueue<ObjectReference>,
542 counter: LazySweepingJobsCounter,
543 mark_objects: VectorQueue<ObjectReference>,
544 mark_dead_objects: bool,
545 mature_sweeping_in_progress: bool,
546 rc: RefCountHelper<VM>,
547}
548
549impl<VM: VMBinding> ProcessDecs<VM> {
550 pub fn new(decs: Vec<ObjectReference>, counter: LazySweepingJobsCounter) -> Self {
551 Self {
552 decs: Some(decs),
553 decs_arc: None,
554 new_decs: VectorQueue::default(),
555 counter,
556 mark_objects: VectorQueue::default(),
557 mark_dead_objects: false,
558 mature_sweeping_in_progress: false,
559 rc: RefCountHelper::NEW,
560 }
561 }
562
563 pub fn new_arc(decs: Arc<Vec<ObjectReference>>, counter: LazySweepingJobsCounter) -> Self {
564 Self {
565 decs: None,
566 decs_arc: Some(decs),
567 new_decs: VectorQueue::default(),
568 counter,
569 mark_objects: VectorQueue::default(),
570 mark_dead_objects: false,
571 mature_sweeping_in_progress: false,
572 rc: RefCountHelper::NEW,
573 }
574 }
575
576 fn recursive_dec(&mut self, worker: &mut GCWorker<VM>, o: ObjectReference) {
577 self.new_decs.push(o);
578 if self.new_decs.is_full() {
579 self.flush(worker)
580 }
581 }
582
583 fn new_work(&self, worker: &mut GCWorker<VM>, w: ProcessDecs<VM>) {
584 worker.add_work(WorkBucketStage::Unconstrained, w);
585 }
586
587 fn flush(&mut self, worker: &mut GCWorker<VM>) {
588 let mmtk = worker.mmtk;
589 if !self.new_decs.is_empty() {
590 let new_decs = self.new_decs.take();
591 self.new_work(
592 worker,
593 ProcessDecs::new(new_decs, self.counter.clone_with_decs()),
594 );
595 }
596 if !self.mark_objects.is_empty() {
597 let objects = self.mark_objects.take();
598 let w = LXRConcurrentTraceObjects::new(objects, mmtk);
599 if LAZY_DECREMENTS {
600 worker.add_work(WorkBucketStage::Unconstrained, w);
601 } else {
602 worker.scheduler().work_buckets[WorkBucketStage::ConcurrentResumable].add(w);
603 }
604 }
605 }
606
607 #[cold]
608 fn process_dead_object(
609 &mut self,
610 worker: &mut GCWorker<VM>,
611 o: ObjectReference,
612 lxr: &LXR<VM>,
613 ) -> bool {
614 if self.mark_dead_objects {
615 lxr.mark(o);
616 }
617 let tls = worker.tls.0;
619 o.iterate_fields::<VM, _>(tls, |slot| {
620 if let Some(x) = slot.load() {
621 let rc = self.rc.count(x);
623 if rc != MAX_REF_COUNT && rc != 0 {
624 self.recursive_dec(worker, x);
625 }
626 if self.mark_dead_objects && !lxr.is_marked(x) {
627 if cfg!(any(feature = "sanity", debug_assertions)) {
628 assert!(
629 x.to_raw_address().is_mapped(),
630 "Invalid object {:?}.{:?} -> {:?}: address is not mapped",
631 o,
632 slot,
633 x
634 );
635 }
636 self.mark_objects.push(x);
637 if self.mark_objects.is_full() {
638 self.flush(worker);
639 }
640 }
641 }
642 });
643 let in_ix_space = lxr.immix_space.in_space(o);
644 if in_ix_space {
645 #[cfg(feature = "vo_bit")]
649 crate::util::metadata::vo_bit::unset_vo_bit(o);
650
651 self.rc.unmark_straddle_object(o);
652 }
653 if RefCountHelper::<VM>::SANITY {
654 unsafe { o.to_raw_address().store(0xdeadusize) };
655 }
656 if in_ix_space {
657 let block = Block::containing(o);
658 lxr.add_to_possibly_dead_mature_blocks(block, false);
659 false
660 } else {
661 true
662 }
663 }
664
665 fn process_decs(&mut self, worker: &mut GCWorker<VM>, decs: &[ObjectReference], lxr: &LXR<VM>) {
666 for o in decs.iter() {
667 if self.rc.is_dead_or_stuck(*o)
668 || (self.mature_sweeping_in_progress && !lxr.is_marked(*o))
669 {
670 continue;
671 }
672 let o = if MATURE_EVACUATION && object_forwarding::is_forwarded::<VM>(*o) {
673 object_forwarding::read_forwarding_pointer::<VM>(*o)
674 } else {
675 *o
676 };
677 let mut dead = false;
678 let mut is_los = false;
679 let mut already_run = false;
680 let result = self.rc.clone().fetch_update(o, |c| {
681 if already_run {
682 log::warn!("fetch_update is re-run! o: {o}");
683 } else {
684 already_run = true;
685 }
686 if c == 1 && !dead {
687 dead = true;
688 is_los = self.process_dead_object(worker, o, lxr);
689 }
690 debug_assert!(c <= MAX_REF_COUNT);
691 if c == 0 || c == MAX_REF_COUNT {
692 None } else {
694 Some(c - 1)
695 }
696 });
697 if result == Ok(1) && is_los {
698 lxr.los().rc_free(o);
699 }
700 }
701 }
702}
703
704impl<VM: VMBinding> GCWork<VM> for ProcessDecs<VM> {
705 fn do_work(&mut self, worker: &mut GCWorker<VM>, mmtk: &'static MMTK<VM>) {
706 let lxr = mmtk.get_plan().downcast_ref::<LXR<VM>>().unwrap();
707 self.mark_dead_objects = if LAZY_DECREMENTS {
708 lxr.concurrent_work_in_progress() && lxr.previous_pause() != Some(Pause::InitialMark)
709 } else {
710 lxr.concurrent_work_in_progress() && lxr.current_pause() != Some(Pause::InitialMark)
711 };
712 self.mature_sweeping_in_progress = if LAZY_DECREMENTS {
713 lxr.previous_pause() == Some(Pause::FinalMark)
714 || lxr.current_pause() == Some(Pause::Full)
715 } else {
716 lxr.current_pause() == Some(Pause::FinalMark)
717 || lxr.current_pause() == Some(Pause::Full)
718 };
719 if let Some(decs) = std::mem::take(&mut self.decs) {
720 self.process_decs(worker, &decs, lxr);
721 } else if let Some(decs) = std::mem::take(&mut self.decs_arc) {
722 self.process_decs(worker, &decs, lxr);
723 }
724 let mut decs = vec![];
725 while !self.new_decs.is_empty() {
726 decs.clear();
727 self.new_decs.swap(&mut decs);
728 self.process_decs(worker, &decs, lxr);
729 }
730 self.flush(worker);
731 }
732}
733
734pub struct CollectRoots<VM: VMBinding> {
735 base: ProcessEdgesBase<VM>,
736}
737
738impl<VM: VMBinding> CollectRoots<VM> {
739 pub fn new(
740 slots: Vec<VM::VMSlot>,
741 roots: bool,
742 mmtk: &'static MMTK<VM>,
743 bucket: WorkBucketStage,
744 ) -> Self {
745 debug_assert!(roots);
746 let base = ProcessEdgesBase::new(slots, roots, mmtk, bucket);
747 Self { base }
748 }
749}
750
751impl<VM: VMBinding> GCWork<VM> for CollectRoots<VM> {
752 fn do_work(&mut self, worker: &mut GCWorker<VM>, _mmtk: &'static MMTK<VM>) {
753 self.set_worker(worker);
754 if !self.slots.is_empty() {
755 let lxr = self.mmtk().get_plan().downcast_ref::<LXR<VM>>().unwrap();
756 let roots = std::mem::take(&mut self.slots);
757 let mut w = ProcessIncs::<_, EDGE_KIND_ROOT>::new(roots, lxr);
758 w.root_kind = self.root_kind;
759 GCWork::do_work(&mut w, self.worker(), self.mmtk());
760 }
761 }
762}
763
764impl<VM: VMBinding> Deref for CollectRoots<VM> {
765 type Target = ProcessEdgesBase<VM>;
766 fn deref(&self) -> &Self::Target {
767 &self.base
768 }
769}
770
771impl<VM: VMBinding> DerefMut for CollectRoots<VM> {
772 fn deref_mut(&mut self) -> &mut Self::Target {
773 &mut self.base
774 }
775}