mmtk 0.32.0

MMTk is a framework for the design and implementation of high-performance and portable memory managers.
Documentation
use crate::plan::is_nursery_gc;
use crate::scheduler::gc_work::ProcessEdgesWork;
use crate::scheduler::{GCWork, GCWorker, WorkBucketStage};
use crate::util::reference_processor::RescanReferences;
use crate::util::ObjectReference;
use crate::util::VMWorkerThread;
use crate::vm::Finalizable;
use crate::vm::{Collection, VMBinding};
use crate::MMTK;
use std::marker::PhantomData;

/// A special processor for Finalizable objects.
// TODO: we should consider if we want to merge FinalizableProcessor with ReferenceProcessor,
// and treat final reference as a special reference type in ReferenceProcessor.
#[derive(Default)]
pub struct FinalizableProcessor<F: Finalizable> {
    /// Candidate objects that has finalizers with them
    candidates: Vec<F>,
    /// Index into candidates to record where we are up to in the last scan of the candidates.
    /// Index after nursery_index are new objects inserted after the last GC.
    nursery_index: usize,
    /// Objects that can be finalized. They are actually dead, but we keep them alive
    /// until the binding pops them from the queue.
    ready_for_finalize: Vec<F>,
}

impl<F: Finalizable> FinalizableProcessor<F> {
    pub fn new() -> Self {
        Self {
            candidates: vec![],
            nursery_index: 0,
            ready_for_finalize: vec![],
        }
    }

    pub fn add(&mut self, object: F) {
        self.candidates.push(object);
    }

    fn forward_finalizable_reference<E: ProcessEdgesWork>(e: &mut E, finalizable: &mut F) {
        finalizable.keep_alive::<E>(e);
    }

    pub fn scan<E: ProcessEdgesWork>(&mut self, tls: VMWorkerThread, e: &mut E, nursery: bool) {
        let start = if nursery { self.nursery_index } else { 0 };

        // We should go through ready_for_finalize objects and keep them alive.
        // Unlike candidates, those objects are known to be alive. This means
        // theoratically we could do the following loop at any time in a GC (not necessarily after closure phase).
        // But we have to iterate through candidates after closure.
        self.candidates.append(&mut self.ready_for_finalize);
        debug_assert!(self.ready_for_finalize.is_empty());

        for mut f in self.candidates.drain(start..).collect::<Vec<F>>() {
            let reff = f.get_reference();
            trace!("Pop {:?} for finalization", reff);
            if reff.is_live() {
                FinalizableProcessor::<F>::forward_finalizable_reference(e, &mut f);
                trace!("{:?} is live, push {:?} back to candidates", reff, f);
                self.candidates.push(f);
                continue;
            }

            // We should not at this point mark the object as live. A binding may register an object
            // multiple times with different finalizer methods. If we mark the object as live here, and encounter
            // the same object later in the candidates list (possibly with a different finalizer method),
            // we will erroneously think the object never died, and won't push it to the ready_to_finalize
            // queue.
            // So we simply push the object to the ready_for_finalize queue, and mark them as live objects later.
            self.ready_for_finalize.push(f);
        }

        // Keep the finalizable objects alive.
        self.forward_finalizable(e, nursery);

        // Set nursery_index to the end of the candidates (the candidates before the index are scanned)
        self.nursery_index = self.candidates.len();

        <<E as ProcessEdgesWork>::VM as VMBinding>::VMCollection::schedule_finalization(tls);
    }

    pub fn forward_candidate<E: ProcessEdgesWork>(&mut self, e: &mut E, _nursery: bool) {
        self.candidates
            .iter_mut()
            .for_each(|f| FinalizableProcessor::<F>::forward_finalizable_reference(e, f));
        e.flush();
    }

    pub fn forward_finalizable<E: ProcessEdgesWork>(&mut self, e: &mut E, _nursery: bool) {
        self.ready_for_finalize
            .iter_mut()
            .for_each(|f| FinalizableProcessor::<F>::forward_finalizable_reference(e, f));
        e.flush();
    }

    pub fn get_ready_object(&mut self) -> Option<F> {
        self.ready_for_finalize.pop()
    }

    pub fn get_all_finalizers(&mut self) -> Vec<F> {
        let mut ret = std::mem::take(&mut self.candidates);
        let ready_objects = std::mem::take(&mut self.ready_for_finalize);
        ret.extend(ready_objects);

        // We removed objects from candidates. Reset nursery_index
        self.nursery_index = 0;

        ret
    }

    pub fn get_finalizers_for(&mut self, object: ObjectReference) -> Vec<F> {
        // Drain filter for finalizers that equal to 'object':
        // * for elements that equal to 'object', they will be removed from the original vec, and returned.
        // * for elements that do not equal to 'object', they will be left in the original vec.
        // TODO: We should replace this with `vec.drain_filter()` when it is stablized.
        let drain_filter = |vec: &mut Vec<F>| -> Vec<F> {
            let mut i = 0;
            let mut ret = vec![];
            while i < vec.len() {
                if vec[i].get_reference() == object {
                    let val = vec.remove(i);
                    ret.push(val);
                } else {
                    i += 1;
                }
            }
            ret
        };
        let mut ret: Vec<F> = drain_filter(&mut self.candidates);
        ret.extend(drain_filter(&mut self.ready_for_finalize));

        // We removed objects from candidates. Reset nursery_index
        self.nursery_index = 0;

        ret
    }
}

#[derive(Default)]
pub struct Finalization<E: ProcessEdgesWork>(PhantomData<E>);

impl<E: ProcessEdgesWork> GCWork<E::VM> for Finalization<E> {
    fn do_work(&mut self, worker: &mut GCWorker<E::VM>, mmtk: &'static MMTK<E::VM>) {
        if !*mmtk.options.no_reference_types {
            // Rescan soft and weak references at the end of the transitive closure from resurrected
            // objects.  New soft and weak references may be discovered during this.
            let rescan = Box::new(RescanReferences {
                soft: true,
                weak: true,
                phantom_data: PhantomData,
            });
            worker.scheduler().work_buckets[WorkBucketStage::FinalRefClosure].set_sentinel(rescan);
        }

        let mut finalizable_processor = mmtk.finalizable_processor.lock().unwrap();
        let num_candidates_begin = finalizable_processor.candidates.len();
        let num_ready_for_finalize_begin = finalizable_processor.ready_for_finalize.len();
        debug!(
            "Finalization, {} objects in candidates, {} objects ready to finalize",
            num_candidates_begin, num_ready_for_finalize_begin
        );

        let mut w = E::new(vec![], false, mmtk, WorkBucketStage::FinalRefClosure);
        w.set_worker(worker);
        finalizable_processor.scan(worker.tls, &mut w, is_nursery_gc(mmtk.get_plan()));

        let num_candidates_end = finalizable_processor.candidates.len();
        let num_ready_for_finalize_end = finalizable_processor.ready_for_finalize.len();

        debug!(
            "Finished finalization, {} objects in candidates, {} objects ready to finalize",
            num_candidates_end, num_ready_for_finalize_end
        );
        probe!(
            mmtk,
            finalization,
            num_candidates_begin,
            num_candidates_end,
            num_ready_for_finalize_begin,
            num_ready_for_finalize_end
        );
    }
}
impl<E: ProcessEdgesWork> Finalization<E> {
    pub fn new() -> Self {
        Self(PhantomData)
    }
}

#[derive(Default)]
pub struct ForwardFinalization<E: ProcessEdgesWork>(PhantomData<E>);

impl<E: ProcessEdgesWork> GCWork<E::VM> for ForwardFinalization<E> {
    fn do_work(&mut self, worker: &mut GCWorker<E::VM>, mmtk: &'static MMTK<E::VM>) {
        trace!("Forward finalization");
        let mut finalizable_processor = mmtk.finalizable_processor.lock().unwrap();
        let mut w = E::new(vec![], false, mmtk, WorkBucketStage::FinalizableForwarding);
        w.set_worker(worker);
        finalizable_processor.forward_candidate(&mut w, is_nursery_gc(mmtk.get_plan()));

        finalizable_processor.forward_finalizable(&mut w, is_nursery_gc(mmtk.get_plan()));
        trace!("Finished forwarding finlizable");
    }
}
impl<E: ProcessEdgesWork> ForwardFinalization<E> {
    pub fn new() -> Self {
        Self(PhantomData)
    }
}