use crate::plan::global::CommonPlan;
use crate::plan::global::CreateSpecificPlanArgs;
use crate::plan::ObjectQueue;
use crate::plan::Plan;
use crate::policy::copyspace::CopySpace;
use crate::policy::gc_work::{TraceKind, TRACE_KIND_TRANSITIVE_PIN};
use crate::policy::space::Space;
use crate::scheduler::*;
use crate::util::copy::CopySemantics;
use crate::util::heap::gc_trigger::SpaceStats;
use crate::util::heap::VMRequest;
use crate::util::statistics::counter::EventCounter;
use crate::util::Address;
use crate::util::ObjectReference;
use crate::util::VMWorkerThread;
use crate::vm::{ObjectModel, VMBinding};
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;
use std::sync::{Arc, Mutex};
use mmtk_macros::{HasSpaces, PlanTraceObject};
#[derive(HasSpaces, PlanTraceObject)]
pub struct CommonGenPlan<VM: VMBinding> {
#[space]
#[copy_semantics(CopySemantics::PromoteToMature)]
pub nursery: CopySpace<VM>,
#[parent]
pub common: CommonPlan<VM>,
pub gc_full_heap: AtomicBool,
pub next_gc_full_heap: AtomicBool,
pub full_heap_gc_count: Arc<Mutex<EventCounter>>,
}
impl<VM: VMBinding> CommonGenPlan<VM> {
pub fn new(mut args: CreateSpecificPlanArgs<VM>) -> Self {
let nursery = CopySpace::new(
args.get_nursery_space_args("nursery", true, false, VMRequest::discontiguous()),
true,
);
let full_heap_gc_count = args
.global_args
.stats
.new_event_counter("majorGC", true, true);
let common = CommonPlan::new(args);
CommonGenPlan {
nursery,
common,
gc_full_heap: AtomicBool::default(),
next_gc_full_heap: AtomicBool::new(false),
full_heap_gc_count,
}
}
pub fn prepare(&mut self, tls: VMWorkerThread) {
let full_heap = !self.is_current_gc_nursery();
if full_heap {
self.full_heap_gc_count.lock().unwrap().inc();
}
self.common.prepare(tls, full_heap);
self.nursery.prepare(true);
self.nursery
.set_copy_for_sft_trace(Some(CopySemantics::PromoteToMature));
}
pub fn release(&mut self, tls: VMWorkerThread) {
let full_heap = !self.is_current_gc_nursery();
self.common.release(tls, full_heap);
self.nursery.release();
}
pub fn end_of_gc(&mut self, tls: VMWorkerThread, next_gc_full_heap: bool) {
self.set_next_gc_full_heap(next_gc_full_heap);
self.common.end_of_gc(tls);
}
fn virtual_memory_exhausted(plan: &dyn GenerationalPlan<VM = VM>) -> bool {
((plan.get_collection_reserved_pages() as f64
* VM::VMObjectModel::VM_WORST_CASE_COPY_EXPANSION) as usize)
> plan.get_mature_physical_pages_available()
}
pub fn collection_required<P: Plan<VM = VM>>(
&self,
plan: &P,
space_full: bool,
space: Option<SpaceStats<VM>>,
) -> bool {
let cur_nursery = self.nursery.reserved_pages();
let max_nursery = self.common.base.gc_trigger.get_max_nursery_pages();
let nursery_full = cur_nursery >= max_nursery;
trace!(
"nursery_full = {:?} (nursery = {}, max_nursery = {})",
nursery_full,
cur_nursery,
max_nursery,
);
if nursery_full {
return true;
}
if Self::virtual_memory_exhausted(plan.generational().unwrap()) {
return true;
}
let is_triggered_by_nursery =
space.is_some_and(|s| s.0.common().descriptor == self.nursery.common().descriptor);
if space_full && !is_triggered_by_nursery {
self.next_gc_full_heap.store(true, Ordering::SeqCst);
}
self.common.base.collection_required(plan, space_full)
}
pub fn force_full_heap_collection(&self) {
self.next_gc_full_heap.store(true, Ordering::SeqCst);
}
pub fn last_collection_full_heap(&self) -> bool {
self.gc_full_heap.load(Ordering::Relaxed)
}
pub fn requires_full_heap_collection<P: Plan<VM = VM>>(&self, plan: &P) -> bool {
#[allow(clippy::if_same_then_else, clippy::needless_bool)]
let is_full_heap = if crate::plan::generational::FULL_NURSERY_GC {
trace!("full heap: forced full heap");
true
} else if self
.common
.base
.global_state
.user_triggered_collection
.load(Ordering::SeqCst)
&& *self.common.base.options.full_heap_system_gc
{
trace!("full heap: user triggered");
true
} else if self.next_gc_full_heap.load(Ordering::SeqCst)
|| self
.common
.base
.global_state
.cur_collection_attempts
.load(Ordering::SeqCst)
> 1
{
trace!(
"full heap: next_gc_full_heap = {}, cur_collection_attempts = {}",
self.next_gc_full_heap.load(Ordering::SeqCst),
self.common
.base
.global_state
.cur_collection_attempts
.load(Ordering::SeqCst)
);
true
} else if Self::virtual_memory_exhausted(plan.generational().unwrap()) {
trace!("full heap: virtual memory exhausted");
true
} else {
false
};
self.gc_full_heap.store(is_full_heap, Ordering::SeqCst);
info!(
"{}",
if is_full_heap {
"Full heap GC"
} else {
"Nursery GC"
}
);
is_full_heap
}
pub fn trace_object_nursery<Q: ObjectQueue, const KIND: TraceKind>(
&self,
queue: &mut Q,
object: ObjectReference,
worker: &mut GCWorker<VM>,
) -> ObjectReference {
assert!(
KIND != TRACE_KIND_TRANSITIVE_PIN,
"A copying nursery cannot pin objects"
);
if self.nursery.in_space(object) {
return self.nursery.trace_object::<Q>(
queue,
object,
Some(CopySemantics::PromoteToMature),
worker,
);
}
if self.common.get_los().in_space(object) {
return self.common.get_los().trace_object::<Q>(queue, object);
}
object
}
pub fn is_current_gc_nursery(&self) -> bool {
!self.gc_full_heap.load(Ordering::SeqCst)
}
pub fn should_next_gc_be_full_heap(plan: &dyn Plan<VM = VM>) -> bool {
let available = plan.get_available_pages();
let min_nursery = plan.base().gc_trigger.get_min_nursery_pages();
let next_gc_full_heap = available < min_nursery;
trace!(
"next gc will be full heap? {}, available pages = {}, min nursery = {}",
next_gc_full_heap,
available,
min_nursery
);
next_gc_full_heap
}
pub fn set_next_gc_full_heap(&self, next_gc_full_heap: bool) {
self.next_gc_full_heap
.store(next_gc_full_heap, Ordering::SeqCst);
}
pub fn get_collection_reserved_pages(&self) -> usize {
self.nursery.reserved_pages()
}
pub fn get_used_pages(&self) -> usize {
self.nursery.reserved_pages() + self.common.get_used_pages()
}
}
pub trait GenerationalPlan: Plan {
fn is_current_gc_nursery(&self) -> bool;
fn is_object_in_nursery(&self, object: ObjectReference) -> bool;
fn is_address_in_nursery(&self, addr: Address) -> bool;
fn get_mature_physical_pages_available(&self) -> usize;
fn get_mature_reserved_pages(&self) -> usize;
fn last_collection_full_heap(&self) -> bool;
fn force_full_heap_collection(&self);
}
pub trait GenerationalPlanExt<VM: VMBinding>: GenerationalPlan<VM = VM> {
fn trace_object_nursery<Q: ObjectQueue, const KIND: TraceKind>(
&self,
queue: &mut Q,
object: ObjectReference,
worker: &mut GCWorker<VM>,
) -> ObjectReference;
}
pub fn is_nursery_gc<VM: VMBinding>(plan: &dyn Plan<VM = VM>) -> bool {
plan.generational()
.is_some_and(|plan| plan.is_current_gc_nursery())
}