1use std::collections::HashSet;
2use std::sync::atomic::AtomicBool;
3use std::sync::atomic::Ordering;
4use std::sync::Mutex;
5use std::vec::Vec;
6
7use crate::plan::is_nursery_gc;
8use crate::plan::tracing::gc_work::DefaultObjectTracerContext;
9use crate::plan::tracing::Trace;
10use crate::scheduler::WorkBucketStage;
11use crate::util::ObjectReference;
12use crate::util::VMWorkerThread;
13use crate::vm::ObjectTracer;
14use crate::vm::ObjectTracerContext;
15use crate::vm::ReferenceGlue;
16use crate::vm::VMBinding;
17
18pub struct ReferenceProcessors {
22 soft: ReferenceProcessor,
23 weak: ReferenceProcessor,
24 phantom: ReferenceProcessor,
25}
26
27impl ReferenceProcessors {
28 pub fn new() -> Self {
29 ReferenceProcessors {
30 soft: ReferenceProcessor::new(Semantics::SOFT),
31 weak: ReferenceProcessor::new(Semantics::WEAK),
32 phantom: ReferenceProcessor::new(Semantics::PHANTOM),
33 }
34 }
35
36 pub fn get(&self, semantics: Semantics) -> &ReferenceProcessor {
37 match semantics {
38 Semantics::SOFT => &self.soft,
39 Semantics::WEAK => &self.weak,
40 Semantics::PHANTOM => &self.phantom,
41 }
42 }
43
44 pub fn add_soft_candidate(&self, reff: ObjectReference) {
45 trace!("Add soft candidate: {}", reff);
46 self.soft.add_candidate(reff);
47 }
48
49 pub fn add_weak_candidate(&self, reff: ObjectReference) {
50 trace!("Add weak candidate: {}", reff);
51 self.weak.add_candidate(reff);
52 }
53
54 pub fn add_phantom_candidate(&self, reff: ObjectReference) {
55 trace!("Add phantom candidate: {}", reff);
56 self.phantom.add_candidate(reff);
57 }
58
59 pub fn enqueue_refs<VM: VMBinding>(&self, tls: VMWorkerThread) {
63 self.soft.enqueue::<VM>(tls);
64 self.weak.enqueue::<VM>(tls);
65 self.phantom.enqueue::<VM>(tls);
66 }
67
68 pub fn forward_refs<VM: VMBinding, OT: ObjectTracer>(
73 &self,
74 trace: &mut OT,
75 mmtk: &'static MMTK<VM>,
76 ) {
77 debug_assert!(
78 mmtk.get_plan().constraints().needs_forward_after_liveness,
79 "A plan with needs_forward_after_liveness=false does not need a separate forward step"
80 );
81 self.soft
82 .forward::<VM, OT>(trace, is_nursery_gc(mmtk.get_plan()));
83 self.weak
84 .forward::<VM, OT>(trace, is_nursery_gc(mmtk.get_plan()));
85 self.phantom
86 .forward::<VM, OT>(trace, is_nursery_gc(mmtk.get_plan()));
87 }
88
89 pub fn retain_soft_refs<VM: VMBinding, OT: ObjectTracer>(
92 &self,
93 trace: &mut OT,
94 mmtk: &'static MMTK<VM>,
95 ) {
96 self.soft
97 .retain::<VM, OT>(trace, is_nursery_gc(mmtk.get_plan()));
98 }
99
100 pub fn scan_soft_refs<VM: VMBinding>(&self, mmtk: &'static MMTK<VM>) {
102 self.soft.scan::<VM>(is_nursery_gc(mmtk.get_plan()));
104 }
105
106 pub fn scan_weak_refs<VM: VMBinding>(&self, mmtk: &'static MMTK<VM>) {
108 self.weak.scan::<VM>(is_nursery_gc(mmtk.get_plan()));
109 }
110
111 pub fn scan_phantom_refs<VM: VMBinding>(&self, mmtk: &'static MMTK<VM>) {
113 self.phantom.scan::<VM>(is_nursery_gc(mmtk.get_plan()));
114 }
115}
116
117impl Default for ReferenceProcessors {
118 fn default() -> Self {
119 Self::new()
120 }
121}
122
123const INITIAL_SIZE: usize = 256;
131
132pub struct ReferenceProcessor {
140 sync: Mutex<ReferenceProcessorSync>,
142
143 semantics: Semantics,
145
146 allow_new_candidate: AtomicBool,
159}
160
161#[derive(Debug, PartialEq, Clone, Copy)]
162pub enum Semantics {
163 SOFT,
164 WEAK,
165 PHANTOM,
166}
167
168struct ReferenceProcessorSync {
169 references: HashSet<ObjectReference>,
175
176 enqueued_references: Vec<ObjectReference>,
179
180 nursery_index: usize,
182}
183
184impl ReferenceProcessor {
185 pub fn new(semantics: Semantics) -> Self {
186 ReferenceProcessor {
187 sync: Mutex::new(ReferenceProcessorSync {
188 references: HashSet::with_capacity(INITIAL_SIZE),
189 enqueued_references: vec![],
190 nursery_index: 0,
191 }),
192 semantics,
193 allow_new_candidate: AtomicBool::new(true),
194 }
195 }
196
197 pub fn add_candidate(&self, reff: ObjectReference) {
199 if !self.allow_new_candidate.load(Ordering::SeqCst) {
200 return;
201 }
202
203 let mut sync = self.sync.lock().unwrap();
204 sync.references.insert(reff);
205 }
206
207 fn disallow_new_candidate(&self) {
208 self.allow_new_candidate.store(false, Ordering::SeqCst);
209 }
210
211 fn allow_new_candidate(&self) {
212 self.allow_new_candidate.store(true, Ordering::SeqCst);
213 }
214
215 fn get_forwarded_referent(referent: ObjectReference) -> ObjectReference {
224 debug_assert!(referent.is_live());
225 referent.get_forwarded_object().unwrap_or(referent)
226 }
227
228 fn get_forwarded_reference(object: ObjectReference) -> ObjectReference {
232 debug_assert!(object.is_live());
233 object.get_forwarded_object().unwrap_or(object)
234 }
235
236 fn keep_referent_alive<OT: ObjectTracer>(
248 tracer: &mut OT,
249 referent: ObjectReference,
250 ) -> ObjectReference {
251 tracer.trace_object(referent)
252 }
253
254 fn trace_forward_object<OT: ObjectTracer>(
259 tracer: &mut OT,
260 referent: ObjectReference,
261 ) -> ObjectReference {
262 tracer.trace_object(referent)
263 }
264
265 pub fn enqueue<VM: VMBinding>(&self, tls: VMWorkerThread) {
267 self.disallow_new_candidate();
272 let mut sync = self.sync.lock().unwrap();
273
274 #[cfg(debug_assertions)]
276 {
277 sync.references.iter().for_each(|reff| {
279 debug_assert!(reff.is_in_any_space());
280 if let Some(referent) = VM::VMReferenceGlue::get_referent(*reff) {
281 debug_assert!(
282 referent.is_in_any_space(),
283 "Referent {:?} (of reference {:?}) is not in any space",
284 referent,
285 reff
286 );
287 }
288 });
289 sync.enqueued_references.iter().for_each(|reff| {
291 debug_assert!(reff.is_in_any_space());
292 let maybe_referent = VM::VMReferenceGlue::get_referent(*reff);
293 debug_assert!(maybe_referent.is_none());
294 });
295 }
296
297 if !sync.enqueued_references.is_empty() {
298 trace!("enqueue: {:?}", sync.enqueued_references);
299 VM::VMReferenceGlue::enqueue_references(&sync.enqueued_references, tls);
300 sync.enqueued_references.clear();
301 }
302
303 self.allow_new_candidate();
304 }
305
306 pub fn forward<VM: VMBinding, OT: ObjectTracer>(&self, trace: &mut OT, _nursery: bool) {
310 let mut sync = self.sync.lock().unwrap();
311 debug!("Starting ReferenceProcessor.forward({:?})", self.semantics);
312
313 fn forward_reference<VM: VMBinding, OT: ObjectTracer>(
315 trace: &mut OT,
316 reference: ObjectReference,
317 ) -> ObjectReference {
318 {
319 use crate::vm::ObjectModel;
320 trace!(
321 "Forwarding reference: {} (size: {})",
322 reference,
323 <VM as VMBinding>::VMObjectModel::get_current_size(reference)
324 );
325 }
326
327 if let Some(old_referent) = <VM as VMBinding>::VMReferenceGlue::get_referent(reference)
328 {
329 let new_referent = ReferenceProcessor::trace_forward_object(trace, old_referent);
330 <VM as VMBinding>::VMReferenceGlue::set_referent(reference, new_referent);
331
332 trace!(
333 " referent: {} (forwarded to {})",
334 old_referent,
335 new_referent
336 );
337 }
338
339 let new_reference = ReferenceProcessor::trace_forward_object(trace, reference);
340 trace!(" reference: forwarded to {}", new_reference);
341
342 new_reference
343 }
344
345 sync.references = sync
346 .references
347 .iter()
348 .map(|reff| forward_reference::<VM, OT>(trace, *reff))
349 .collect();
350
351 sync.enqueued_references = sync
352 .enqueued_references
353 .iter()
354 .map(|reff| forward_reference::<VM, OT>(trace, *reff))
355 .collect();
356
357 debug!("Ending ReferenceProcessor.forward({:?})", self.semantics);
358
359 self.disallow_new_candidate();
361 }
362
363 fn scan<VM: VMBinding>(&self, _nursery: bool) {
369 let mut sync = self.sync.lock().unwrap();
370
371 debug!("Starting ReferenceProcessor.scan({:?})", self.semantics);
372
373 trace!(
374 "{:?} Reference table is {:?}",
375 self.semantics,
376 sync.references
377 );
378
379 let mut enqueued_references = vec![];
382
383 let new_set: HashSet<ObjectReference> = sync
385 .references
386 .iter()
387 .filter_map(|reff| self.process_reference::<VM>(*reff, &mut enqueued_references))
388 .collect();
389
390 let num_old = sync.references.len();
391 let num_new = new_set.len();
392 let num_enqueued = enqueued_references.len();
393
394 debug!(
395 "{:?} reference table from {} to {} ({} enqueued)",
396 self.semantics, num_old, num_new, num_enqueued,
397 );
398
399 let semantics_int = self.semantics as usize;
400
401 probe!(
402 mmtk,
403 reference_scanned,
404 semantics_int,
405 num_old,
406 num_new,
407 num_enqueued
408 );
409
410 sync.references = new_set;
411 sync.enqueued_references.extend(enqueued_references);
412
413 debug!("Ending ReferenceProcessor.scan({:?})", self.semantics);
414 }
415
416 fn retain<VM: VMBinding, OT: ObjectTracer>(&self, trace: &mut OT, _nursery: bool) {
421 debug_assert!(self.semantics == Semantics::SOFT);
422
423 let sync = self.sync.lock().unwrap();
424
425 debug!("Starting ReferenceProcessor.retain({:?})", self.semantics);
426 trace!(
427 "{:?} Reference table is {:?}",
428 self.semantics,
429 sync.references
430 );
431
432 let num_refs = sync.references.len();
433 let mut num_live = 0usize;
434 let mut num_retained = 0usize;
435
436 for reference in sync.references.iter() {
437 trace!("Processing reference: {:?}", reference);
438
439 if !reference.is_live() {
440 continue;
443 }
444 num_live += 1;
445 if let Some(referent) = VM::VMReferenceGlue::get_referent(*reference) {
447 Self::keep_referent_alive(trace, referent);
448 num_retained += 1;
449 trace!(" ~> {:?} (retained)", referent);
450 }
451 }
452
453 probe!(mmtk, reference_retained, num_refs, num_live, num_retained,);
454
455 debug!("Ending ReferenceProcessor.retain({:?})", self.semantics);
456 }
457
458 fn process_reference<VM: VMBinding>(
466 &self,
467 reference: ObjectReference,
468 enqueued_references: &mut Vec<ObjectReference>,
469 ) -> Option<ObjectReference> {
470 trace!("Process reference: {}", reference);
471
472 if !reference.is_live() {
475 VM::VMReferenceGlue::clear_referent(reference);
476 trace!(" UNREACHABLE reference: {}", reference);
477 return None;
478 }
479
480 let new_reference = Self::get_forwarded_reference(reference);
482 trace!(" forwarded to: {}", new_reference);
483
484 let maybe_old_referent = VM::VMReferenceGlue::get_referent(reference);
486 trace!(" referent: {:?}", maybe_old_referent);
487
488 let Some(old_referent) = maybe_old_referent else {
493 trace!(" (cleared referent) ");
494 return None;
495 };
496
497 if old_referent.is_live() {
498 let new_referent = Self::get_forwarded_referent(old_referent);
501 debug_assert!(new_referent.is_live());
502 trace!(" forwarded referent to: {}", new_referent);
503
504 VM::VMReferenceGlue::set_referent(new_reference, new_referent);
512 Some(new_reference)
513 } else {
514 trace!(" UNREACHABLE referent: {}", old_referent);
516
517 VM::VMReferenceGlue::clear_referent(new_reference);
518 enqueued_references.push(new_reference);
519 None
520 }
521 }
522}
523
524use crate::scheduler::GCWork;
525use crate::scheduler::GCWorker;
526use crate::MMTK;
527use std::marker::PhantomData;
528
529#[derive(Default)]
530pub(crate) struct RescanReferences<VM: VMBinding> {
531 pub soft: bool,
532 pub weak: bool,
533 pub phantom_data: PhantomData<VM>,
534}
535
536impl<VM: VMBinding> GCWork<VM> for RescanReferences<VM> {
537 fn do_work(&mut self, _worker: &mut GCWorker<VM>, mmtk: &'static MMTK<VM>) {
538 if self.soft {
539 mmtk.reference_processors.scan_soft_refs(mmtk);
540 }
541 if self.weak {
542 mmtk.reference_processors.scan_weak_refs(mmtk);
543 }
544 }
545}
546
547#[derive(Default)]
548pub(crate) struct SoftRefProcessing<T: Trace>(PhantomData<T>);
549impl<T: Trace> GCWork<T::VM> for SoftRefProcessing<T> {
550 fn do_work(&mut self, worker: &mut GCWorker<T::VM>, mmtk: &'static MMTK<T::VM>) {
551 if !mmtk.state.is_emergency_collection() {
552 let rescan = Box::new(RescanReferences {
555 soft: true,
556 weak: false,
557 phantom_data: PhantomData,
558 });
559 worker.scheduler().work_buckets[WorkBucketStage::SoftRefClosure].set_sentinel(rescan);
560
561 let tracer_context =
563 DefaultObjectTracerContext::<T>::new(WorkBucketStage::SoftRefClosure);
564 tracer_context.with_tracer(worker, |tracer| {
565 mmtk.reference_processors.retain_soft_refs(tracer, mmtk);
566 });
567 } else {
568 mmtk.reference_processors.scan_soft_refs(mmtk);
570 }
571 }
572}
573
574impl<T: Trace> SoftRefProcessing<T> {
575 pub fn new() -> Self {
576 Self(PhantomData)
577 }
578}
579
580#[derive(Default)]
581pub(crate) struct WeakRefProcessing<VM: VMBinding>(PhantomData<VM>);
582impl<VM: VMBinding> GCWork<VM> for WeakRefProcessing<VM> {
583 fn do_work(&mut self, _worker: &mut GCWorker<VM>, mmtk: &'static MMTK<VM>) {
584 mmtk.reference_processors.scan_weak_refs(mmtk);
585 }
586}
587impl<VM: VMBinding> WeakRefProcessing<VM> {
588 pub fn new() -> Self {
589 Self(PhantomData)
590 }
591}
592
593#[derive(Default)]
594pub(crate) struct PhantomRefProcessing<VM: VMBinding>(PhantomData<VM>);
595impl<VM: VMBinding> GCWork<VM> for PhantomRefProcessing<VM> {
596 fn do_work(&mut self, _worker: &mut GCWorker<VM>, mmtk: &'static MMTK<VM>) {
597 mmtk.reference_processors.scan_phantom_refs(mmtk);
598 }
599}
600impl<VM: VMBinding> PhantomRefProcessing<VM> {
601 pub fn new() -> Self {
602 Self(PhantomData)
603 }
604}
605
606#[derive(Default)]
607pub(crate) struct RefForwarding<T: Trace>(PhantomData<T>);
608impl<T: Trace> GCWork<T::VM> for RefForwarding<T> {
609 fn do_work(&mut self, worker: &mut GCWorker<T::VM>, mmtk: &'static MMTK<T::VM>) {
610 let tracer_context = DefaultObjectTracerContext::<T>::new(WorkBucketStage::RefForwarding);
611 tracer_context.with_tracer(worker, |tracer| {
612 mmtk.reference_processors.forward_refs(tracer, mmtk);
613 });
614 }
615}
616impl<T: Trace> RefForwarding<T> {
617 pub fn new() -> Self {
618 Self(PhantomData)
619 }
620}
621
622#[derive(Default)]
623pub(crate) struct RefEnqueue<VM: VMBinding>(PhantomData<VM>);
624impl<VM: VMBinding> GCWork<VM> for RefEnqueue<VM> {
625 fn do_work(&mut self, worker: &mut GCWorker<VM>, mmtk: &'static MMTK<VM>) {
626 mmtk.reference_processors.enqueue_refs::<VM>(worker.tls);
627 }
628}
629impl<VM: VMBinding> RefEnqueue<VM> {
630 pub fn new() -> Self {
631 Self(PhantomData)
632 }
633}