use crate::plan::Plan;
use crate::scheduler::GCWork;
use crate::scheduler::GCWorker;
use crate::scheduler::WorkBucketStage;
use crate::util::scanning_helper;
use crate::util::ObjectReference;
use crate::vm::slot::Slot;
use crate::vm::{ObjectModel, VMBinding};
use crate::MMTK;
use std::collections::HashSet;
#[allow(dead_code)]
pub struct SanityChecker<SL: Slot> {
refs: HashSet<ObjectReference>,
root_slots: Vec<Vec<SL>>,
root_nodes: Vec<Vec<ObjectReference>>,
}
impl<SL: Slot> Default for SanityChecker<SL> {
fn default() -> Self {
Self::new()
}
}
impl<SL: Slot> SanityChecker<SL> {
pub fn new() -> Self {
Self {
refs: HashSet::new(),
root_slots: vec![],
root_nodes: vec![],
}
}
pub fn add_root_slots(&mut self, roots: Vec<SL>) {
debug!("Added {} root slots", roots.len());
self.root_slots.push(roots)
}
pub fn add_root_nodes(&mut self, roots: Vec<ObjectReference>) {
debug!("Added {} root nodes", roots.len());
self.root_nodes.push(roots)
}
fn clear_roots_cache(&mut self) {
debug!("Cleared roots cache");
self.root_slots.clear();
self.root_nodes.clear();
}
}
pub struct ScheduleSanityGC<P: Plan> {
plan: &'static P,
}
impl<P: Plan> ScheduleSanityGC<P> {
pub fn new(plan: &'static P) -> Self {
ScheduleSanityGC { plan }
}
}
impl<P: Plan> GCWork<P::VM> for ScheduleSanityGC<P> {
fn do_work(&mut self, worker: &mut GCWorker<P::VM>, mmtk: &'static MMTK<P::VM>) {
let scheduler = worker.scheduler();
scheduler.reset_state();
#[cfg(feature = "extreme_assertions")]
mmtk.slot_logger.reset();
mmtk.sanity_begin();
worker.scheduler().work_buckets[WorkBucketStage::Prepare]
.add(SanityPrepare::<P>::new(self.plan));
worker.scheduler().work_buckets[WorkBucketStage::Closure]
.add(SanityClosure::<P>::new(self.plan));
worker.scheduler().work_buckets[WorkBucketStage::Release]
.add(SanityRelease::<P>::new(self.plan));
}
}
pub struct SanityPrepare<P: Plan> {
pub plan: &'static P,
}
impl<P: Plan> SanityPrepare<P> {
pub fn new(plan: &'static P) -> Self {
Self { plan }
}
}
impl<P: Plan> GCWork<P::VM> for SanityPrepare<P> {
fn do_work(&mut self, _worker: &mut GCWorker<P::VM>, mmtk: &'static MMTK<P::VM>) {
info!("Sanity GC prepare");
{
let mut sanity_checker = mmtk.sanity_checker.lock().unwrap();
sanity_checker.refs.clear();
}
}
}
pub struct SanityRelease<P: Plan> {
pub plan: &'static P,
}
impl<P: Plan> SanityRelease<P> {
pub fn new(plan: &'static P) -> Self {
Self { plan }
}
}
impl<P: Plan> GCWork<P::VM> for SanityRelease<P> {
fn do_work(&mut self, _worker: &mut GCWorker<P::VM>, mmtk: &'static MMTK<P::VM>) {
info!("Sanity GC release");
mmtk.sanity_checker.lock().unwrap().clear_roots_cache();
mmtk.sanity_end();
}
}
pub struct SanityClosure<P: Plan> {
pub plan: &'static P,
}
impl<P: Plan> SanityClosure<P> {
pub fn new(plan: &'static P) -> Self {
Self { plan }
}
}
impl<P: Plan> GCWork<P::VM> for SanityClosure<P> {
fn do_work(&mut self, worker: &mut GCWorker<P::VM>, mmtk: &'static MMTK<P::VM>) {
info!("Sanity GC closure");
let mut sanity_checker = mmtk.sanity_checker.lock().unwrap();
let mut queue = Vec::new();
for roots in &sanity_checker.root_slots {
queue.extend(roots.iter().flat_map(|slot| slot.load()));
}
for roots in &sanity_checker.root_nodes {
queue.extend(roots);
}
let tls = worker.tls;
while let Some(object) = queue.pop() {
if !sanity_checker.refs.insert(object) {
continue;
}
trace!("Doing sanity check on object {object}");
assert!(
object.is_sane(),
"`object.is_sane()` returned false. object: {object}",
);
assert!(
self.plan.sanity_check_object(object),
"plan.sanity_check_object(object) returned false. object: {object}",
);
assert!(
<P::VM as VMBinding>::VMObjectModel::is_object_sane(object),
"VMObjectModel::is_object_sane(object) returned false. object: {object}",
);
scanning_helper::visit_children_non_moving::<P::VM>(tls, object, &mut |child| {
queue.push(child);
child
});
}
}
}