use super::gc_work::GenImmixMatureGCWorkContext;
use super::gc_work::GenImmixNurseryGCWorkContext;
use crate::plan::generational::global::CommonGenPlan;
use crate::plan::generational::global::GenerationalPlan;
use crate::plan::global::BasePlan;
use crate::plan::global::CommonPlan;
use crate::plan::global::CreateGeneralPlanArgs;
use crate::plan::global::CreateSpecificPlanArgs;
use crate::plan::AllocationSemantics;
use crate::plan::Plan;
use crate::plan::PlanConstraints;
use crate::policy::gc_work::TraceKind;
use crate::policy::immix::ImmixSpace;
use crate::policy::immix::ImmixSpaceArgs;
use crate::policy::immix::{TRACE_KIND_DEFRAG, TRACE_KIND_FAST};
use crate::policy::space::Space;
use crate::scheduler::GCWorkScheduler;
use crate::scheduler::GCWorker;
use crate::util::alloc::allocators::AllocatorSelector;
use crate::util::copy::*;
use crate::util::heap::gc_trigger::SpaceStats;
use crate::util::heap::VMRequest;
use crate::util::Address;
use crate::util::ObjectReference;
use crate::util::VMWorkerThread;
use crate::vm::*;
use crate::ObjectQueue;
use enum_map::EnumMap;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;
use mmtk_macros::{HasSpaces, PlanTraceObject};
#[derive(HasSpaces, PlanTraceObject)]
pub struct GenImmix<VM: VMBinding> {
#[parent]
pub gen: CommonGenPlan<VM>,
#[post_scan]
#[space]
#[copy_semantics(CopySemantics::Mature)]
pub immix_space: ImmixSpace<VM>,
pub last_gc_was_defrag: AtomicBool,
pub last_gc_was_full_heap: AtomicBool,
}
pub const GENIMMIX_CONSTRAINTS: PlanConstraints = PlanConstraints {
max_non_los_default_alloc_bytes: crate::util::rust_util::min_of_usize(
crate::policy::immix::MAX_IMMIX_OBJECT_SIZE,
crate::plan::generational::GEN_CONSTRAINTS.max_non_los_default_alloc_bytes,
),
..crate::plan::generational::GEN_CONSTRAINTS
};
impl<VM: VMBinding> Plan for GenImmix<VM> {
fn constraints(&self) -> &'static PlanConstraints {
&GENIMMIX_CONSTRAINTS
}
fn create_copy_config(&'static self) -> CopyConfig<Self::VM> {
use enum_map::enum_map;
CopyConfig {
copy_mapping: enum_map! {
CopySemantics::PromoteToMature => CopySelector::ImmixHybrid(0),
CopySemantics::Mature => CopySelector::ImmixHybrid(0),
_ => CopySelector::Unused,
},
space_mapping: vec![(CopySelector::ImmixHybrid(0), &self.immix_space)],
constraints: &GENIMMIX_CONSTRAINTS,
}
}
fn last_collection_was_exhaustive(&self) -> bool {
self.last_gc_was_full_heap.load(Ordering::Relaxed)
&& ImmixSpace::<VM>::is_last_gc_exhaustive(
self.last_gc_was_defrag.load(Ordering::Relaxed),
)
}
fn collection_required(&self, space_full: bool, space: Option<SpaceStats<Self::VM>>) -> bool
where
Self: Sized,
{
self.gen.collection_required(self, space_full, space)
}
#[allow(clippy::if_same_then_else)]
#[allow(clippy::branches_sharing_code)]
fn schedule_collection(&'static self, scheduler: &GCWorkScheduler<Self::VM>) {
let is_full_heap = self.requires_full_heap_collection();
probe!(mmtk, gen_full_heap, is_full_heap);
if !is_full_heap {
info!("Nursery GC");
scheduler.schedule_common_work::<GenImmixNurseryGCWorkContext<VM>>(self);
} else {
info!("Full heap GC");
crate::plan::immix::Immix::schedule_immix_full_heap_collection::<
GenImmix<VM>,
GenImmixMatureGCWorkContext<VM, TRACE_KIND_FAST>,
GenImmixMatureGCWorkContext<VM, TRACE_KIND_DEFRAG>,
>(self, &self.immix_space, scheduler);
}
}
fn get_allocator_mapping(&self) -> &'static EnumMap<AllocationSemantics, AllocatorSelector> {
&super::mutator::ALLOCATOR_MAPPING
}
fn prepare(&mut self, tls: VMWorkerThread) {
let full_heap = !self.gen.is_current_gc_nursery();
self.gen.prepare(tls);
if full_heap {
self.immix_space.prepare(
full_heap,
crate::policy::immix::defrag::StatsForDefrag::new(self),
);
}
}
fn release(&mut self, tls: VMWorkerThread) {
let full_heap = !self.gen.is_current_gc_nursery();
self.gen.release(tls);
if full_heap {
self.immix_space.release(full_heap);
}
self.last_gc_was_full_heap
.store(full_heap, Ordering::Relaxed);
}
fn end_of_gc(&mut self, _tls: VMWorkerThread) {
self.gen
.set_next_gc_full_heap(CommonGenPlan::should_next_gc_be_full_heap(self));
let did_defrag = self.immix_space.end_of_gc();
self.last_gc_was_defrag.store(did_defrag, Ordering::Relaxed);
}
fn current_gc_may_move_object(&self) -> bool {
if self.is_current_gc_nursery() {
true
} else {
self.immix_space.in_defrag()
}
}
fn get_collection_reserved_pages(&self) -> usize {
self.gen.get_collection_reserved_pages() + self.immix_space.defrag_headroom_pages()
}
fn get_used_pages(&self) -> usize {
self.gen.get_used_pages() + self.immix_space.reserved_pages()
}
fn get_available_pages(&self) -> usize {
(self
.get_total_pages()
.saturating_sub(self.get_reserved_pages()))
>> 1
}
fn base(&self) -> &BasePlan<VM> {
&self.gen.common.base
}
fn base_mut(&mut self) -> &mut BasePlan<Self::VM> {
&mut self.gen.common.base
}
fn common(&self) -> &CommonPlan<VM> {
&self.gen.common
}
fn generational(&self) -> Option<&dyn GenerationalPlan<VM = VM>> {
Some(self)
}
}
impl<VM: VMBinding> GenerationalPlan for GenImmix<VM> {
fn is_current_gc_nursery(&self) -> bool {
self.gen.is_current_gc_nursery()
}
fn is_object_in_nursery(&self, object: ObjectReference) -> bool {
self.gen.nursery.in_space(object)
}
fn is_address_in_nursery(&self, addr: Address) -> bool {
self.gen.nursery.address_in_space(addr)
}
fn get_mature_physical_pages_available(&self) -> usize {
self.immix_space.available_physical_pages()
}
fn get_mature_reserved_pages(&self) -> usize {
self.immix_space.reserved_pages()
}
fn force_full_heap_collection(&self) {
self.gen.force_full_heap_collection()
}
fn last_collection_full_heap(&self) -> bool {
self.gen.last_collection_full_heap()
}
}
impl<VM: VMBinding> crate::plan::generational::global::GenerationalPlanExt<VM> for GenImmix<VM> {
fn trace_object_nursery<Q: ObjectQueue, const KIND: TraceKind>(
&self,
queue: &mut Q,
object: ObjectReference,
worker: &mut GCWorker<VM>,
) -> ObjectReference {
self.gen
.trace_object_nursery::<Q, KIND>(queue, object, worker)
}
}
impl<VM: VMBinding> GenImmix<VM> {
pub fn new(args: CreateGeneralPlanArgs<VM>) -> Self {
let mut plan_args = CreateSpecificPlanArgs {
global_args: args,
constraints: &GENIMMIX_CONSTRAINTS,
global_side_metadata_specs:
crate::plan::generational::new_generational_global_metadata_specs::<VM>(),
};
let immix_space = ImmixSpace::new(
plan_args.get_space_args("immix_mature", true, false, VMRequest::discontiguous()),
ImmixSpaceArgs {
reset_log_bit_in_major_gc: false,
unlog_object_when_traced: false,
#[cfg(feature = "vo_bit")]
mixed_age: false,
},
);
let genimmix = GenImmix {
gen: CommonGenPlan::new(plan_args),
immix_space,
last_gc_was_defrag: AtomicBool::new(false),
last_gc_was_full_heap: AtomicBool::new(false),
};
genimmix.verify_side_metadata_sanity();
genimmix
}
fn requires_full_heap_collection(&self) -> bool {
self.gen.requires_full_heap_collection(self)
}
}