use std::cell::UnsafeCell;
use std::sync::Mutex;
use std::vec::Vec;
use crate::plan::{MutatorContext, TraceLocal};
use crate::util::OpaquePointer;
use crate::util::{Address, ObjectReference};
use crate::vm::VMBinding;
use crate::vm::{ActivePlan, ReferenceGlue};
pub struct ReferenceProcessors {
soft: ReferenceProcessor,
weak: ReferenceProcessor,
phantom: ReferenceProcessor,
}
impl ReferenceProcessors {
pub fn new() -> Self {
ReferenceProcessors {
soft: ReferenceProcessor::new(Semantics::SOFT),
weak: ReferenceProcessor::new(Semantics::WEAK),
phantom: ReferenceProcessor::new(Semantics::PHANTOM),
}
}
pub fn get(&self, semantics: Semantics) -> &ReferenceProcessor {
match semantics {
Semantics::SOFT => &self.soft,
Semantics::WEAK => &self.weak,
Semantics::PHANTOM => &self.phantom,
}
}
pub fn clear(&self) {
self.soft.clear();
self.weak.clear();
self.phantom.clear();
}
pub fn add_soft_candidate<VM: VMBinding>(
&self,
reff: ObjectReference,
referent: ObjectReference,
) {
self.soft.add_candidate::<VM>(reff, referent);
}
pub fn add_weak_candidate<VM: VMBinding>(
&self,
reff: ObjectReference,
referent: ObjectReference,
) {
self.weak.add_candidate::<VM>(reff, referent);
}
pub fn add_phantom_candidate<VM: VMBinding>(
&self,
reff: ObjectReference,
referent: ObjectReference,
) {
self.phantom.add_candidate::<VM>(reff, referent);
}
pub fn forward_refs<VM: VMBinding, T: TraceLocal>(&self, trace: &mut T) {
self.soft.forward::<VM, T>(trace, false);
self.weak.forward::<VM, T>(trace, false);
self.phantom.forward::<VM, T>(trace, false);
}
pub fn scan_weak_refs<VM: VMBinding, T: TraceLocal>(&self, trace: &mut T, tls: OpaquePointer) {
self.soft.scan::<VM, T>(trace, false, false, tls);
self.weak.scan::<VM, T>(trace, false, false, tls);
}
pub fn scan_soft_refs<VM: VMBinding, T: TraceLocal>(&self, trace: &mut T, tls: OpaquePointer) {
self.soft.scan::<VM, T>(trace, false, false, tls);
}
pub fn scan_phantom_refs<VM: VMBinding, T: TraceLocal>(
&self,
trace: &mut T,
tls: OpaquePointer,
) {
self.phantom.scan::<VM, T>(trace, false, false, tls);
}
}
impl Default for ReferenceProcessors {
fn default() -> Self {
Self::new()
}
}
pub const TRACE: bool = false;
pub const TRACE_UNREACHABLE: bool = false;
pub const TRACE_DETAIL: bool = false;
pub const TRACE_FORWARD: bool = false;
const INITIAL_SIZE: usize = 256;
pub struct ReferenceProcessor {
sync: UnsafeCell<Mutex<ReferenceProcessorSync>>,
semantics: Semantics,
}
unsafe impl Sync for ReferenceProcessor {}
#[derive(Debug, PartialEq)]
pub enum Semantics {
SOFT,
WEAK,
PHANTOM,
}
struct ReferenceProcessorSync {
references: Vec<Address>,
unforwarded_references: Option<Vec<Address>>,
nursery_index: usize,
}
impl ReferenceProcessor {
pub fn new(semantics: Semantics) -> Self {
ReferenceProcessor {
sync: UnsafeCell::new(Mutex::new(ReferenceProcessorSync {
references: Vec::with_capacity(INITIAL_SIZE),
unforwarded_references: None,
nursery_index: 0,
})),
semantics,
}
}
fn sync(&self) -> &Mutex<ReferenceProcessorSync> {
unsafe { &*self.sync.get() }
}
#[allow(clippy::mut_from_ref)]
unsafe fn sync_mut(&self) -> &mut ReferenceProcessorSync {
(&mut *self.sync.get()).get_mut().unwrap()
}
pub fn clear(&self) {
let mut sync = self.sync().lock().unwrap();
sync.references.clear();
sync.unforwarded_references = None;
sync.nursery_index = 0;
}
pub fn add_candidate<VM: VMBinding>(&self, reff: ObjectReference, referent: ObjectReference) {
let mut sync = self.sync().lock().unwrap();
VM::VMReferenceGlue::set_referent(reff, referent);
sync.references.push(reff.to_address());
}
pub fn forward<VM: VMBinding, T: TraceLocal>(&self, trace: &mut T, _nursery: bool) {
let mut sync = unsafe { self.sync_mut() };
let references: &mut Vec<Address> = &mut sync.references;
let mut unforwarded_references: Vec<Address> = sync.unforwarded_references.clone().unwrap();
if TRACE {
trace!("Starting ReferenceProcessor.forward({:?})", self.semantics);
}
if TRACE_DETAIL {
trace!("{:?} Reference table is {:?}", self.semantics, references);
trace!(
"{:?} unforwardedReferences is {:?}",
self.semantics,
unforwarded_references
);
}
for (i, unforwarded_ref) in unforwarded_references
.iter_mut()
.enumerate()
.take(references.len())
{
let reference = unsafe { unforwarded_ref.to_object_reference() };
if TRACE_DETAIL {
trace!("slot {:?}: forwarding {:?}", i, reference);
}
VM::VMReferenceGlue::set_referent(
reference,
trace.get_forwarded_referent(VM::VMReferenceGlue::get_referent(reference)),
);
let new_reference = trace.get_forwarded_reference(reference);
*unforwarded_ref = new_reference.to_address();
}
if TRACE {
trace!("Ending ReferenceProcessor.forward({:?})", self.semantics)
}
sync.unforwarded_references = None;
}
fn scan<VM: VMBinding, T: TraceLocal>(
&self,
trace: &mut T,
nursery: bool,
retain: bool,
tls: OpaquePointer,
) {
let sync = unsafe { self.sync_mut() };
sync.unforwarded_references = Some(sync.references.clone());
let references: &mut Vec<Address> = &mut sync.references;
if TRACE {
trace!("Starting ReferenceProcessor.scan({:?})", self.semantics);
}
let mut to_index = if nursery { sync.nursery_index } else { 0 };
let from_index = to_index;
if TRACE_DETAIL {
trace!("{:?} Reference table is {:?}", self.semantics, references);
}
if retain {
for addr in references.iter().skip(from_index) {
let reference = unsafe { addr.to_object_reference() };
self.retain_referent::<VM, T>(trace, reference);
}
} else {
for i in from_index..references.len() {
let reference = unsafe { references[i].to_object_reference() };
let new_reference = VM::VMReferenceGlue::process_reference(trace, reference, tls);
if !new_reference.is_null() {
references[to_index] = new_reference.to_address();
to_index += 1;
if TRACE_DETAIL {
let index = to_index - 1;
trace!(
"SCANNED {} {:?} -> {:?}",
index,
references[index],
unsafe { references[index].to_object_reference() }
);
}
}
}
trace!(
"{:?} references: {} -> {}",
self.semantics,
references.len(),
to_index
);
sync.nursery_index = to_index;
references.truncate(to_index);
}
unsafe { VM::VMActivePlan::mutator(tls) }.flush_remembered_sets();
if TRACE {
trace!("Ending ReferenceProcessor.scan({:?})", self.semantics);
}
}
fn retain_referent<VM: VMBinding, T: TraceLocal>(
&self,
trace: &mut T,
reference: ObjectReference,
) {
debug_assert!(!reference.is_null());
debug_assert!(self.semantics == Semantics::SOFT);
if TRACE_DETAIL {
trace!("Processing reference: {:?}", reference);
}
if !reference.is_live() {
return;
}
let referent = VM::VMReferenceGlue::get_referent(reference);
if !referent.is_null() {
trace.retain_referent(referent);
}
if TRACE_DETAIL {
trace!(" ~> {:?} (retained)", referent.to_address());
}
}
}