Skip to content

Decuple reference processor from ProcessEdgesWork #604

@wks

Description

@wks

TL;DR: The reference processor is tightly coupled with ProcessEdgesWork work packets, making it impossible to support runtimes that can only do object-enqueuing. We can make it general.

Problem

Currently, reference-processing work packets {Soft,Weak,Phantom}RefProcessing take ProcessEdgesWork as a parameter. For example:

pub struct SoftRefProcessing<E: ProcessEdgesWork>(PhantomData<E>);
impl<E: ProcessEdgesWork> GCWork<E::VM> for SoftRefProcessing<E> {
    fn do_work(&mut self, worker: &mut GCWorker<E::VM>, mmtk: &'static MMTK<E::VM>) {
        let mut w = E::new(vec![], false, mmtk);
        w.set_worker(worker);
        mmtk.reference_processors.scan_soft_refs(&mut w, mmtk);
        w.flush();
    }
}

Seeing from the use pattern, it instantiates E, passes it to scan_soft_refs, and calls .flush() on it. It never calls w.do_work, which indicates ProcessEdgesWork is not a sub-task of SoftRefProcessing. Instead, SoftRefProcessing is just stealing some functionalities from ProcessEdgesWork!

Analysis

It uses the E: ProcessEdgesWork type for three purposes:

  1. Use E::trace_object to trace object. Traced soft/weak/phantom references will become softly/weakly/phantomly reachable.
  2. Use E as an object queue. E::trace_object takes a queue parameter (currently in the form of a TransitiveClosure, but will be refactored in Remove TransitiveClosure param from Space::trace_object and Scanning::scan_object #559).
  3. Use the object queue to create a object-scanning work packet. w.flush() does this. It will create a ScanObjects work packet to scan objects.

From the analysis, the SoftRefProcessing work packet only has two dependencies:

  1. A delegate for calling trace_object in the appropriate space, and
  2. The type of the object-scanning work packet to create. (More concretely, the post-scan hook which Immix needs.)

The queue is just a Vec<ObjectReference> (or whatever that wraps it) and can be created locally.

Solution

To move away from ProcessEdgesWork, we just need to parameterise {Soft,Weak,Phantom}RefProcessing with a trait that provides the above two operations, namely trace_object and post_scan_object.

I have a draft for this trait. I call it TracingDelegate.

pub trait TracingDelegate<VM: VMBinding>: 'static + Copy + Send {
    fn trace_object<T: TransitiveClosure>(
        &self,
        trace: &mut T,
        object: ObjectReference,
        worker: &mut GCWorker<VM>,
    ) -> ObjectReference;

    fn may_move_objects() -> bool;

    fn post_scan_object(&self, object: ObjectReference);
}

They are just the three methods provided by PlanTraceObject, except without the KIND which can be parameterised on concrete implementations.

There should be two implementations, one for SFT, and the other for PlanTraceObject, just like there are SFTProcessEdges and PlanProcessEdges.

Then SoftRefProcessing can just call trace_object from that trait. The ScanObjects trait can also be refactored to use that trait.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions