Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Decouple root scanning from ProcessEdgesWork. #598

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 72 additions & 6 deletions src/scheduler/gc_work.rs
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,60 @@ impl<E: ProcessEdgesWork> GCWork<E::VM> for VMProcessWeakRefs<E> {
}
}

struct NonMovingProcessEdgesWork<E: ProcessEdgesWork> {
delegate: E,
nodes: Vec<ObjectReference>,
phantom: PhantomData<E>,
}

impl<E: ProcessEdgesWork> NonMovingProcessEdgesWork<E> {
fn new(nodes: Vec<ObjectReference>, mmtk: &'static MMTK<E::VM>) -> Self {
Self {
delegate: E::new(vec![], false, mmtk),
nodes,
phantom: PhantomData,
}
}
}

impl<E: ProcessEdgesWork> GCWork<E::VM> for NonMovingProcessEdgesWork<E> {
fn do_work(&mut self, worker: &mut GCWorker<E::VM>, _mmtk: &'static MMTK<E::VM>) {
for node in self.nodes.iter() {
let new_object = self.delegate.trace_object(*node);
if new_object != *node {
panic!("Object moved! {} -> {}", node, new_object);
}
}
let new_nodes = std::mem::take(&mut self.delegate.nodes);
let scan_objects = ScanObjects::<E>::new(new_nodes, false);
let bucket = WorkBucketStage::Closure;
worker.add_work(bucket, scan_objects);
}
}

struct GenericProcessRootsWorkFactory<E: ProcessEdgesWork> {
mmtk: &'static MMTK<E::VM>,
}
impl<E: ProcessEdgesWork> GenericProcessRootsWorkFactory<E> {
pub fn new(mmtk: &'static MMTK<E::VM>) -> Box<Self> {
Box::new(Self { mmtk })
}
}

impl<E: ProcessEdgesWork> RootsHandlerFactory for GenericProcessRootsWorkFactory<E> {
fn create_process_edge_roots_work(&self, edges: Vec<Address>) {
let bucket = WorkBucketStage::Closure;
let packet = E::new(edges, true, self.mmtk);
memory_manager::add_work_packet(self.mmtk, bucket, packet)
}

fn create_process_node_roots_work(&self, nodes: Vec<ObjectReference>) {
let bucket = WorkBucketStage::Closure;
let packet = NonMovingProcessEdgesWork::<E>::new(nodes, self.mmtk);
memory_manager::add_work_packet(self.mmtk, bucket, packet)
}
}

#[derive(Default)]
pub struct ScanStackRoots<Edges: ProcessEdgesWork>(PhantomData<Edges>);

Expand All @@ -275,7 +329,11 @@ impl<E: ProcessEdgesWork> ScanStackRoots<E> {
impl<E: ProcessEdgesWork> GCWork<E::VM> for ScanStackRoots<E> {
fn do_work(&mut self, worker: &mut GCWorker<E::VM>, mmtk: &'static MMTK<E::VM>) {
trace!("ScanStackRoots");
<E::VM as VMBinding>::VMScanning::scan_thread_roots::<E>();
let factory = GenericProcessRootsWorkFactory::<E>::new(mmtk);
<E::VM as VMBinding>::VMScanning::scan_threads_root(worker.tls, factory);

// FIXME: scan_threads_root is asynchronous. Thread scanning is not complete by now!
// Introduce a fork-join mechanism.
<E::VM as VMBinding>::VMScanning::notify_initial_thread_scan_complete(false, worker.tls);
for mutator in <E::VM as VMBinding>::VMActivePlan::mutators() {
mutator.flush();
Expand All @@ -289,15 +347,17 @@ pub struct ScanStackRoot<Edges: ProcessEdgesWork>(pub &'static mut Mutator<Edges
impl<E: ProcessEdgesWork> GCWork<E::VM> for ScanStackRoot<E> {
fn do_work(&mut self, worker: &mut GCWorker<E::VM>, mmtk: &'static MMTK<E::VM>) {
trace!("ScanStackRoot for mutator {:?}", self.0.get_tls());
let factory = GenericProcessRootsWorkFactory::<E>::new(mmtk);
let base = &mmtk.plan.base();
let mutators = <E::VM as VMBinding>::VMActivePlan::number_of_mutators();
<E::VM as VMBinding>::VMScanning::scan_thread_root::<E>(
unsafe { &mut *(self.0 as *mut _) },
worker.tls,
let mutator = unsafe { &mut *(self.0 as *mut _) };
<E::VM as VMBinding>::VMScanning::scan_thread_root(
worker.tls, factory, mutator,
);
self.0.flush();

if mmtk.plan.base().inform_stack_scanned(mutators) {
// FIXME: It is not complete yet! Scanning::scan_thread_root is asynchronous.
<E::VM as VMBinding>::VMScanning::notify_initial_thread_scan_complete(
false, worker.tls,
);
Expand All @@ -316,9 +376,10 @@ impl<E: ProcessEdgesWork> ScanVMSpecificRoots<E> {
}

impl<E: ProcessEdgesWork> GCWork<E::VM> for ScanVMSpecificRoots<E> {
fn do_work(&mut self, _worker: &mut GCWorker<E::VM>, _mmtk: &'static MMTK<E::VM>) {
fn do_work(&mut self, worker: &mut GCWorker<E::VM>, mmtk: &'static MMTK<E::VM>) {
trace!("ScanStaticRoots");
<E::VM as VMBinding>::VMScanning::scan_vm_specific_roots::<E>();
let factory = GenericProcessRootsWorkFactory::<E>::new(mmtk);
<E::VM as VMBinding>::VMScanning::scan_vm_specific_roots(worker.tls, factory);
}
}

Expand Down Expand Up @@ -723,3 +784,8 @@ impl<E: ProcessEdgesWork, P: Plan<VM = E::VM> + PlanTraceObject<E::VM>> GCWork<E
trace!("PlanScanObjects End");
}
}

/// Do we really need this?
pub trait ProcessNodesWork<VM: VMBinding>: GCWork<VM> {
fn new(nodes: Vec<ObjectReference>, roots: bool) -> Self;
}
1 change: 1 addition & 0 deletions src/vm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ pub use self::object_model::specs::*;
pub use self::object_model::ObjectModel;
pub use self::reference_glue::ReferenceGlue;
pub use self::scanning::EdgeVisitor;
pub use self::scanning::RootsHandlerFactory;
pub use self::scanning::Scanning;

/// The `VMBinding` trait associates with each trait, and provides VM-specific constants.
Expand Down
57 changes: 52 additions & 5 deletions src/vm/scanning.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use crate::plan::Mutator;
use crate::scheduler::ProcessEdgesWork;
use crate::util::VMWorkerThread;
use crate::util::{Address, ObjectReference};
use crate::vm::VMBinding;
Expand All @@ -11,6 +10,28 @@ pub trait EdgeVisitor {
// TODO: Add visit_soft_edge, visit_weak_edge, ... here.
}

/// Root-scanning methods use this trait to spawn work packets for processing roots.
pub trait RootsHandlerFactory {
/// Create work packets to handle the roots represented as edges.
///
/// The work packet may update the edge.
///
/// Arguments:
/// * `edges`: A vector of edges.
fn create_process_edge_roots_work(&self, edges: Vec<Address>);

/// Create work packets to handle edges.
///
/// The work packet cannot update the roots. This is a good chance to pin the objects.
///
/// This method is useful for conservative stack scanning, or VMs that cannot update some
/// of the root edges.
///
/// Arguments:
/// * `nodes`: A vector of object references pointed by root edges.
fn create_process_node_roots_work(&self, nodes: Vec<ObjectReference>);
}

/// VM-specific methods for scanning roots/objects.
pub trait Scanning<VM: VMBinding> {
/// Scan stack roots after all mutators are paused.
Expand All @@ -34,6 +55,25 @@ pub trait Scanning<VM: VMBinding> {
edge_visitor: &mut EV,
);

/// Scan and object and process all edges at the same time.
///
/// Arguments:
/// * `tls`: The VM-specific thread-local storage for the current worker.
/// * `object`: The object to be scanned.
/// * `trace_object`: Called back for the value held in each edge. The return value of
/// `trace_object` is the new value of the edge.
fn scan_object_and_process_edges<F: FnMut(ObjectReference) -> ObjectReference> (
_tls: VMWorkerThread,
_object: ObjectReference,
_trace_object: F,
) {
unimplemented!()
}

fn supports_edge_enqueuing(_tls: VMWorkerThread, _object: ObjectReference) -> bool {
true
}

/// MMTk calls this method at the first time during a collection that thread's stacks
/// have been scanned. This can be used (for example) to clean up
/// obsolete compiled methods that are no longer being executed.
Expand All @@ -60,21 +100,28 @@ pub trait Scanning<VM: VMBinding> {
}

/// Scan all the mutators for roots.
fn scan_thread_roots<W: ProcessEdgesWork<VM = VM>>();
fn scan_threads_root(
tls: VMWorkerThread,
factory: Box<dyn RootsHandlerFactory>,
);

/// Scan one mutator for roots.
///
/// Arguments:
/// * `mutator`: The reference to the mutator whose roots will be scanned.
/// * `tls`: The GC thread that is performing this scanning.
fn scan_thread_root<W: ProcessEdgesWork<VM = VM>>(
mutator: &'static mut Mutator<VM>,
fn scan_thread_root(
tls: VMWorkerThread,
factory: Box<dyn RootsHandlerFactory>,
mutator: &'static mut Mutator<VM>,
);

/// Scan VM-specific roots. The creation of all root scan tasks (except thread scanning)
/// goes here.
fn scan_vm_specific_roots<W: ProcessEdgesWork<VM = VM>>();
fn scan_vm_specific_roots(
tls: VMWorkerThread,
factory: Box<dyn RootsHandlerFactory>,
);

/// Return whether the VM supports return barriers. This is unused at the moment.
fn supports_return_barrier() -> bool;
Expand Down