mmtk/util/
finalizable_processor.rs

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