mmtk/util/
finalizable_processor.rs

1use crate::plan::is_nursery_gc;
2use crate::plan::tracing::gc_work::DefaultObjectTracerContext;
3use crate::plan::tracing::Trace;
4use crate::scheduler::{GCWork, GCWorker, WorkBucketStage};
5use crate::util::reference_processor::RescanReferences;
6use crate::util::ObjectReference;
7use crate::util::VMWorkerThread;
8use crate::vm::{Collection, ObjectTracer, VMBinding};
9use crate::vm::{Finalizable, ObjectTracerContext};
10use crate::MMTK;
11use std::marker::PhantomData;
12
13/// A special processor for Finalizable objects.
14// TODO: we should consider if we want to merge FinalizableProcessor with ReferenceProcessor,
15// and treat final reference as a special reference type in ReferenceProcessor.
16#[derive(Default)]
17pub struct FinalizableProcessor<F: Finalizable> {
18    /// Candidate objects that has finalizers with them
19    candidates: Vec<F>,
20    /// Index into candidates to record where we are up to in the last scan of the candidates.
21    /// Index after nursery_index are new objects inserted after the last GC.
22    nursery_index: usize,
23    /// Objects that can be finalized. They are actually dead, but we keep them alive
24    /// until the binding pops them from the queue.
25    ready_for_finalize: Vec<F>,
26}
27
28impl<F: Finalizable> FinalizableProcessor<F> {
29    pub fn new() -> Self {
30        Self {
31            candidates: vec![],
32            nursery_index: 0,
33            ready_for_finalize: vec![],
34        }
35    }
36
37    pub fn add(&mut self, object: F) {
38        self.candidates.push(object);
39    }
40
41    fn forward_finalizable_reference<OT: ObjectTracer>(tracer: &mut OT, finalizable: &mut F) {
42        finalizable.keep_alive::<OT>(tracer);
43    }
44
45    pub fn scan<VM: VMBinding, OT: ObjectTracer>(
46        &mut self,
47        tls: VMWorkerThread,
48        tracer: &mut OT,
49        nursery: bool,
50    ) {
51        let start = if nursery { self.nursery_index } else { 0 };
52
53        // We should go through ready_for_finalize objects and keep them alive.
54        // Unlike candidates, those objects are known to be alive. This means
55        // theoratically we could do the following loop at any time in a GC (not necessarily after closure phase).
56        // But we have to iterate through candidates after closure.
57        self.candidates.append(&mut self.ready_for_finalize);
58        debug_assert!(self.ready_for_finalize.is_empty());
59
60        for mut f in self.candidates.drain(start..).collect::<Vec<F>>() {
61            let reff = f.get_reference();
62            trace!("Pop {:?} for finalization", reff);
63            if reff.is_live() {
64                FinalizableProcessor::<F>::forward_finalizable_reference(tracer, &mut f);
65                trace!("{:?} is live, push {:?} back to candidates", reff, f);
66                self.candidates.push(f);
67                continue;
68            }
69
70            // We should not at this point mark the object as live. A binding may register an object
71            // multiple times with different finalizer methods. If we mark the object as live here, and encounter
72            // the same object later in the candidates list (possibly with a different finalizer method),
73            // we will erroneously think the object never died, and won't push it to the ready_to_finalize
74            // queue.
75            // So we simply push the object to the ready_for_finalize queue, and mark them as live objects later.
76            self.ready_for_finalize.push(f);
77        }
78
79        // Keep the finalizable objects alive.
80        self.forward_finalizable(tracer, nursery);
81
82        // Set nursery_index to the end of the candidates (the candidates before the index are scanned)
83        self.nursery_index = self.candidates.len();
84
85        VM::VMCollection::schedule_finalization(tls);
86    }
87
88    pub fn forward_candidate<OT: ObjectTracer>(&mut self, tracer: &mut OT, _nursery: bool) {
89        self.candidates
90            .iter_mut()
91            .for_each(|f| FinalizableProcessor::<F>::forward_finalizable_reference(tracer, f));
92    }
93
94    pub fn forward_finalizable<OT: ObjectTracer>(&mut self, tracer: &mut OT, _nursery: bool) {
95        self.ready_for_finalize
96            .iter_mut()
97            .for_each(|f| FinalizableProcessor::<F>::forward_finalizable_reference(tracer, f));
98    }
99
100    pub fn get_ready_object(&mut self) -> Option<F> {
101        self.ready_for_finalize.pop()
102    }
103
104    pub fn get_all_finalizers(&mut self) -> Vec<F> {
105        let mut ret = std::mem::take(&mut self.candidates);
106        let ready_objects = std::mem::take(&mut self.ready_for_finalize);
107        ret.extend(ready_objects);
108
109        // We removed objects from candidates. Reset nursery_index
110        self.nursery_index = 0;
111
112        ret
113    }
114
115    pub fn get_finalizers_for(&mut self, object: ObjectReference) -> Vec<F> {
116        // Drain filter for finalizers that equal to 'object':
117        // * for elements that equal to 'object', they will be removed from the original vec, and returned.
118        // * for elements that do not equal to 'object', they will be left in the original vec.
119        // TODO: We should replace this with `vec.drain_filter()` when it is stablized.
120        let drain_filter = |vec: &mut Vec<F>| -> Vec<F> {
121            let mut i = 0;
122            let mut ret = vec![];
123            while i < vec.len() {
124                if vec[i].get_reference() == object {
125                    let val = vec.remove(i);
126                    ret.push(val);
127                } else {
128                    i += 1;
129                }
130            }
131            ret
132        };
133        let mut ret: Vec<F> = drain_filter(&mut self.candidates);
134        ret.extend(drain_filter(&mut self.ready_for_finalize));
135
136        // We removed objects from candidates. Reset nursery_index
137        self.nursery_index = 0;
138
139        ret
140    }
141}
142
143#[derive(Default)]
144pub struct Finalization<T: Trace>(PhantomData<T>);
145
146impl<T: Trace> GCWork<T::VM> for Finalization<T> {
147    fn do_work(&mut self, worker: &mut GCWorker<T::VM>, mmtk: &'static MMTK<T::VM>) {
148        if !*mmtk.options.no_reference_types {
149            // Rescan soft and weak references at the end of the transitive closure from resurrected
150            // objects.  New soft and weak references may be discovered during this.
151            let rescan = Box::new(RescanReferences {
152                soft: true,
153                weak: true,
154                phantom_data: PhantomData,
155            });
156            worker.scheduler().work_buckets[WorkBucketStage::FinalRefClosure].set_sentinel(rescan);
157        }
158
159        let mut finalizable_processor = mmtk.finalizable_processor.lock().unwrap();
160        let num_candidates_begin = finalizable_processor.candidates.len();
161        let num_ready_for_finalize_begin = finalizable_processor.ready_for_finalize.len();
162        debug!(
163            "Finalization, {} objects in candidates, {} objects ready to finalize",
164            num_candidates_begin, num_ready_for_finalize_begin
165        );
166
167        let tracer_context = DefaultObjectTracerContext::<T>::new(WorkBucketStage::FinalRefClosure);
168        let tls = worker.tls;
169        tracer_context.with_tracer(worker, |tracer| {
170            finalizable_processor.scan::<T::VM, _>(tls, tracer, is_nursery_gc(mmtk.get_plan()));
171        });
172
173        let num_candidates_end = finalizable_processor.candidates.len();
174        let num_ready_for_finalize_end = finalizable_processor.ready_for_finalize.len();
175
176        debug!(
177            "Finished finalization, {} objects in candidates, {} objects ready to finalize",
178            num_candidates_end, num_ready_for_finalize_end
179        );
180        probe!(
181            mmtk,
182            finalization,
183            num_candidates_begin,
184            num_candidates_end,
185            num_ready_for_finalize_begin,
186            num_ready_for_finalize_end
187        );
188    }
189}
190
191impl<T: Trace> Finalization<T> {
192    pub fn new() -> Self {
193        Self(PhantomData)
194    }
195}
196
197#[derive(Default)]
198pub struct ForwardFinalization<T: Trace>(PhantomData<T>);
199
200impl<T: Trace> GCWork<T::VM> for ForwardFinalization<T> {
201    fn do_work(&mut self, worker: &mut GCWorker<T::VM>, mmtk: &'static MMTK<T::VM>) {
202        trace!("Forward finalization");
203        let mut finalizable_processor = mmtk.finalizable_processor.lock().unwrap();
204
205        let tracer_context =
206            DefaultObjectTracerContext::<T>::new(WorkBucketStage::FinalizableForwarding);
207        tracer_context.with_tracer(worker, |tracer| {
208            finalizable_processor.forward_candidate(tracer, is_nursery_gc(mmtk.get_plan()));
209
210            finalizable_processor.forward_finalizable(tracer, is_nursery_gc(mmtk.get_plan()));
211        });
212        trace!("Finished forwarding finlizable");
213    }
214}
215impl<T: Trace> ForwardFinalization<T> {
216    pub fn new() -> Self {
217        Self(PhantomData)
218    }
219}