Skip to content

Edge trait #606

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

Merged
merged 4 commits into from
Aug 29, 2022
Merged
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
2 changes: 1 addition & 1 deletion docs/tutorial/code/mygc_semispace/gc_work.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ impl<VM:VMBinding> ProcessEdgesWork for MyGCProcessEdges<VM> {
type VM = VM;
type ScanObjectsWorkType = ScanObjects<Self>;

fn new(edges: Vec<Address>, roots: bool, mmtk: &'static MMTK<VM>) -> Self {
fn new(edges: Vec<EdgeOf<Self>>, roots: bool, mmtk: &'static MMTK<VM>) -> Self {
let base = ProcessEdgesBase::new(edges, roots, mmtk);
let plan = base.plan().downcast_ref::<MyGC<VM>>().unwrap();
Self { base, plan }
Expand Down
9 changes: 8 additions & 1 deletion src/mmtk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
use crate::plan::Plan;
use crate::policy::space::SFTMap;
use crate::scheduler::GCWorkScheduler;

#[cfg(feature = "extreme_assertions")]
use crate::util::edge_logger::EdgeLogger;
use crate::util::finalizable_processor::FinalizableProcessor;
use crate::util::heap::layout::heap_layout::Mmapper;
use crate::util::heap::layout::heap_layout::VMMap;
Expand Down Expand Up @@ -86,7 +89,9 @@ pub struct MMTK<VM: VMBinding> {
Mutex<FinalizableProcessor<<VM::VMReferenceGlue as ReferenceGlue<VM>>::FinalizableType>>,
pub(crate) scheduler: Arc<GCWorkScheduler<VM>>,
#[cfg(feature = "sanity")]
pub(crate) sanity_checker: Mutex<SanityChecker>,
pub(crate) sanity_checker: Mutex<SanityChecker<VM::VMEdge>>,
#[cfg(feature = "extreme_assertions")]
pub(crate) edge_logger: EdgeLogger<VM::VMEdge>,
inside_harness: AtomicBool,
}

Expand Down Expand Up @@ -130,6 +135,8 @@ impl<VM: VMBinding> MMTK<VM> {
#[cfg(feature = "sanity")]
sanity_checker: Mutex::new(SanityChecker::new()),
inside_harness: AtomicBool::new(false),
#[cfg(feature = "extreme_assertions")]
edge_logger: EdgeLogger::new(),
}
}

Expand Down
11 changes: 6 additions & 5 deletions src/plan/generational/gc_work.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use crate::plan::generational::global::Gen;
use crate::policy::space::Space;
use crate::scheduler::gc_work::*;
use crate::util::{Address, ObjectReference};
use crate::util::ObjectReference;
use crate::vm::edge_shape::Edge;
use crate::vm::*;
use crate::MMTK;
use std::ops::{Deref, DerefMut};
Expand All @@ -18,7 +19,7 @@ impl<VM: VMBinding> ProcessEdgesWork for GenNurseryProcessEdges<VM> {
type VM = VM;
type ScanObjectsWorkType = ScanObjects<Self>;

fn new(edges: Vec<Address>, roots: bool, mmtk: &'static MMTK<VM>) -> Self {
fn new(edges: Vec<EdgeOf<Self>>, roots: bool, mmtk: &'static MMTK<VM>) -> Self {
let base = ProcessEdgesBase::new(edges, roots, mmtk);
let gen = base.plan().generational();
Self { gen, base }
Expand All @@ -34,11 +35,11 @@ impl<VM: VMBinding> ProcessEdgesWork for GenNurseryProcessEdges<VM> {
.trace_object_nursery(&mut self.base.nodes, object, worker)
}
#[inline]
fn process_edge(&mut self, slot: Address) {
let object = unsafe { slot.load::<ObjectReference>() };
fn process_edge(&mut self, slot: EdgeOf<Self>) {
let object = slot.load();
let new_object = self.trace_object(object);
debug_assert!(!self.gen.nursery.in_space(new_object));
unsafe { slot.store(new_object) };
slot.store(new_object);
}

#[inline(always)]
Expand Down
2 changes: 1 addition & 1 deletion src/plan/markcompact/gc_work.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ impl<VM: VMBinding> GCWork<VM> for UpdateReferences<VM> {
VM::VMScanning::prepare_for_roots_re_scanning();
mmtk.plan.base().prepare_for_stack_scanning();
#[cfg(feature = "extreme_assertions")]
crate::util::edge_logger::reset();
mmtk.edge_logger.reset();

// TODO investigate why the following will create duplicate edges
// scheduler.work_buckets[WorkBucketStage::RefForwarding]
Expand Down
10 changes: 5 additions & 5 deletions src/plan/tracing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@

use std::mem;

use crate::scheduler::gc_work::ProcessEdgesWork;
use crate::scheduler::gc_work::{EdgeOf, ProcessEdgesWork};
use crate::scheduler::{GCWorker, WorkBucketStage};
use crate::util::{Address, ObjectReference};
use crate::util::ObjectReference;
use crate::vm::EdgeVisitor;

/// This trait represents an object queue to enqueue objects during tracing.
Expand Down Expand Up @@ -63,7 +63,7 @@ impl ObjectQueue for VectorObjectQueue {

/// A transitive closure visitor to collect all the edges of an object.
pub struct ObjectsClosure<'a, E: ProcessEdgesWork> {
buffer: Vec<Address>,
buffer: Vec<EdgeOf<E>>,
worker: &'a mut GCWorker<E::VM>,
}

Expand All @@ -85,9 +85,9 @@ impl<'a, E: ProcessEdgesWork> ObjectsClosure<'a, E> {
}
}

impl<'a, E: ProcessEdgesWork> EdgeVisitor for ObjectsClosure<'a, E> {
impl<'a, E: ProcessEdgesWork> EdgeVisitor<EdgeOf<E>> for ObjectsClosure<'a, E> {
#[inline(always)]
fn visit_edge(&mut self, slot: Address) {
fn visit_edge(&mut self, slot: EdgeOf<E>) {
if self.buffer.is_empty() {
self.buffer.reserve(E::CAPACITY);
}
Expand Down
35 changes: 20 additions & 15 deletions src/scheduler/gc_work.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::plan::ObjectsClosure;
use crate::plan::VectorObjectQueue;
use crate::util::metadata::*;
use crate::util::*;
use crate::vm::edge_shape::Edge;
use crate::vm::*;
use crate::*;
use std::marker::PhantomData;
Expand Down Expand Up @@ -227,7 +228,7 @@ impl<VM: VMBinding> GCWork<VM> for EndOfGC {
#[cfg(feature = "extreme_assertions")]
if crate::util::edge_logger::should_check_duplicate_edges(&*mmtk.plan) {
// reset the logging info at the end of each GC
crate::util::edge_logger::reset();
mmtk.edge_logger.reset();
}

if <VM as VMBinding>::VMCollection::COORDINATOR_ONLY_STW {
Expand Down Expand Up @@ -331,7 +332,7 @@ impl<E: ProcessEdgesWork> GCWork<E::VM> for ScanVMSpecificRoots<E> {
}

pub struct ProcessEdgesBase<VM: VMBinding> {
pub edges: Vec<Address>,
pub edges: Vec<VM::VMEdge>,
pub nodes: VectorObjectQueue,
mmtk: &'static MMTK<VM>,
// Use raw pointer for fast pointer dereferencing, instead of using `Option<&'static mut GCWorker<E::VM>>`.
Expand All @@ -345,12 +346,12 @@ unsafe impl<VM: VMBinding> Send for ProcessEdgesBase<VM> {}
impl<VM: VMBinding> ProcessEdgesBase<VM> {
// Requires an MMTk reference. Each plan-specific type that uses ProcessEdgesBase can get a static plan reference
// at creation. This avoids overhead for dynamic dispatch or downcasting plan for each object traced.
pub fn new(edges: Vec<Address>, roots: bool, mmtk: &'static MMTK<VM>) -> Self {
pub fn new(edges: Vec<VM::VMEdge>, roots: bool, mmtk: &'static MMTK<VM>) -> Self {
#[cfg(feature = "extreme_assertions")]
if crate::util::edge_logger::should_check_duplicate_edges(&*mmtk.plan) {
for edge in &edges {
// log edge, panic if already logged
crate::util::edge_logger::log_edge(*edge);
mmtk.edge_logger.log_edge(*edge);
}
}
Self {
Expand Down Expand Up @@ -388,6 +389,9 @@ impl<VM: VMBinding> ProcessEdgesBase<VM> {
}
}

/// A short-hand for `<E::VM as VMBinding>::VMEdge`.
pub type EdgeOf<E> = <<E as ProcessEdgesWork>::VM as VMBinding>::VMEdge;

/// Scan & update a list of object slots
//
// Note: be very careful when using this trait. process_node() will push objects
Expand All @@ -407,7 +411,8 @@ pub trait ProcessEdgesWork:
const CAPACITY: usize = 4096;
const OVERWRITE_REFERENCE: bool = true;
const SCAN_OBJECTS_IMMEDIATELY: bool = true;
fn new(edges: Vec<Address>, roots: bool, mmtk: &'static MMTK<Self::VM>) -> Self;

fn new(edges: Vec<EdgeOf<Self>>, roots: bool, mmtk: &'static MMTK<Self::VM>) -> Self;

/// Trace an MMTk object. The implementation should forward this call to the policy-specific
/// `trace_object()` methods, depending on which space this object is in.
Expand Down Expand Up @@ -463,11 +468,11 @@ pub trait ProcessEdgesWork:
}

#[inline]
fn process_edge(&mut self, slot: Address) {
let object = unsafe { slot.load::<ObjectReference>() };
fn process_edge(&mut self, slot: EdgeOf<Self>) {
let object = slot.load();
let new_object = self.trace_object(object);
if Self::OVERWRITE_REFERENCE {
unsafe { slot.store(new_object) };
slot.store(new_object);
}
}

Expand Down Expand Up @@ -511,7 +516,7 @@ impl<VM: VMBinding> ProcessEdgesWork for SFTProcessEdges<VM> {
type VM = VM;
type ScanObjectsWorkType = ScanObjects<Self>;

fn new(edges: Vec<Address>, roots: bool, mmtk: &'static MMTK<VM>) -> Self {
fn new(edges: Vec<EdgeOf<Self>>, roots: bool, mmtk: &'static MMTK<VM>) -> Self {
let base = ProcessEdgesBase::new(edges, roots, mmtk);
Self { base }
}
Expand Down Expand Up @@ -552,8 +557,8 @@ impl<E: ProcessEdgesWork> Clone for ProcessEdgesWorkRootsWorkFactory<E> {
}
}

impl<E: ProcessEdgesWork> RootsWorkFactory for ProcessEdgesWorkRootsWorkFactory<E> {
fn create_process_edge_roots_work(&mut self, edges: Vec<Address>) {
impl<E: ProcessEdgesWork> RootsWorkFactory<EdgeOf<E>> for ProcessEdgesWorkRootsWorkFactory<E> {
fn create_process_edge_roots_work(&mut self, edges: Vec<EdgeOf<E>>) {
crate::memory_manager::add_work_packet(
self.mmtk,
WorkBucketStage::Closure,
Expand Down Expand Up @@ -827,7 +832,7 @@ impl<VM: VMBinding, P: PlanTraceObject<VM> + Plan<VM = VM>, const KIND: TraceKin
type VM = VM;
type ScanObjectsWorkType = PlanScanObjects<Self, P>;

fn new(edges: Vec<Address>, roots: bool, mmtk: &'static MMTK<VM>) -> Self {
fn new(edges: Vec<EdgeOf<Self>>, roots: bool, mmtk: &'static MMTK<VM>) -> Self {
let base = ProcessEdgesBase::new(edges, roots, mmtk);
let plan = base.plan().downcast_ref::<P>().unwrap();
Self { plan, base }
Expand All @@ -854,11 +859,11 @@ impl<VM: VMBinding, P: PlanTraceObject<VM> + Plan<VM = VM>, const KIND: TraceKin
}

#[inline]
fn process_edge(&mut self, slot: Address) {
let object = unsafe { slot.load::<ObjectReference>() };
fn process_edge(&mut self, slot: EdgeOf<Self>) {
let object = slot.load();
let new_object = self.trace_object(object);
if P::may_move_objects::<KIND>() {
unsafe { slot.store(new_object) };
slot.store(new_object);
}
}
}
Expand Down
62 changes: 38 additions & 24 deletions src/util/edge_logger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 +5,53 @@
//!

use crate::plan::Plan;
use crate::util::Address;
use crate::vm::edge_shape::Edge;
use crate::vm::VMBinding;
use std::collections::HashSet;
use std::sync::RwLock;

lazy_static! {
pub struct EdgeLogger<ES: Edge> {
// A private hash-set to keep track of edges.
static ref EDGE_LOG: RwLock<HashSet<Address>> = RwLock::new(HashSet::new());
edge_log: RwLock<HashSet<ES>>,
}

unsafe impl<ES: Edge> Sync for EdgeLogger<ES> {}

impl<ES: Edge> EdgeLogger<ES> {
pub fn new() -> Self {
Self {
edge_log: Default::default(),
}
}

/// Logs an edge.
/// Panics if the edge was already logged.
///
/// # Arguments
///
/// * `edge` - The edge to log.
///
pub fn log_edge(&self, edge: ES) {
trace!("log_edge({:?})", edge);
let mut edge_log = self.edge_log.write().unwrap();
assert!(
edge_log.insert(edge),
"duplicate edge ({:?}) detected",
edge
);
}

/// Reset the edge logger by clearing the hash-set of edges.
/// This function is called at the end of each GC iteration.
///
pub fn reset(&self) {
let mut edge_log = self.edge_log.write().unwrap();
edge_log.clear();
}
}

/// Whether we should check duplicate edges. This depends on the actual plan.
pub fn should_check_duplicate_edges<VM: VMBinding>(plan: &dyn Plan<VM = VM>) -> bool {
// If a plan allows tracing duplicate edges, we should not run this check.
!plan.constraints().may_trace_duplicate_edges
}

/// Logs an edge.
/// Panics if the edge was already logged.
///
/// # Arguments
///
/// * `edge` - The edge to log.
///
pub fn log_edge(edge: Address) {
trace!("log_edge({})", edge);
let mut edge_log = EDGE_LOG.write().unwrap();
assert!(edge_log.insert(edge), "duplicate edge ({}) detected", edge);
}

/// Reset the edge logger by clearing the hash-set of edges.
/// This function is called at the end of each GC iteration.
///
pub fn reset() {
let mut edge_log = EDGE_LOG.write().unwrap();
edge_log.clear();
}
15 changes: 8 additions & 7 deletions src/util/sanity/sanity_checker.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::plan::Plan;
use crate::scheduler::gc_work::*;
use crate::util::{Address, ObjectReference};
use crate::util::ObjectReference;
use crate::vm::edge_shape::Edge;
use crate::vm::*;
use crate::MMTK;
use crate::{scheduler::*, ObjectQueue};
Expand All @@ -9,20 +10,20 @@ use std::ops::{Deref, DerefMut};
use std::sync::atomic::Ordering;

#[allow(dead_code)]
pub struct SanityChecker {
pub struct SanityChecker<ES: Edge> {
/// Visited objects
refs: HashSet<ObjectReference>,
/// Cached root edges for sanity root scanning
roots: Vec<Vec<Address>>,
roots: Vec<Vec<ES>>,
}

impl Default for SanityChecker {
impl<ES: Edge> Default for SanityChecker<ES> {
fn default() -> Self {
Self::new()
}
}

impl SanityChecker {
impl<ES: Edge> SanityChecker<ES> {
pub fn new() -> Self {
Self {
refs: HashSet::new(),
Expand All @@ -31,7 +32,7 @@ impl SanityChecker {
}

/// Cache a list of root edges to the sanity checker.
pub fn add_roots(&mut self, roots: Vec<Address>) {
pub fn add_roots(&mut self, roots: Vec<ES>) {
self.roots.push(roots)
}

Expand Down Expand Up @@ -164,7 +165,7 @@ impl<VM: VMBinding> ProcessEdgesWork for SanityGCProcessEdges<VM> {
type ScanObjectsWorkType = ScanObjects<Self>;

const OVERWRITE_REFERENCE: bool = false;
fn new(edges: Vec<Address>, roots: bool, mmtk: &'static MMTK<VM>) -> Self {
fn new(edges: Vec<EdgeOf<Self>>, roots: bool, mmtk: &'static MMTK<VM>) -> Self {
Self {
base: ProcessEdgesBase::new(edges, roots, mmtk),
// ..Default::default()
Expand Down
Loading