use std::marker::PhantomData;
use crate::plan::PlanTraceObject;
use crate::policy::gc_work::TraceKind;
use crate::scheduler::{GCWorker, EDGES_WORK_BUFFER_SIZE};
use crate::util::{ObjectReference, VMThread, VMWorkerThread};
use crate::vm::{Scanning, VMBinding};
use crate::{Plan, MMTK};
pub(crate) mod gc_work;
pub trait Trace: 'static + Send + Clone {
type VM: VMBinding;
fn from_mmtk(mmtk: &'static MMTK<Self::VM>) -> Self;
fn trace_object<Q: ObjectQueue>(
&self,
worker: &mut GCWorker<Self::VM>,
object: ObjectReference,
queue: &mut Q,
) -> ObjectReference;
fn post_scan_object(&self, object: ObjectReference);
fn may_move_objects() -> bool;
}
pub type SlotOfTrace<T> = <<T as Trace>::VM as VMBinding>::VMSlot;
#[allow(dead_code)]
#[derive(Default)]
pub struct SFTTrace<VM: VMBinding> {
phantom_data: PhantomData<VM>,
}
impl<VM: VMBinding> Clone for SFTTrace<VM> {
fn clone(&self) -> Self {
Self {
phantom_data: PhantomData,
}
}
}
impl<VM: VMBinding> Trace for SFTTrace<VM> {
type VM = VM;
fn from_mmtk(_mmtk: &'static MMTK<Self::VM>) -> Self {
Default::default()
}
fn trace_object<Q: ObjectQueue>(
&self,
worker: &mut GCWorker<VM>,
object: ObjectReference,
queue: &mut Q,
) -> ObjectReference {
use crate::policy::sft::GCWorkerMutRef;
let worker = GCWorkerMutRef::new(worker);
let sft = unsafe { crate::mmtk::SFT_MAP.get_unchecked(object.to_raw_address()) };
let mut tmp_queue = None;
let result = sft.sft_trace_object(&mut tmp_queue, object, worker);
if let Some(queued_object) = tmp_queue {
queue.enqueue(queued_object);
}
result
}
fn post_scan_object(&self, _object: ObjectReference) {
}
fn may_move_objects() -> bool {
true
}
}
pub struct PlanTrace<P: Plan + PlanTraceObject<P::VM>, const KIND: TraceKind> {
plan: &'static P,
}
impl<P: Plan + PlanTraceObject<P::VM>, const KIND: TraceKind> Clone for PlanTrace<P, KIND> {
fn clone(&self) -> Self {
Self { plan: self.plan }
}
}
impl<P: Plan + PlanTraceObject<P::VM>, const KIND: TraceKind> Trace for PlanTrace<P, KIND> {
type VM = P::VM;
fn from_mmtk(mmtk: &'static MMTK<Self::VM>) -> Self {
let plan = mmtk.get_plan().downcast_ref::<P>().unwrap();
Self { plan }
}
fn trace_object<Q: ObjectQueue>(
&self,
worker: &mut GCWorker<Self::VM>,
object: ObjectReference,
queue: &mut Q,
) -> ObjectReference {
self.plan.trace_object::<Q, KIND>(queue, object, worker)
}
fn post_scan_object(&self, object: ObjectReference) {
self.plan.post_scan_object(object);
}
fn may_move_objects() -> bool {
P::may_move_objects::<KIND>()
}
}
#[derive(Default)]
pub struct UnsupportedTrace<VM: VMBinding> {
phantom_data: PhantomData<VM>,
}
impl<VM: VMBinding> Clone for UnsupportedTrace<VM> {
fn clone(&self) -> Self {
Self {
phantom_data: PhantomData,
}
}
}
impl<VM: VMBinding> Trace for UnsupportedTrace<VM> {
type VM = VM;
fn from_mmtk(_mmtk: &'static MMTK<Self::VM>) -> Self {
panic!("UnsupportedTrace cannot be constructed.")
}
fn trace_object<Q: ObjectQueue>(
&self,
_worker: &mut GCWorker<VM>,
_object: ObjectReference,
_queue: &mut Q,
) -> ObjectReference {
panic!("UnsupportedTrace::trace_object must not be called.")
}
fn post_scan_object(&self, _object: ObjectReference) {
panic!("UnsupportedTrace::post_scan_object must not be called.")
}
fn may_move_objects() -> bool {
panic!("UnsupportedTrace::may_move_objects must not be called.")
}
}
pub trait ObjectQueue {
fn enqueue(&mut self, object: ObjectReference);
}
impl<F: FnMut(ObjectReference)> ObjectQueue for F {
fn enqueue(&mut self, object: ObjectReference) {
self(object)
}
}
impl ObjectQueue for Option<ObjectReference> {
fn enqueue(&mut self, object: ObjectReference) {
debug_assert!(self.is_none());
*self = Some(object);
}
}
pub type OptionObjectQueue = Option<ObjectReference>;
pub type VectorObjectQueue = VectorQueue<ObjectReference>;
pub struct VectorQueue<T> {
buffer: Vec<T>,
}
impl<T> VectorQueue<T> {
const CAPACITY: usize = EDGES_WORK_BUFFER_SIZE;
pub fn new() -> Self {
Self { buffer: Vec::new() }
}
pub fn is_empty(&self) -> bool {
self.buffer.is_empty()
}
pub fn take(&mut self) -> Vec<T> {
std::mem::take(&mut self.buffer)
}
pub fn into_vec(self) -> Vec<T> {
self.buffer
}
pub fn is_full(&self) -> bool {
self.buffer.len() >= Self::CAPACITY
}
pub fn push(&mut self, v: T) {
if self.buffer.is_empty() {
self.buffer.reserve(Self::CAPACITY);
}
self.buffer.push(v);
}
pub fn len(&self) -> usize {
self.buffer.len()
}
pub fn clear(&mut self) {
self.buffer.clear()
}
}
impl<T> Default for VectorQueue<T> {
fn default() -> Self {
Self::new()
}
}
impl ObjectQueue for VectorQueue<ObjectReference> {
fn enqueue(&mut self, v: ObjectReference) {
self.push(v);
}
}
pub(crate) struct SlotIterator<VM: VMBinding> {
_p: PhantomData<VM>,
}
impl<VM: VMBinding> SlotIterator<VM> {
pub fn iterate_fields<F: FnMut(VM::VMSlot)>(object: ObjectReference, _tls: VMThread, mut f: F) {
let fake_tls = VMWorkerThread(VMThread::UNINITIALIZED);
if !<VM::VMScanning as Scanning<VM>>::support_slot_enqueuing(fake_tls, object) {
panic!("SlotIterator::iterate_fields cannot be used on objects that don't support slot-enqueuing");
}
<VM::VMScanning as Scanning<VM>>::scan_object(fake_tls, object, &mut f);
}
}