From 7433025d7099995a183ca302a67dd3a2c3e4dc1c Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Fri, 15 Jul 2022 10:17:08 +1000 Subject: [PATCH 01/14] Instantiate MMTk in gc_init() --- docs/tutorial/src/mygc/create.md | 6 +-- src/memory_manager.rs | 51 +++++++++--------- src/mmtk.rs | 69 +++++++++++++++++------- src/plan/barriers.rs | 4 +- src/plan/generational/copying/mutator.rs | 6 +-- src/plan/generational/immix/mutator.rs | 6 +-- src/plan/global.rs | 14 ++--- src/plan/markcompact/gc_work.rs | 6 +-- src/plan/marksweep/gc_work.rs | 2 +- src/scheduler/gc_work.rs | 48 ++++++++--------- src/scheduler/scheduler.rs | 4 +- src/scheduler/worker.rs | 2 +- src/util/analysis/mod.rs | 2 +- src/util/finalizable_processor.rs | 10 ++-- src/util/malloc/mod.rs | 10 ++-- src/util/reference_processor.rs | 28 +++++----- src/util/sanity/sanity_checker.rs | 14 ++--- src/util/statistics/stats.rs | 2 +- 18 files changed, 159 insertions(+), 125 deletions(-) diff --git a/docs/tutorial/src/mygc/create.md b/docs/tutorial/src/mygc/create.md index fa54825142..de7fbe965e 100644 --- a/docs/tutorial/src/mygc/create.md +++ b/docs/tutorial/src/mygc/create.md @@ -53,15 +53,15 @@ files. mmtk: &'static MMTK, ) -> Box> { Box::new(match mmtk.options.plan { - PlanSelector::NoGC => crate::plan::nogc::mutator::create_nogc_mutator(tls, &*mmtk.plan), + PlanSelector::NoGC => crate::plan::nogc::mutator::create_nogc_mutator(tls, &*mmtk.get().plan), PlanSelector::SemiSpace => { - crate::plan::semispace::mutator::create_ss_mutator(tls, &*mmtk.plan) + crate::plan::semispace::mutator::create_ss_mutator(tls, &*mmtk.get().plan) } // ... // Create MyGC mutator based on selector - PlanSelector::MyGC => crate::plan::mygc::mutator::create_mygc_mutator(tls, &*mmtk.plan), }) + PlanSelector::MyGC => crate::plan::mygc::mutator::create_mygc_mutator(tls, &*mmtk.get().plan), }) } pub fn create_plan( diff --git a/src/memory_manager.rs b/src/memory_manager.rs index 5509344dad..86d2323c16 100644 --- a/src/memory_manager.rs +++ b/src/memory_manager.rs @@ -60,7 +60,8 @@ pub fn gc_init(mmtk: &'static mut MMTK, heap_size: usize) { } } assert!(heap_size > 0, "Invalid heap size"); - mmtk.plan.gc_init(heap_size, &crate::VM_MAP); + mmtk.instantiate(); + mmtk.get_mut().plan.gc_init(heap_size, &crate::VM_MAP); info!("Initialized MMTk with {:?}", *mmtk.options.plan); #[cfg(feature = "extreme_assertions")] warn!("The feature 'extreme_assertions' is enabled. MMTk will run expensive run-time checks. Slow performance should be expected."); @@ -150,7 +151,7 @@ pub fn get_allocator_mapping( mmtk: &MMTK, semantics: AllocationSemantics, ) -> AllocatorSelector { - mmtk.plan.get_allocator_mapping()[semantics] + mmtk.get().plan.get_allocator_mapping()[semantics] } /// The standard malloc. MMTk either uses its own allocator, or forward the call to a @@ -272,11 +273,11 @@ pub fn start_worker( /// Collection::spawn_gc_thread() so that the VM knows the context. pub fn initialize_collection(mmtk: &'static MMTK, tls: VMThread) { assert!( - !mmtk.plan.is_initialized(), + !mmtk.get().plan.is_initialized(), "MMTk collection has been initialized (was initialize_collection() already called before?)" ); - mmtk.scheduler.spawn_gc_threads(mmtk, tls); - mmtk.plan.base().initialized.store(true, Ordering::SeqCst); + mmtk.get().scheduler.spawn_gc_threads(mmtk, tls); + mmtk.get().plan.base().initialized.store(true, Ordering::SeqCst); } /// Allow MMTk to trigger garbage collection when heap is full. This should only be used in pair with disable_collection(). @@ -287,10 +288,10 @@ pub fn initialize_collection(mmtk: &'static MMTK, tls: VMThre /// * `mmtk`: A reference to an MMTk instance. pub fn enable_collection(mmtk: &'static MMTK) { debug_assert!( - !mmtk.plan.should_trigger_gc_when_heap_is_full(), + !mmtk.get().plan.should_trigger_gc_when_heap_is_full(), "enable_collection() is called when GC is already enabled." ); - mmtk.plan + mmtk.get().plan .base() .trigger_gc_when_heap_is_full .store(true, Ordering::SeqCst); @@ -308,10 +309,10 @@ pub fn enable_collection(mmtk: &'static MMTK) { /// * `mmtk`: A reference to an MMTk instance. pub fn disable_collection(mmtk: &'static MMTK) { debug_assert!( - mmtk.plan.should_trigger_gc_when_heap_is_full(), + mmtk.get().plan.should_trigger_gc_when_heap_is_full(), "disable_collection() is called when GC is not enabled." ); - mmtk.plan + mmtk.get().plan .base() .trigger_gc_when_heap_is_full .store(false, Ordering::SeqCst); @@ -343,7 +344,7 @@ pub fn process_bulk(mmtk: &'static MMTK, options: &str) -> bo /// Arguments: /// * `mmtk`: A reference to an MMTk instance. pub fn used_bytes(mmtk: &MMTK) -> usize { - mmtk.plan.get_used_pages() << LOG_BYTES_IN_PAGE + mmtk.get().plan.get_used_pages() << LOG_BYTES_IN_PAGE } /// Return free memory in bytes. @@ -351,7 +352,7 @@ pub fn used_bytes(mmtk: &MMTK) -> usize { /// Arguments: /// * `mmtk`: A reference to an MMTk instance. pub fn free_bytes(mmtk: &MMTK) -> usize { - mmtk.plan.get_free_pages() << LOG_BYTES_IN_PAGE + mmtk.get().plan.get_free_pages() << LOG_BYTES_IN_PAGE } /// Return the starting address of the heap. *Note that currently MMTk uses @@ -371,7 +372,7 @@ pub fn last_heap_address() -> Address { /// Arguments: /// * `mmtk`: A reference to an MMTk instance. pub fn total_bytes(mmtk: &MMTK) -> usize { - mmtk.plan.get_total_pages() << LOG_BYTES_IN_PAGE + mmtk.get().plan.get_total_pages() << LOG_BYTES_IN_PAGE } /// Trigger a garbage collection as requested by the user. @@ -380,7 +381,7 @@ pub fn total_bytes(mmtk: &MMTK) -> usize { /// * `mmtk`: A reference to an MMTk instance. /// * `tls`: The thread that triggers this collection request. pub fn handle_user_collection_request(mmtk: &MMTK, tls: VMMutatorThread) { - mmtk.plan.handle_user_collection_request(tls, false); + mmtk.get().plan.handle_user_collection_request(tls, false); } /// Is the object alive? @@ -492,7 +493,7 @@ pub fn is_mapped_address(address: Address) -> bool { /// * `mmtk`: A reference to an MMTk instance. /// * `object`: The object to check. pub fn modify_check(mmtk: &MMTK, object: ObjectReference) { - mmtk.plan.modify_check(object); + mmtk.get().plan.modify_check(object); } /// Add a reference to the list of weak references. A binding may @@ -502,7 +503,7 @@ pub fn modify_check(mmtk: &MMTK, object: ObjectReference) { /// * `mmtk`: A reference to an MMTk instance. /// * `reff`: The weak reference to add. pub fn add_weak_candidate(mmtk: &MMTK, reff: ObjectReference) { - mmtk.reference_processors.add_weak_candidate::(reff); + mmtk.get().reference_processors.add_weak_candidate::(reff); } /// Add a reference to the list of soft references. A binding may @@ -512,7 +513,7 @@ pub fn add_weak_candidate(mmtk: &MMTK, reff: ObjectReference) /// * `mmtk`: A reference to an MMTk instance. /// * `reff`: The soft reference to add. pub fn add_soft_candidate(mmtk: &MMTK, reff: ObjectReference) { - mmtk.reference_processors.add_soft_candidate::(reff); + mmtk.get().reference_processors.add_soft_candidate::(reff); } /// Add a reference to the list of phantom references. A binding may @@ -522,7 +523,7 @@ pub fn add_soft_candidate(mmtk: &MMTK, reff: ObjectReference) /// * `mmtk`: A reference to an MMTk instance. /// * `reff`: The phantom reference to add. pub fn add_phantom_candidate(mmtk: &MMTK, reff: ObjectReference) { - mmtk.reference_processors.add_phantom_candidate::(reff); + mmtk.get().reference_processors.add_phantom_candidate::(reff); } /// Generic hook to allow benchmarks to be harnessed. We do a full heap @@ -559,7 +560,7 @@ pub fn add_finalizer( warn!("add_finalizer() is called when no_finalizer = true"); } - mmtk.finalizable_processor.lock().unwrap().add(object); + mmtk.get().finalizable_processor.lock().unwrap().add(object); } /// Get an object that is ready for finalization. After each GC, if any registered object is not @@ -577,7 +578,7 @@ pub fn get_finalized_object( warn!("get_finalized_object() is called when no_finalizer = true"); } - mmtk.finalizable_processor + mmtk.get().finalizable_processor .lock() .unwrap() .get_ready_object() @@ -597,7 +598,7 @@ pub fn get_all_finalizers( warn!("get_all_finalizers() is called when no_finalizer = true"); } - mmtk.finalizable_processor + mmtk.get().finalizable_processor .lock() .unwrap() .get_all_finalizers() @@ -617,7 +618,7 @@ pub fn get_finalizers_for( warn!("get_finalizers() is called when no_finalizer = true"); } - mmtk.finalizable_processor + mmtk.get().finalizable_processor .lock() .unwrap() .get_finalizers_for(object) @@ -630,7 +631,7 @@ pub fn get_finalizers_for( /// Arguments: /// * `mmtk`: A reference to an MMTk instance. pub fn num_of_workers(mmtk: &'static MMTK) -> usize { - mmtk.scheduler.num_workers() + mmtk.get().scheduler.num_workers() } /// Add a work packet to the given work bucket. Note that this simply adds the work packet to the given @@ -645,7 +646,7 @@ pub fn add_work_packet>( bucket: WorkBucketStage, packet: W, ) { - mmtk.scheduler.work_buckets[bucket].add(packet) + mmtk.get().scheduler.work_buckets[bucket].add(packet) } /// Bulk add a number of work packets to the given work bucket. Note that this simply adds the work packets @@ -660,11 +661,11 @@ pub fn add_work_packets( bucket: WorkBucketStage, packets: Vec>>, ) { - mmtk.scheduler.work_buckets[bucket].bulk_add(packets) + mmtk.get().scheduler.work_buckets[bucket].bulk_add(packets) } /// Add a callback to be notified after the transitive closure is finished. /// The callback should return true if it add more work packets to the closure bucket. pub fn on_closure_end(mmtk: &'static MMTK, f: Box bool>) { - mmtk.scheduler.on_closure_end(f) + mmtk.get().scheduler.on_closure_end(f) } diff --git a/src/mmtk.rs b/src/mmtk.rs index 51c14ddd68..3f6716c688 100644 --- a/src/mmtk.rs +++ b/src/mmtk.rs @@ -18,6 +18,8 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use std::sync::Mutex; +use std::mem::MaybeUninit; + lazy_static! { // I am not sure if we should include these mmappers as part of MMTk struct. // The considerations are: @@ -42,25 +44,24 @@ pub static SFT_MAP: InitializeOnce> = InitializeOnce::new(); /// An MMTk instance. MMTk allows multiple instances to run independently, and each instance gives users a separate heap. /// *Note that multi-instances is not fully supported yet* pub struct MMTK { + pub(crate) options: Arc, + pub(crate) instance: MaybeUninit>, + pub(crate) instantiated: AtomicBool, +} + +pub struct MMTKInner { pub(crate) plan: Box>, pub(crate) reference_processors: ReferenceProcessors, pub(crate) finalizable_processor: Mutex>::FinalizableType>>, - pub(crate) options: Arc, pub(crate) scheduler: Arc>, #[cfg(feature = "sanity")] pub(crate) sanity_checker: Mutex, inside_harness: AtomicBool, } -impl MMTK { - pub fn new() -> Self { - // Initialize SFT first in case we need to use this in the constructor. - // The first call will initialize SFT map. Other calls will be blocked until SFT map is initialized. - SFT_MAP.initialize_once(&SFTMap::new); - - let options = Arc::new(UnsafeOptionsWrapper::new(Options::default())); - +impl MMTKInner { + pub fn new(options: Arc) -> Self { let num_workers = if cfg!(feature = "single_worker") { 1 } else { @@ -75,35 +76,67 @@ impl MMTK { options.clone(), scheduler.clone(), ); - MMTK { + + MMTKInner { plan, reference_processors: ReferenceProcessors::new(), finalizable_processor: Mutex::new(FinalizableProcessor::< >::FinalizableType, >::new()), - options, scheduler, #[cfg(feature = "sanity")] sanity_checker: Mutex::new(SanityChecker::new()), inside_harness: AtomicBool::new(false), } } +} + +impl MMTK { + pub fn new() -> Self { + // Initialize SFT first in case we need to use this in the constructor. + // The first call will initialize SFT map. Other calls will be blocked until SFT map is initialized. + SFT_MAP.initialize_once(&SFTMap::new); + + let options = Arc::new(UnsafeOptionsWrapper::new(Options::default())); + MMTK { + options, + instance: MaybeUninit::>::uninit(), + instantiated: AtomicBool::new(false), + } + } + + pub(crate) fn instantiate(&mut self) { + self.instance.write(MMTKInner::new(self.options.clone())); + self.instantiated.store(true, Ordering::SeqCst); + } + + #[inline(always)] + pub fn get(&self) -> &MMTKInner { + debug_assert!(self.instantiated.load(Ordering::SeqCst), "MMTK is not instantiated (is gc_init() called?)"); + unsafe { self.instance.assume_init_ref() } + } + + #[inline(always)] + pub fn get_mut(&mut self) -> &mut MMTKInner { + debug_assert!(self.instantiated.load(Ordering::SeqCst), "MMTK is not instantiated (is gc_init() called?)"); + unsafe { self.instance.assume_init_mut() } + } pub fn harness_begin(&self, tls: VMMutatorThread) { // FIXME Do a full heap GC if we have generational GC - self.plan.handle_user_collection_request(tls, true); - self.inside_harness.store(true, Ordering::SeqCst); - self.plan.base().stats.start_all(); - self.scheduler.enable_stat(); + self.get().plan.handle_user_collection_request(tls, true); + self.get().inside_harness.store(true, Ordering::SeqCst); + self.get().plan.base().stats.start_all(); + self.get().scheduler.enable_stat(); } pub fn harness_end(&'static self) { - self.plan.base().stats.stop_all(self); - self.inside_harness.store(false, Ordering::SeqCst); + self.get().plan.base().stats.stop_all(self); + self.get().inside_harness.store(false, Ordering::SeqCst); } pub fn get_plan(&self) -> &dyn Plan { - self.plan.as_ref() + self.get().plan.as_ref() } #[inline(always)] diff --git a/src/plan/barriers.rs b/src/plan/barriers.rs index f1245131bf..e1cc19863f 100644 --- a/src/plan/barriers.rs +++ b/src/plan/barriers.rs @@ -101,12 +101,12 @@ impl Barrier for ObjectRememberingBarrier { let mut modbuf = vec![]; std::mem::swap(&mut modbuf, &mut self.modbuf); debug_assert!( - !self.mmtk.scheduler.work_buckets[WorkBucketStage::Final].is_activated(), + !self.mmtk.get().scheduler.work_buckets[WorkBucketStage::Final].is_activated(), "{:?}", self as *const _ ); if !modbuf.is_empty() { - self.mmtk.scheduler.work_buckets[WorkBucketStage::Closure] + self.mmtk.get().scheduler.work_buckets[WorkBucketStage::Closure] .add(ProcessModBuf::::new(modbuf, self.meta)); } } diff --git a/src/plan/generational/copying/mutator.rs b/src/plan/generational/copying/mutator.rs index b1bb2ecaa6..4b3fcbce70 100644 --- a/src/plan/generational/copying/mutator.rs +++ b/src/plan/generational/copying/mutator.rs @@ -32,16 +32,16 @@ pub fn create_gencopy_mutator( mutator_tls: VMMutatorThread, mmtk: &'static MMTK, ) -> Mutator { - let gencopy = mmtk.plan.downcast_ref::>().unwrap(); + let gencopy = mmtk.get().plan.downcast_ref::>().unwrap(); let config = MutatorConfig { allocator_mapping: &*ALLOCATOR_MAPPING, - space_mapping: Box::new(create_gen_space_mapping(&*mmtk.plan, &gencopy.gen.nursery)), + space_mapping: Box::new(create_gen_space_mapping(&*mmtk.get().plan, &gencopy.gen.nursery)), prepare_func: &gencopy_mutator_prepare, release_func: &gencopy_mutator_release, }; Mutator { - allocators: Allocators::::new(mutator_tls, &*mmtk.plan, &config.space_mapping), + allocators: Allocators::::new(mutator_tls, &*mmtk.get().plan, &config.space_mapping), barrier: Box::new(ObjectRememberingBarrier::>::new( mmtk, *VM::VMObjectModel::GLOBAL_LOG_BIT_SPEC, diff --git a/src/plan/generational/immix/mutator.rs b/src/plan/generational/immix/mutator.rs index ab38569713..13dd6527a9 100644 --- a/src/plan/generational/immix/mutator.rs +++ b/src/plan/generational/immix/mutator.rs @@ -30,16 +30,16 @@ pub fn create_genimmix_mutator( mutator_tls: VMMutatorThread, mmtk: &'static MMTK, ) -> Mutator { - let genimmix = mmtk.plan.downcast_ref::>().unwrap(); + let genimmix = mmtk.get().plan.downcast_ref::>().unwrap(); let config = MutatorConfig { allocator_mapping: &*ALLOCATOR_MAPPING, - space_mapping: Box::new(create_gen_space_mapping(&*mmtk.plan, &genimmix.gen.nursery)), + space_mapping: Box::new(create_gen_space_mapping(&*mmtk.get().plan, &genimmix.gen.nursery)), prepare_func: &genimmix_mutator_prepare, release_func: &genimmix_mutator_release, }; Mutator { - allocators: Allocators::::new(mutator_tls, &*mmtk.plan, &config.space_mapping), + allocators: Allocators::::new(mutator_tls, &*mmtk.get().plan, &config.space_mapping), barrier: Box::new(ObjectRememberingBarrier::>::new( mmtk, *VM::VMObjectModel::GLOBAL_LOG_BIT_SPEC, diff --git a/src/plan/global.rs b/src/plan/global.rs index 871c2c7472..501ffdb191 100644 --- a/src/plan/global.rs +++ b/src/plan/global.rs @@ -40,9 +40,9 @@ pub fn create_mutator( mmtk: &'static MMTK, ) -> Box> { Box::new(match *mmtk.options.plan { - PlanSelector::NoGC => crate::plan::nogc::mutator::create_nogc_mutator(tls, &*mmtk.plan), + PlanSelector::NoGC => crate::plan::nogc::mutator::create_nogc_mutator(tls, &*mmtk.get().plan), PlanSelector::SemiSpace => { - crate::plan::semispace::mutator::create_ss_mutator(tls, &*mmtk.plan) + crate::plan::semispace::mutator::create_ss_mutator(tls, &*mmtk.get().plan) } PlanSelector::GenCopy => { crate::plan::generational::copying::mutator::create_gencopy_mutator(tls, mmtk) @@ -51,14 +51,14 @@ pub fn create_mutator( crate::plan::generational::immix::mutator::create_genimmix_mutator(tls, mmtk) } PlanSelector::MarkSweep => { - crate::plan::marksweep::mutator::create_ms_mutator(tls, &*mmtk.plan) + crate::plan::marksweep::mutator::create_ms_mutator(tls, &*mmtk.get().plan) } - PlanSelector::Immix => crate::plan::immix::mutator::create_immix_mutator(tls, &*mmtk.plan), + PlanSelector::Immix => crate::plan::immix::mutator::create_immix_mutator(tls, &*mmtk.get().plan), PlanSelector::PageProtect => { - crate::plan::pageprotect::mutator::create_pp_mutator(tls, &*mmtk.plan) + crate::plan::pageprotect::mutator::create_pp_mutator(tls, &*mmtk.get().plan) } PlanSelector::MarkCompact => { - crate::plan::markcompact::mutator::create_markcompact_mutator(tls, &*mmtk.plan) + crate::plan::markcompact::mutator::create_markcompact_mutator(tls, &*mmtk.get().plan) } }) } @@ -101,7 +101,7 @@ pub fn create_gc_worker_context( tls: VMWorkerThread, mmtk: &'static MMTK, ) -> GCWorkerCopyContext { - GCWorkerCopyContext::::new(tls, &*mmtk.plan, mmtk.plan.create_copy_config()) + GCWorkerCopyContext::::new(tls, &*mmtk.get().plan, mmtk.get().plan.create_copy_config()) } /// A plan describes the global core functionality for all memory management schemes. diff --git a/src/plan/markcompact/gc_work.rs b/src/plan/markcompact/gc_work.rs index d7f76df34e..729ca9b9cd 100644 --- a/src/plan/markcompact/gc_work.rs +++ b/src/plan/markcompact/gc_work.rs @@ -41,7 +41,7 @@ impl GCWork for UpdateReferences { fn do_work(&mut self, _worker: &mut GCWorker, mmtk: &'static MMTK) { // The following needs to be done right before the second round of root scanning VM::VMScanning::prepare_for_roots_re_scanning(); - mmtk.plan.base().prepare_for_stack_scanning(); + mmtk.get().plan.base().prepare_for_stack_scanning(); #[cfg(feature = "extreme_assertions")] crate::util::edge_logger::reset(); @@ -49,11 +49,11 @@ impl GCWork for UpdateReferences { // scheduler.work_buckets[WorkBucketStage::RefForwarding] // .add(ScanStackRoots::>::new()); for mutator in VM::VMActivePlan::mutators() { - mmtk.scheduler.work_buckets[WorkBucketStage::SecondRoots] + mmtk.get().scheduler.work_buckets[WorkBucketStage::SecondRoots] .add(ScanStackRoot::>(mutator)); } - mmtk.scheduler.work_buckets[WorkBucketStage::SecondRoots] + mmtk.get().scheduler.work_buckets[WorkBucketStage::SecondRoots] .add(ScanVMSpecificRoots::>::new()); } } diff --git a/src/plan/marksweep/gc_work.rs b/src/plan/marksweep/gc_work.rs index 01933edebd..05b92df4b4 100644 --- a/src/plan/marksweep/gc_work.rs +++ b/src/plan/marksweep/gc_work.rs @@ -64,7 +64,7 @@ impl GCWork for MSSweepChunks { ms.work_live_bytes.store(0, Ordering::SeqCst); } - mmtk.scheduler.work_buckets[WorkBucketStage::Release].bulk_add(work_packets); + mmtk.get().scheduler.work_buckets[WorkBucketStage::Release].bulk_add(work_packets); } } diff --git a/src/scheduler/gc_work.rs b/src/scheduler/gc_work.rs index badaa2999b..495783df35 100644 --- a/src/scheduler/gc_work.rs +++ b/src/scheduler/gc_work.rs @@ -15,7 +15,7 @@ pub struct ScheduleCollection; impl GCWork for ScheduleCollection { fn do_work(&mut self, worker: &mut GCWorker, mmtk: &'static MMTK) { - mmtk.plan.schedule_collection(worker.scheduler()); + mmtk.get().plan.schedule_collection(worker.scheduler()); } } @@ -47,10 +47,10 @@ impl GCWork for Prepare { plan_mut.prepare(worker.tls); for mutator in ::VMActivePlan::mutators() { - mmtk.scheduler.work_buckets[WorkBucketStage::Prepare] + mmtk.get().scheduler.work_buckets[WorkBucketStage::Prepare] .add(PrepareMutator::::new(mutator)); } - for w in &mmtk.scheduler.worker_group.workers_shared { + for w in &mmtk.get().scheduler.worker_group.workers_shared { let result = w.designated_work.push(Box::new(PrepareCollector)); debug_assert!(result.is_ok()); } @@ -85,7 +85,7 @@ impl GCWork for PrepareCollector { fn do_work(&mut self, worker: &mut GCWorker, mmtk: &'static MMTK) { trace!("Prepare Collector"); worker.get_copy_context_mut().prepare(); - mmtk.plan.prepare_worker(worker); + mmtk.get().plan.prepare_worker(worker); } } @@ -116,10 +116,10 @@ impl GCWork for Release { plan_mut.release(worker.tls); for mutator in ::VMActivePlan::mutators() { - mmtk.scheduler.work_buckets[WorkBucketStage::Release] + mmtk.get().scheduler.work_buckets[WorkBucketStage::Release] .add(ReleaseMutator::::new(mutator)); } - for w in &mmtk.scheduler.worker_group.workers_shared { + for w in &mmtk.get().scheduler.worker_group.workers_shared { let result = w.designated_work.push(Box::new(ReleaseCollector)); debug_assert!(result.is_ok()); } @@ -176,22 +176,22 @@ impl GCWork for StopMutators { // If the VM requires that only the coordinator thread can stop the world, // we delegate the work to the coordinator. if ::VMCollection::COORDINATOR_ONLY_STW && !worker.is_coordinator() { - mmtk.scheduler + mmtk.get().scheduler .add_coordinator_work(StopMutators::::new(), worker); return; } trace!("stop_all_mutators start"); - mmtk.plan.base().prepare_for_stack_scanning(); + mmtk.get().plan.base().prepare_for_stack_scanning(); ::VMCollection::stop_all_mutators(worker.tls, |mutator| { - mmtk.scheduler.work_buckets[WorkBucketStage::Prepare].add(ScanStackRoot::(mutator)); + mmtk.get().scheduler.work_buckets[WorkBucketStage::Prepare].add(ScanStackRoot::(mutator)); }); trace!("stop_all_mutators end"); - mmtk.scheduler.notify_mutators_paused(mmtk); + mmtk.get().scheduler.notify_mutators_paused(mmtk); if ::VMScanning::SCAN_MUTATORS_IN_SAFEPOINT { // Prepare mutators if necessary // FIXME: This test is probably redundant. JikesRVM requires to call `prepare_mutator` once after mutators are paused - if !mmtk.plan.base().stacks_prepared() { + if !mmtk.get().plan.base().stacks_prepared() { for mutator in ::VMActivePlan::mutators() { ::VMCollection::prepare_mutator( worker.tls, @@ -202,16 +202,16 @@ impl GCWork for StopMutators { } // Scan mutators if ::VMScanning::SINGLE_THREAD_MUTATOR_SCANNING { - mmtk.scheduler.work_buckets[WorkBucketStage::Prepare] + mmtk.get().scheduler.work_buckets[WorkBucketStage::Prepare] .add(ScanStackRoots::::new()); } else { for mutator in ::VMActivePlan::mutators() { - mmtk.scheduler.work_buckets[WorkBucketStage::Prepare] + mmtk.get().scheduler.work_buckets[WorkBucketStage::Prepare] .add(ScanStackRoot::(mutator)); } } } - mmtk.scheduler.work_buckets[WorkBucketStage::Prepare].add(ScanVMSpecificRoots::::new()); + mmtk.get().scheduler.work_buckets[WorkBucketStage::Prepare].add(ScanVMSpecificRoots::::new()); } } @@ -225,7 +225,7 @@ impl GCWork for EndOfGC { info!("End of GC"); #[cfg(feature = "extreme_assertions")] - if crate::util::edge_logger::should_check_duplicate_edges(&*mmtk.plan) { + if crate::util::edge_logger::should_check_duplicate_edges(&*mmtk.get().plan) { // reset the logging info at the end of each GC crate::util::edge_logger::reset(); } @@ -235,10 +235,10 @@ impl GCWork for EndOfGC { "VM only allows coordinator to resume mutators, but the current worker is not the coordinator."); } - mmtk.plan.base().set_gc_status(GcStatus::NotInGC); + mmtk.get().plan.base().set_gc_status(GcStatus::NotInGC); // Reset the triggering information. - mmtk.plan.base().reset_collection_trigger(); + mmtk.get().plan.base().reset_collection_trigger(); ::VMCollection::resume_mutators(worker.tls); } @@ -285,7 +285,7 @@ impl GCWork for ScanStackRoots { for mutator in ::VMActivePlan::mutators() { mutator.flush(); } - mmtk.plan.common().base.set_gc_status(GcStatus::GcProper); + mmtk.get().plan.common().base.set_gc_status(GcStatus::GcProper); } } @@ -294,7 +294,7 @@ pub struct ScanStackRoot(pub &'static mut Mutator GCWork for ScanStackRoot { fn do_work(&mut self, worker: &mut GCWorker, mmtk: &'static MMTK) { trace!("ScanStackRoot for mutator {:?}", self.0.get_tls()); - let base = &mmtk.plan.base(); + let base = &mmtk.get().plan.base(); let mutators = ::VMActivePlan::number_of_mutators(); let factory = ProcessEdgesWorkRootsWorkFactory::::new(mmtk); ::VMScanning::scan_thread_root( @@ -304,7 +304,7 @@ impl GCWork for ScanStackRoot { ); self.0.flush(); - if mmtk.plan.base().inform_stack_scanned(mutators) { + if mmtk.get().plan.base().inform_stack_scanned(mutators) { ::VMScanning::notify_initial_thread_scan_complete( false, worker.tls, ); @@ -347,7 +347,7 @@ impl ProcessEdgesBase { // at creation. This avoids overhead for dynamic dispatch or downcasting plan for each object traced. pub fn new(edges: Vec
, roots: bool, mmtk: &'static MMTK) -> Self { #[cfg(feature = "extreme_assertions")] - if crate::util::edge_logger::should_check_duplicate_edges(&*mmtk.plan) { + if crate::util::edge_logger::should_check_duplicate_edges(&*mmtk.get().plan) { for edge in &edges { // log edge, panic if already logged crate::util::edge_logger::log_edge(*edge); @@ -374,7 +374,7 @@ impl ProcessEdgesBase { } #[inline] pub fn plan(&self) -> &'static dyn Plan { - &*self.mmtk.plan + &*self.mmtk.get().plan } /// Pop all nodes from nodes, and clear nodes to an empty vector. #[inline] @@ -434,7 +434,7 @@ pub trait ProcessEdgesWork: // Executing these work packets now can remarkably reduce the global synchronization time. self.worker().do_boxed_work(work_packet); } else { - self.mmtk.scheduler.work_buckets[WorkBucketStage::Closure].add_boxed(work_packet); + self.mmtk.get().scheduler.work_buckets[WorkBucketStage::Closure].add_boxed(work_packet); } } @@ -633,7 +633,7 @@ impl GCWork for ProcessModBuf { store_metadata::(&self.meta, *obj, 1, None, Some(Ordering::SeqCst)); } } - if mmtk.plan.is_current_gc_nursery() { + if mmtk.get().plan.is_current_gc_nursery() { if !self.modbuf.is_empty() { let mut modbuf = vec![]; ::std::mem::swap(&mut modbuf, &mut self.modbuf); diff --git a/src/scheduler/scheduler.rs b/src/scheduler/scheduler.rs index e37ee213a6..f374825e9d 100644 --- a/src/scheduler/scheduler.rs +++ b/src/scheduler/scheduler.rs @@ -141,7 +141,7 @@ impl GCWorkScheduler { ); let gc_controller = GCController::new( mmtk, - mmtk.plan.base().gc_requester.clone(), + mmtk.get().plan.base().gc_requester.clone(), self.clone(), receiver, coordinator_worker, @@ -449,7 +449,7 @@ impl GCWorkScheduler { } pub fn notify_mutators_paused(&self, mmtk: &'static MMTK) { - mmtk.plan.base().gc_requester.clear_request(); + mmtk.get().plan.base().gc_requester.clear_request(); let first_stw_bucket = &self.work_buckets[WorkBucketStage::first_stw_stage()]; debug_assert!(!first_stw_bucket.is_activated()); first_stw_bucket.activate(); diff --git a/src/scheduler/worker.rs b/src/scheduler/worker.rs index 55cccc7dfa..edc74b619e 100644 --- a/src/scheduler/worker.rs +++ b/src/scheduler/worker.rs @@ -222,7 +222,7 @@ impl WorkerGroup { let worker = Box::new(GCWorker::new( mmtk, ordinal, - mmtk.scheduler.clone(), + mmtk.get().scheduler.clone(), false, sender.clone(), shared.clone(), diff --git a/src/util/analysis/mod.rs b/src/util/analysis/mod.rs index 4814afe880..a2fe1c6471 100644 --- a/src/util/analysis/mod.rs +++ b/src/util/analysis/mod.rs @@ -33,7 +33,7 @@ pub struct GcHookWork; impl GCWork for GcHookWork { fn do_work(&mut self, _worker: &mut GCWorker, mmtk: &'static MMTK) { - let base = &mmtk.plan.base(); + let base = &mmtk.get().plan.base(); base.analysis_manager.gc_hook(mmtk); } } diff --git a/src/util/finalizable_processor.rs b/src/util/finalizable_processor.rs index a409d86231..086f98b418 100644 --- a/src/util/finalizable_processor.rs +++ b/src/util/finalizable_processor.rs @@ -131,7 +131,7 @@ pub struct Finalization(PhantomData); impl GCWork for Finalization { fn do_work(&mut self, worker: &mut GCWorker, mmtk: &'static MMTK) { - let mut finalizable_processor = mmtk.finalizable_processor.lock().unwrap(); + let mut finalizable_processor = mmtk.get().finalizable_processor.lock().unwrap(); debug!( "Finalization, {} objects in candidates, {} objects ready to finalize", finalizable_processor.candidates.len(), @@ -140,7 +140,7 @@ impl GCWork for Finalization { let mut w = E::new(vec![], false, mmtk); w.set_worker(worker); - finalizable_processor.scan(worker.tls, &mut w, mmtk.plan.is_current_gc_nursery()); + finalizable_processor.scan(worker.tls, &mut w, mmtk.get().plan.is_current_gc_nursery()); debug!( "Finished finalization, {} objects in candidates, {} objects ready to finalize", finalizable_processor.candidates.len(), @@ -160,12 +160,12 @@ pub struct ForwardFinalization(PhantomData); impl GCWork for ForwardFinalization { fn do_work(&mut self, worker: &mut GCWorker, mmtk: &'static MMTK) { trace!("Forward finalization"); - let mut finalizable_processor = mmtk.finalizable_processor.lock().unwrap(); + let mut finalizable_processor = mmtk.get().finalizable_processor.lock().unwrap(); let mut w = E::new(vec![], false, mmtk); w.set_worker(worker); - finalizable_processor.forward_candidate(&mut w, mmtk.plan.is_current_gc_nursery()); + finalizable_processor.forward_candidate(&mut w, mmtk.get().plan.is_current_gc_nursery()); - finalizable_processor.forward_finalizable(&mut w, mmtk.plan.is_current_gc_nursery()); + finalizable_processor.forward_finalizable(&mut w, mmtk.get().plan.is_current_gc_nursery()); trace!("Finished forwarding finlizable"); } } diff --git a/src/util/malloc/mod.rs b/src/util/malloc/mod.rs index 551b014026..26214ccc97 100644 --- a/src/util/malloc/mod.rs +++ b/src/util/malloc/mod.rs @@ -29,7 +29,7 @@ pub fn malloc(size: usize) -> Address { pub fn counted_malloc(mmtk: &MMTK, size: usize) -> Address { let res = malloc(size); if !res.is_zero() { - mmtk.plan.base().increase_malloc_bytes_by(size); + mmtk.get().plan.base().increase_malloc_bytes_by(size); } res } @@ -44,7 +44,7 @@ pub fn calloc(num: usize, size: usize) -> Address { pub fn counted_calloc(mmtk: &MMTK, num: usize, size: usize) -> Address { let res = calloc(num, size); if !res.is_zero() { - mmtk.plan.base().increase_malloc_bytes_by(num * size); + mmtk.get().plan.base().increase_malloc_bytes_by(num * size); } res } @@ -65,10 +65,10 @@ pub fn realloc_with_old_size( let res = realloc(addr, size); if !addr.is_zero() { - mmtk.plan.base().decrease_malloc_bytes_by(old_size); + mmtk.get().plan.base().decrease_malloc_bytes_by(old_size); } if size != 0 && !res.is_zero() { - mmtk.plan.base().increase_malloc_bytes_by(size); + mmtk.get().plan.base().increase_malloc_bytes_by(size); } res @@ -84,6 +84,6 @@ pub fn free(addr: Address) { pub fn free_with_size(mmtk: &MMTK, addr: Address, old_size: usize) { free(addr); if !addr.is_zero() { - mmtk.plan.base().decrease_malloc_bytes_by(old_size); + mmtk.get().plan.base().decrease_malloc_bytes_by(old_size); } } diff --git a/src/util/reference_processor.rs b/src/util/reference_processor.rs index 7998a7bba9..cd47e482ce 100644 --- a/src/util/reference_processor.rs +++ b/src/util/reference_processor.rs @@ -66,15 +66,15 @@ impl ReferenceProcessors { /// plans, this separate step is required. pub fn forward_refs(&self, trace: &mut E, mmtk: &'static MMTK) { debug_assert!( - mmtk.plan.constraints().needs_forward_after_liveness, + mmtk.get().plan.constraints().needs_forward_after_liveness, "A plan with needs_forward_after_liveness=false does not need a separate forward step" ); self.soft - .forward::(trace, mmtk.plan.is_current_gc_nursery()); + .forward::(trace, mmtk.get().plan.is_current_gc_nursery()); self.weak - .forward::(trace, mmtk.plan.is_current_gc_nursery()); + .forward::(trace, mmtk.get().plan.is_current_gc_nursery()); self.phantom - .forward::(trace, mmtk.plan.is_current_gc_nursery()); + .forward::(trace, mmtk.get().plan.is_current_gc_nursery()); } // Methods for scanning weak references. It needs to be called in a decreasing order of reference strengths, i.e. soft > weak > phantom @@ -83,21 +83,21 @@ impl ReferenceProcessors { pub fn scan_soft_refs(&self, trace: &mut E, mmtk: &'static MMTK) { // For soft refs, it is up to the VM to decide when to reclaim this. // If this is not an emergency collection, we have no heap stress. We simply retain soft refs. - if !mmtk.plan.is_emergency_collection() { + if !mmtk.get().plan.is_emergency_collection() { // This step only retains the referents (keep the referents alive), it does not update its addresses. // We will call soft.scan() again with retain=false to update its addresses based on liveness. self.soft - .retain::(trace, mmtk.plan.is_current_gc_nursery()); + .retain::(trace, mmtk.get().plan.is_current_gc_nursery()); } // This will update the references (and the referents). self.soft - .scan::(trace, mmtk.plan.is_current_gc_nursery()); + .scan::(trace, mmtk.get().plan.is_current_gc_nursery()); } /// Scan weak references. pub fn scan_weak_refs(&self, trace: &mut E, mmtk: &'static MMTK) { self.weak - .scan::(trace, mmtk.plan.is_current_gc_nursery()); + .scan::(trace, mmtk.get().plan.is_current_gc_nursery()); } /// Scan phantom references. @@ -107,7 +107,7 @@ impl ReferenceProcessors { mmtk: &'static MMTK, ) { self.phantom - .scan::(trace, mmtk.plan.is_current_gc_nursery()); + .scan::(trace, mmtk.get().plan.is_current_gc_nursery()); } } @@ -491,7 +491,7 @@ impl GCWork for SoftRefProcessing { fn do_work(&mut self, worker: &mut GCWorker, mmtk: &'static MMTK) { let mut w = E::new(vec![], false, mmtk); w.set_worker(worker); - mmtk.reference_processors.scan_soft_refs(&mut w, mmtk); + mmtk.get().reference_processors.scan_soft_refs(&mut w, mmtk); w.flush(); } } @@ -507,7 +507,7 @@ impl GCWork for WeakRefProcessing { fn do_work(&mut self, worker: &mut GCWorker, mmtk: &'static MMTK) { let mut w = E::new(vec![], false, mmtk); w.set_worker(worker); - mmtk.reference_processors.scan_weak_refs(&mut w, mmtk); + mmtk.get().reference_processors.scan_weak_refs(&mut w, mmtk); w.flush(); } } @@ -523,7 +523,7 @@ impl GCWork for PhantomRefProcessing { fn do_work(&mut self, worker: &mut GCWorker, mmtk: &'static MMTK) { let mut w = E::new(vec![], false, mmtk); w.set_worker(worker); - mmtk.reference_processors.scan_phantom_refs(&mut w, mmtk); + mmtk.get().reference_processors.scan_phantom_refs(&mut w, mmtk); w.flush(); } } @@ -539,7 +539,7 @@ impl GCWork for RefForwarding { fn do_work(&mut self, worker: &mut GCWorker, mmtk: &'static MMTK) { let mut w = E::new(vec![], false, mmtk); w.set_worker(worker); - mmtk.reference_processors.forward_refs(&mut w, mmtk); + mmtk.get().reference_processors.forward_refs(&mut w, mmtk); w.flush(); } } @@ -553,7 +553,7 @@ impl RefForwarding { pub struct RefEnqueue(PhantomData); impl GCWork for RefEnqueue { fn do_work(&mut self, worker: &mut GCWorker, mmtk: &'static MMTK) { - mmtk.reference_processors.enqueue_refs::(worker.tls); + mmtk.get().reference_processors.enqueue_refs::(worker.tls); } } impl RefEnqueue { diff --git a/src/util/sanity/sanity_checker.rs b/src/util/sanity/sanity_checker.rs index 78fb3130f1..47383a5fc1 100644 --- a/src/util/sanity/sanity_checker.rs +++ b/src/util/sanity/sanity_checker.rs @@ -54,7 +54,7 @@ impl ScheduleSanityGC

{ impl GCWork for ScheduleSanityGC

{ fn do_work(&mut self, worker: &mut GCWorker, mmtk: &'static MMTK) { let scheduler = worker.scheduler(); - let plan = &mmtk.plan; + let plan = &mmtk.get().plan; scheduler.reset_state(); @@ -100,16 +100,16 @@ impl SanityPrepare

{ impl GCWork for SanityPrepare

{ fn do_work(&mut self, _worker: &mut GCWorker, mmtk: &'static MMTK) { - mmtk.plan.enter_sanity(); + mmtk.get().plan.enter_sanity(); { let mut sanity_checker = mmtk.sanity_checker.lock().unwrap(); sanity_checker.refs.clear(); } for mutator in ::VMActivePlan::mutators() { - mmtk.scheduler.work_buckets[WorkBucketStage::Prepare] + mmtk.get().scheduler.work_buckets[WorkBucketStage::Prepare] .add(PrepareMutator::::new(mutator)); } - for w in &mmtk.scheduler.worker_group.workers_shared { + for w in &mmtk.get().scheduler.worker_group.workers_shared { let result = w.designated_work.push(Box::new(PrepareCollector)); debug_assert!(result.is_ok()); } @@ -128,13 +128,13 @@ impl SanityRelease

{ impl GCWork for SanityRelease

{ fn do_work(&mut self, _worker: &mut GCWorker, mmtk: &'static MMTK) { - mmtk.plan.leave_sanity(); + mmtk.get().plan.leave_sanity(); mmtk.sanity_checker.lock().unwrap().clear_roots_cache(); for mutator in ::VMActivePlan::mutators() { - mmtk.scheduler.work_buckets[WorkBucketStage::Release] + mmtk.get().scheduler.work_buckets[WorkBucketStage::Release] .add(ReleaseMutator::::new(mutator)); } - for w in &mmtk.scheduler.worker_group.workers_shared { + for w in &mmtk.get().scheduler.worker_group.workers_shared { let result = w.designated_work.push(Box::new(ReleaseCollector)); debug_assert!(result.is_ok()); } diff --git a/src/util/statistics/stats.rs b/src/util/statistics/stats.rs index f89ec538e4..44ec532e8f 100644 --- a/src/util/statistics/stats.rs +++ b/src/util/statistics/stats.rs @@ -187,7 +187,7 @@ impl Stats { println!( "============================ MMTk Statistics Totals ============================" ); - let scheduler_stat = mmtk.scheduler.statistics(); + let scheduler_stat = mmtk.get().scheduler.statistics(); self.print_column_names(&scheduler_stat); print!("{}\t", self.get_phase() / 2); let counter = self.counters.lock().unwrap(); From d29e1f3301131375de7ac979f07d346810eb6857 Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Fri, 15 Jul 2022 11:53:18 +1000 Subject: [PATCH 02/14] Update documentation --- src/memory_manager.rs | 22 +++++++++++++++++++--- src/mmtk.rs | 12 ++++++------ src/util/options.rs | 6 +++--- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/src/memory_manager.rs b/src/memory_manager.rs index 86d2323c16..8f82e9ec2f 100644 --- a/src/memory_manager.rs +++ b/src/memory_manager.rs @@ -27,8 +27,19 @@ use crate::vm::VMBinding; use std::sync::atomic::Ordering; /// Initialize an MMTk instance. A VM should call this method after creating an [MMTK](../mmtk/struct.MMTK.html) -/// instance but before using any of the methods provided in MMTk. This method will attempt to initialize a -/// logger. If the VM would like to use its own logger, it should initialize the logger before calling this method. +/// instance but before using any of the methods provided in MMTk (except `process()` and `process_bulk()`). +/// +/// We expect a binding to ininitialize MMTk in the following steps: +/// +/// 1. Create an [MMTK](../mmtk/struct.MMTK.html) instance. Usually this is done statically, i.e. a binding holds one singleton as a static variable for MMTk. +/// 2. Set command line options for MMTk by [process()](./fn.process.html) or [process_bulk()](./fn.process_bulk.html). At this point, +/// other APIs should not be used, as MMTk is not yet initialized. +/// 3. Initialize MMTk by calling this function, `gc_init()`. After this call, most MMTk APIs are avialable for use. But MMTk no longer expects +/// new command line options. If a binding calls `process()` after this point, it may have no effect. +/// 4. Enable garbage collection in MMTk by [enable_collection()](./fn.enable_collection.html). A binding should only call this once its +/// thread system is ready. MMTk will not trigger garbage collection before this call. +/// +/// Not that This method will attempt to initialize a logger. If the VM would like to use its own logger, it should initialize the logger before calling this method. /// Note that, to allow MMTk to do GC properly, `initialize_collection()` needs to be called after this call when /// the VM's thread system is ready to spawn GC workers. /// @@ -60,13 +71,18 @@ pub fn gc_init(mmtk: &'static mut MMTK, heap_size: usize) { } } assert!(heap_size > 0, "Invalid heap size"); - mmtk.instantiate(); + mmtk.initialize(); mmtk.get_mut().plan.gc_init(heap_size, &crate::VM_MAP); info!("Initialized MMTk with {:?}", *mmtk.options.plan); #[cfg(feature = "extreme_assertions")] warn!("The feature 'extreme_assertions' is enabled. MMTk will run expensive run-time checks. Slow performance should be expected."); } +/// Is MMTk initialized? MMTk is initialized once `gc_init()` is called. +pub fn is_gc_initialized(mmtk: &'static MMTK) -> bool { + mmtk.is_initialized.load(Ordering::SeqCst) +} + /// Request MMTk to create a mutator for the given thread. For performance reasons, A VM should /// store the returned mutator in a thread local storage that can be accessed efficiently. /// diff --git a/src/mmtk.rs b/src/mmtk.rs index 3f6716c688..22585b7761 100644 --- a/src/mmtk.rs +++ b/src/mmtk.rs @@ -46,7 +46,7 @@ pub static SFT_MAP: InitializeOnce> = InitializeOnce::new(); pub struct MMTK { pub(crate) options: Arc, pub(crate) instance: MaybeUninit>, - pub(crate) instantiated: AtomicBool, + pub(crate) is_initialized: AtomicBool, } pub struct MMTKInner { @@ -101,24 +101,24 @@ impl MMTK { MMTK { options, instance: MaybeUninit::>::uninit(), - instantiated: AtomicBool::new(false), + is_initialized: AtomicBool::new(false), } } - pub(crate) fn instantiate(&mut self) { + pub(crate) fn initialize(&mut self) { self.instance.write(MMTKInner::new(self.options.clone())); - self.instantiated.store(true, Ordering::SeqCst); + self.is_initialized.store(true, Ordering::SeqCst); } #[inline(always)] pub fn get(&self) -> &MMTKInner { - debug_assert!(self.instantiated.load(Ordering::SeqCst), "MMTK is not instantiated (is gc_init() called?)"); + debug_assert!(self.is_initialized.load(Ordering::SeqCst), "MMTK is not initialized (is gc_init() called?)"); unsafe { self.instance.assume_init_ref() } } #[inline(always)] pub fn get_mut(&mut self) -> &mut MMTKInner { - debug_assert!(self.instantiated.load(Ordering::SeqCst), "MMTK is not instantiated (is gc_init() called?)"); + debug_assert!(self.is_initialized.load(Ordering::SeqCst), "MMTK is not initialized (is gc_init() called?)"); unsafe { self.instance.assume_init_mut() } } diff --git a/src/util/options.rs b/src/util/options.rs index 3105b21a00..629a146960 100644 --- a/src/util/options.rs +++ b/src/util/options.rs @@ -303,12 +303,12 @@ macro_rules! options { // At some point, we may disallow this and most options can only be set by command line. options! { // The plan to use. This needs to be initialized before creating an MMTk instance (currently by setting env vars) - plan: PlanSelector [env_var: true, command_line: false] [always_valid] = PlanSelector::NoGC, + plan: PlanSelector [env_var: true, command_line: true] [always_valid] = PlanSelector::NoGC, // Number of GC worker threads. (There is always one GC controller thread.) // FIXME: Currently we create GCWorkScheduler when MMTK is created, which is usually static. // To allow this as a command-line option, we need to refactor the creation fo the `MMTK` instance. // See: https://github.com/mmtk/mmtk-core/issues/532 - threads: usize [env_var: true, command_line: false] [|v: &usize| *v > 0] = num_cpus::get(), + threads: usize [env_var: true, command_line: true] [|v: &usize| *v > 0] = num_cpus::get(), // Enable an optimization that only scans the part of the stack that has changed since the last GC (not supported) use_short_stack_scans: bool [env_var: true, command_line: true] [always_valid] = false, // Enable a return barrier (not supported) @@ -348,7 +348,7 @@ options! { // The size of vmspace. This needs to be initialized before creating an MMTk instance (currently by setting env vars) // FIXME: This value is set for JikesRVM. We need a proper way to set options. // We need to set these values programmatically in VM specific code. - vm_space_size: usize [env_var: true, command_line: false] [|v: &usize| *v > 0] = 0x7cc_cccc, + vm_space_size: usize [env_var: true, command_line: true] [|v: &usize| *v > 0] = 0x7cc_cccc, // Perf events to measure // Semicolons are used to separate events // Each event is in the format of event_name,pid,cpu (see man perf_event_open for what pid and cpu mean). From 5c2df2b587ee34bd339eda66719ad2cba9104db6 Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Fri, 15 Jul 2022 14:48:52 +1000 Subject: [PATCH 03/14] Make heap size as an option --- src/memory_manager.rs | 4 +--- src/mmtk.rs | 3 +++ src/util/options.rs | 11 +++++++---- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/memory_manager.rs b/src/memory_manager.rs index 8f82e9ec2f..9a6512e0a6 100644 --- a/src/memory_manager.rs +++ b/src/memory_manager.rs @@ -46,7 +46,7 @@ use std::sync::atomic::Ordering; /// Arguments: /// * `mmtk`: A reference to an MMTk instance to initialize. /// * `heap_size`: The heap size for the MMTk instance in bytes. -pub fn gc_init(mmtk: &'static mut MMTK, heap_size: usize) { +pub fn gc_init(mmtk: &'static mut MMTK) { match crate::util::logger::try_init() { Ok(_) => debug!("MMTk initialized the logger."), Err(_) => debug!( @@ -70,9 +70,7 @@ pub fn gc_init(mmtk: &'static mut MMTK, heap_size: usize) { } } } - assert!(heap_size > 0, "Invalid heap size"); mmtk.initialize(); - mmtk.get_mut().plan.gc_init(heap_size, &crate::VM_MAP); info!("Initialized MMTk with {:?}", *mmtk.options.plan); #[cfg(feature = "extreme_assertions")] warn!("The feature 'extreme_assertions' is enabled. MMTk will run expensive run-time checks. Slow performance should be expected."); diff --git a/src/mmtk.rs b/src/mmtk.rs index 22585b7761..c7665a6654 100644 --- a/src/mmtk.rs +++ b/src/mmtk.rs @@ -108,6 +108,9 @@ impl MMTK { pub(crate) fn initialize(&mut self) { self.instance.write(MMTKInner::new(self.options.clone())); self.is_initialized.store(true, Ordering::SeqCst); + + let heap_size = *self.options.heap_size; + self.get_mut().plan.gc_init(heap_size, &crate::VM_MAP); } #[inline(always)] diff --git a/src/util/options.rs b/src/util/options.rs index 629a146960..019e8c7774 100644 --- a/src/util/options.rs +++ b/src/util/options.rs @@ -302,13 +302,16 @@ macro_rules! options { // Currently we allow all the options to be set by env var for the sake of convenience. // At some point, we may disallow this and most options can only be set by command line. options! { - // The plan to use. This needs to be initialized before creating an MMTk instance (currently by setting env vars) + // The plan to use. plan: PlanSelector [env_var: true, command_line: true] [always_valid] = PlanSelector::NoGC, // Number of GC worker threads. (There is always one GC controller thread.) // FIXME: Currently we create GCWorkScheduler when MMTK is created, which is usually static. // To allow this as a command-line option, we need to refactor the creation fo the `MMTK` instance. // See: https://github.com/mmtk/mmtk-core/issues/532 threads: usize [env_var: true, command_line: true] [|v: &usize| *v > 0] = num_cpus::get(), + // Heap size. Default to 512MB. + // TODO: We should have a default heap size related to the max physical memory. + heap_size: usize [env_var: true, command_line: true] [|v: &usize| *v > 0] = 512 << 20, // Enable an optimization that only scans the part of the stack that has changed since the last GC (not supported) use_short_stack_scans: bool [env_var: true, command_line: true] [always_valid] = false, // Enable a return barrier (not supported) @@ -317,9 +320,9 @@ options! { eager_complete_sweep: bool [env_var: true, command_line: true] [always_valid] = false, // Should we ignore GCs requested by the user (e.g. java.lang.System.gc)? ignore_system_g_c: bool [env_var: true, command_line: true] [always_valid] = false, - // The upper bound of nursery size. This needs to be initialized before creating an MMTk instance (currently by setting env vars) + // The upper bound of nursery size. max_nursery: usize [env_var: true, command_line: true] [|v: &usize| *v > 0 ] = DEFAULT_MAX_NURSERY, - // The lower bound of nusery size. This needs to be initialized before creating an MMTk instance (currently by setting env vars) + // The lower bound of nusery size. min_nursery: usize [env_var: true, command_line: true] [|v: &usize| *v > 0 ] = DEFAULT_MIN_NURSERY, // Should a major GC be performed when a system GC is required? full_heap_system_gc: bool [env_var: true, command_line: true] [always_valid] = false, @@ -345,7 +348,7 @@ options! { // But this should have no obvious mutator overhead, and can be used to test GC performance along with a larger stress // factor (e.g. tens of metabytes). precise_stress: bool [env_var: true, command_line: true] [always_valid] = true, - // The size of vmspace. This needs to be initialized before creating an MMTk instance (currently by setting env vars) + // The size of vmspace. // FIXME: This value is set for JikesRVM. We need a proper way to set options. // We need to set these values programmatically in VM specific code. vm_space_size: usize [env_var: true, command_line: true] [|v: &usize| *v > 0] = 0x7cc_cccc, From 21e2e66c9a5d495e8ddb04a584690c8210636b05 Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Fri, 15 Jul 2022 16:29:46 +1000 Subject: [PATCH 04/14] Fix test/style --- examples/allocation_benchmark.c | 3 +- examples/main.c | 3 +- examples/mmtk.h | 3 +- src/memory_manager.rs | 33 ++++++++++++++----- src/mmtk.rs | 12 +++++-- src/plan/generational/copying/mutator.rs | 5 ++- src/plan/generational/immix/mutator.rs | 5 ++- src/plan/global.rs | 8 +++-- src/plan/markcompact/gc_work.rs | 7 ++-- src/scheduler/gc_work.rs | 16 ++++++--- src/util/options.rs | 4 +-- src/util/reference_processor.rs | 8 +++-- src/util/sanity/sanity_checker.rs | 12 ++++--- vmbindings/dummyvm/api/mmtk.h | 3 +- vmbindings/dummyvm/src/api.rs | 9 +++-- .../tests/allocate_with_disable_collection.rs | 3 +- .../allocate_with_initialize_collection.rs | 3 +- .../allocate_with_re_enable_collection.rs | 3 +- .../allocate_without_initialize_collection.rs | 3 +- vmbindings/dummyvm/src/tests/fixtures/mod.rs | 6 ++-- vmbindings/dummyvm/src/tests/issue139.rs | 3 +- 21 files changed, 109 insertions(+), 43 deletions(-) diff --git a/examples/allocation_benchmark.c b/examples/allocation_benchmark.c index b125954786..693ac11457 100644 --- a/examples/allocation_benchmark.c +++ b/examples/allocation_benchmark.c @@ -5,7 +5,8 @@ int main() { volatile uint64_t * tmp; - mmtk_gc_init(1024*1024*1024); + mmtk_set_heap_size(1024*1024*1024); + mmtk_gc_init(); MMTk_Mutator handle = mmtk_bind_mutator(0); for (int i=0; i<1024*1024*100; i++) { diff --git a/examples/main.c b/examples/main.c index 2051d4f925..2366e5edaf 100644 --- a/examples/main.c +++ b/examples/main.c @@ -2,7 +2,8 @@ #include "mmtk.h" int main(int argc, char* argv[]){ - mmtk_gc_init(1024*1024); + mmtk_set_heap_size(1024*1024); + mmtk_gc_init(); MMTk_Mutator handle = mmtk_bind_mutator(0); diff --git a/examples/mmtk.h b/examples/mmtk.h index 6f0843d729..a7ddc02253 100644 --- a/examples/mmtk.h +++ b/examples/mmtk.h @@ -18,7 +18,8 @@ extern "C" { typedef void* MMTk_Mutator; // Initialize an MMTk instance -extern void mmtk_gc_init(size_t heap_size); +extern void mmtk_gc_init(); +extern void mmtk_set_heap_size(size_t size); // Request MMTk to create a new mutator for the given `tls` thread extern MMTk_Mutator mmtk_bind_mutator(void* tls); diff --git a/src/memory_manager.rs b/src/memory_manager.rs index 9a6512e0a6..569465e6df 100644 --- a/src/memory_manager.rs +++ b/src/memory_manager.rs @@ -291,7 +291,11 @@ pub fn initialize_collection(mmtk: &'static MMTK, tls: VMThre "MMTk collection has been initialized (was initialize_collection() already called before?)" ); mmtk.get().scheduler.spawn_gc_threads(mmtk, tls); - mmtk.get().plan.base().initialized.store(true, Ordering::SeqCst); + mmtk.get() + .plan + .base() + .initialized + .store(true, Ordering::SeqCst); } /// Allow MMTk to trigger garbage collection when heap is full. This should only be used in pair with disable_collection(). @@ -305,7 +309,8 @@ pub fn enable_collection(mmtk: &'static MMTK) { !mmtk.get().plan.should_trigger_gc_when_heap_is_full(), "enable_collection() is called when GC is already enabled." ); - mmtk.get().plan + mmtk.get() + .plan .base() .trigger_gc_when_heap_is_full .store(true, Ordering::SeqCst); @@ -326,7 +331,8 @@ pub fn disable_collection(mmtk: &'static MMTK) { mmtk.get().plan.should_trigger_gc_when_heap_is_full(), "disable_collection() is called when GC is not enabled." ); - mmtk.get().plan + mmtk.get() + .plan .base() .trigger_gc_when_heap_is_full .store(false, Ordering::SeqCst); @@ -517,7 +523,9 @@ pub fn modify_check(mmtk: &MMTK, object: ObjectReference) { /// * `mmtk`: A reference to an MMTk instance. /// * `reff`: The weak reference to add. pub fn add_weak_candidate(mmtk: &MMTK, reff: ObjectReference) { - mmtk.get().reference_processors.add_weak_candidate::(reff); + mmtk.get() + .reference_processors + .add_weak_candidate::(reff); } /// Add a reference to the list of soft references. A binding may @@ -527,7 +535,9 @@ pub fn add_weak_candidate(mmtk: &MMTK, reff: ObjectReference) /// * `mmtk`: A reference to an MMTk instance. /// * `reff`: The soft reference to add. pub fn add_soft_candidate(mmtk: &MMTK, reff: ObjectReference) { - mmtk.get().reference_processors.add_soft_candidate::(reff); + mmtk.get() + .reference_processors + .add_soft_candidate::(reff); } /// Add a reference to the list of phantom references. A binding may @@ -537,7 +547,9 @@ pub fn add_soft_candidate(mmtk: &MMTK, reff: ObjectReference) /// * `mmtk`: A reference to an MMTk instance. /// * `reff`: The phantom reference to add. pub fn add_phantom_candidate(mmtk: &MMTK, reff: ObjectReference) { - mmtk.get().reference_processors.add_phantom_candidate::(reff); + mmtk.get() + .reference_processors + .add_phantom_candidate::(reff); } /// Generic hook to allow benchmarks to be harnessed. We do a full heap @@ -592,7 +604,8 @@ pub fn get_finalized_object( warn!("get_finalized_object() is called when no_finalizer = true"); } - mmtk.get().finalizable_processor + mmtk.get() + .finalizable_processor .lock() .unwrap() .get_ready_object() @@ -612,7 +625,8 @@ pub fn get_all_finalizers( warn!("get_all_finalizers() is called when no_finalizer = true"); } - mmtk.get().finalizable_processor + mmtk.get() + .finalizable_processor .lock() .unwrap() .get_all_finalizers() @@ -632,7 +646,8 @@ pub fn get_finalizers_for( warn!("get_finalizers() is called when no_finalizer = true"); } - mmtk.get().finalizable_processor + mmtk.get() + .finalizable_processor .lock() .unwrap() .get_finalizers_for(object) diff --git a/src/mmtk.rs b/src/mmtk.rs index c7665a6654..a66d91f9c8 100644 --- a/src/mmtk.rs +++ b/src/mmtk.rs @@ -45,7 +45,7 @@ pub static SFT_MAP: InitializeOnce> = InitializeOnce::new(); /// *Note that multi-instances is not fully supported yet* pub struct MMTK { pub(crate) options: Arc, - pub(crate) instance: MaybeUninit>, + instance: MaybeUninit>, pub(crate) is_initialized: AtomicBool, } @@ -115,13 +115,19 @@ impl MMTK { #[inline(always)] pub fn get(&self) -> &MMTKInner { - debug_assert!(self.is_initialized.load(Ordering::SeqCst), "MMTK is not initialized (is gc_init() called?)"); + debug_assert!( + self.is_initialized.load(Ordering::SeqCst), + "MMTK is not initialized (is gc_init() called?)" + ); unsafe { self.instance.assume_init_ref() } } #[inline(always)] pub fn get_mut(&mut self) -> &mut MMTKInner { - debug_assert!(self.is_initialized.load(Ordering::SeqCst), "MMTK is not initialized (is gc_init() called?)"); + debug_assert!( + self.is_initialized.load(Ordering::SeqCst), + "MMTK is not initialized (is gc_init() called?)" + ); unsafe { self.instance.assume_init_mut() } } diff --git a/src/plan/generational/copying/mutator.rs b/src/plan/generational/copying/mutator.rs index 4b3fcbce70..950861ab8b 100644 --- a/src/plan/generational/copying/mutator.rs +++ b/src/plan/generational/copying/mutator.rs @@ -35,7 +35,10 @@ pub fn create_gencopy_mutator( let gencopy = mmtk.get().plan.downcast_ref::>().unwrap(); let config = MutatorConfig { allocator_mapping: &*ALLOCATOR_MAPPING, - space_mapping: Box::new(create_gen_space_mapping(&*mmtk.get().plan, &gencopy.gen.nursery)), + space_mapping: Box::new(create_gen_space_mapping( + &*mmtk.get().plan, + &gencopy.gen.nursery, + )), prepare_func: &gencopy_mutator_prepare, release_func: &gencopy_mutator_release, }; diff --git a/src/plan/generational/immix/mutator.rs b/src/plan/generational/immix/mutator.rs index 13dd6527a9..1779e4a66c 100644 --- a/src/plan/generational/immix/mutator.rs +++ b/src/plan/generational/immix/mutator.rs @@ -33,7 +33,10 @@ pub fn create_genimmix_mutator( let genimmix = mmtk.get().plan.downcast_ref::>().unwrap(); let config = MutatorConfig { allocator_mapping: &*ALLOCATOR_MAPPING, - space_mapping: Box::new(create_gen_space_mapping(&*mmtk.get().plan, &genimmix.gen.nursery)), + space_mapping: Box::new(create_gen_space_mapping( + &*mmtk.get().plan, + &genimmix.gen.nursery, + )), prepare_func: &genimmix_mutator_prepare, release_func: &genimmix_mutator_release, }; diff --git a/src/plan/global.rs b/src/plan/global.rs index 501ffdb191..fc7773caf6 100644 --- a/src/plan/global.rs +++ b/src/plan/global.rs @@ -40,7 +40,9 @@ pub fn create_mutator( mmtk: &'static MMTK, ) -> Box> { Box::new(match *mmtk.options.plan { - PlanSelector::NoGC => crate::plan::nogc::mutator::create_nogc_mutator(tls, &*mmtk.get().plan), + PlanSelector::NoGC => { + crate::plan::nogc::mutator::create_nogc_mutator(tls, &*mmtk.get().plan) + } PlanSelector::SemiSpace => { crate::plan::semispace::mutator::create_ss_mutator(tls, &*mmtk.get().plan) } @@ -53,7 +55,9 @@ pub fn create_mutator( PlanSelector::MarkSweep => { crate::plan::marksweep::mutator::create_ms_mutator(tls, &*mmtk.get().plan) } - PlanSelector::Immix => crate::plan::immix::mutator::create_immix_mutator(tls, &*mmtk.get().plan), + PlanSelector::Immix => { + crate::plan::immix::mutator::create_immix_mutator(tls, &*mmtk.get().plan) + } PlanSelector::PageProtect => { crate::plan::pageprotect::mutator::create_pp_mutator(tls, &*mmtk.get().plan) } diff --git a/src/plan/markcompact/gc_work.rs b/src/plan/markcompact/gc_work.rs index 729ca9b9cd..38312630df 100644 --- a/src/plan/markcompact/gc_work.rs +++ b/src/plan/markcompact/gc_work.rs @@ -49,8 +49,11 @@ impl GCWork for UpdateReferences { // scheduler.work_buckets[WorkBucketStage::RefForwarding] // .add(ScanStackRoots::>::new()); for mutator in VM::VMActivePlan::mutators() { - mmtk.get().scheduler.work_buckets[WorkBucketStage::SecondRoots] - .add(ScanStackRoot::>(mutator)); + mmtk.get().scheduler.work_buckets[WorkBucketStage::SecondRoots].add(ScanStackRoot::< + ForwardingProcessEdges, + >( + mutator + )); } mmtk.get().scheduler.work_buckets[WorkBucketStage::SecondRoots] diff --git a/src/scheduler/gc_work.rs b/src/scheduler/gc_work.rs index 495783df35..75cd3c7e65 100644 --- a/src/scheduler/gc_work.rs +++ b/src/scheduler/gc_work.rs @@ -176,7 +176,8 @@ impl GCWork for StopMutators { // If the VM requires that only the coordinator thread can stop the world, // we delegate the work to the coordinator. if ::VMCollection::COORDINATOR_ONLY_STW && !worker.is_coordinator() { - mmtk.get().scheduler + mmtk.get() + .scheduler .add_coordinator_work(StopMutators::::new(), worker); return; } @@ -184,7 +185,8 @@ impl GCWork for StopMutators { trace!("stop_all_mutators start"); mmtk.get().plan.base().prepare_for_stack_scanning(); ::VMCollection::stop_all_mutators(worker.tls, |mutator| { - mmtk.get().scheduler.work_buckets[WorkBucketStage::Prepare].add(ScanStackRoot::(mutator)); + mmtk.get().scheduler.work_buckets[WorkBucketStage::Prepare] + .add(ScanStackRoot::(mutator)); }); trace!("stop_all_mutators end"); mmtk.get().scheduler.notify_mutators_paused(mmtk); @@ -211,7 +213,8 @@ impl GCWork for StopMutators { } } } - mmtk.get().scheduler.work_buckets[WorkBucketStage::Prepare].add(ScanVMSpecificRoots::::new()); + mmtk.get().scheduler.work_buckets[WorkBucketStage::Prepare] + .add(ScanVMSpecificRoots::::new()); } } @@ -285,7 +288,11 @@ impl GCWork for ScanStackRoots { for mutator in ::VMActivePlan::mutators() { mutator.flush(); } - mmtk.get().plan.common().base.set_gc_status(GcStatus::GcProper); + mmtk.get() + .plan + .common() + .base + .set_gc_status(GcStatus::GcProper); } } @@ -416,6 +423,7 @@ pub trait ProcessEdgesWork: fn cache_roots_for_sanity_gc(&mut self) { assert!(self.roots); self.mmtk() + .get() .sanity_checker .lock() .unwrap() diff --git a/src/util/options.rs b/src/util/options.rs index 019e8c7774..0c10390797 100644 --- a/src/util/options.rs +++ b/src/util/options.rs @@ -223,7 +223,7 @@ macro_rules! options { (@verify_set_from($self: expr, $key: expr, $verify_field: ident, $($name: ident),*)) => { match $key { $(stringify!($name) => { assert!($self.$name.$verify_field, "cannot set option {} (not {})", $key, stringify!($verify_field)) }),* - _ => panic!("Invalid Options key") + _ => panic!("Invalid Options key: {}", $key) } }; @@ -266,7 +266,7 @@ macro_rules! options { eprintln!("Warn: unable to set {}={:?}. Cant parse value. Default value will be used.", s, val); false })* - _ => panic!("Invalid Options key") + _ => panic!("Invalid Options key: {}", s) } } } diff --git a/src/util/reference_processor.rs b/src/util/reference_processor.rs index cd47e482ce..ba49ec0604 100644 --- a/src/util/reference_processor.rs +++ b/src/util/reference_processor.rs @@ -523,7 +523,9 @@ impl GCWork for PhantomRefProcessing { fn do_work(&mut self, worker: &mut GCWorker, mmtk: &'static MMTK) { let mut w = E::new(vec![], false, mmtk); w.set_worker(worker); - mmtk.get().reference_processors.scan_phantom_refs(&mut w, mmtk); + mmtk.get() + .reference_processors + .scan_phantom_refs(&mut w, mmtk); w.flush(); } } @@ -553,7 +555,9 @@ impl RefForwarding { pub struct RefEnqueue(PhantomData); impl GCWork for RefEnqueue { fn do_work(&mut self, worker: &mut GCWorker, mmtk: &'static MMTK) { - mmtk.get().reference_processors.enqueue_refs::(worker.tls); + mmtk.get() + .reference_processors + .enqueue_refs::(worker.tls); } } impl RefEnqueue { diff --git a/src/util/sanity/sanity_checker.rs b/src/util/sanity/sanity_checker.rs index 47383a5fc1..6247f845a7 100644 --- a/src/util/sanity/sanity_checker.rs +++ b/src/util/sanity/sanity_checker.rs @@ -72,7 +72,7 @@ impl GCWork for ScheduleSanityGC

{ // scheduler.work_buckets[WorkBucketStage::Prepare] // .add(ScanStackRoot::>(mutator)); // } - for roots in &mmtk.sanity_checker.lock().unwrap().roots { + for roots in &mmtk.get().sanity_checker.lock().unwrap().roots { scheduler.work_buckets[WorkBucketStage::Closure].add( SanityGCProcessEdges::::new(roots.clone(), true, mmtk), ); @@ -102,7 +102,7 @@ impl GCWork for SanityPrepare

{ fn do_work(&mut self, _worker: &mut GCWorker, mmtk: &'static MMTK) { mmtk.get().plan.enter_sanity(); { - let mut sanity_checker = mmtk.sanity_checker.lock().unwrap(); + let mut sanity_checker = mmtk.get().sanity_checker.lock().unwrap(); sanity_checker.refs.clear(); } for mutator in ::VMActivePlan::mutators() { @@ -129,7 +129,11 @@ impl SanityRelease

{ impl GCWork for SanityRelease

{ fn do_work(&mut self, _worker: &mut GCWorker, mmtk: &'static MMTK) { mmtk.get().plan.leave_sanity(); - mmtk.sanity_checker.lock().unwrap().clear_roots_cache(); + mmtk.get() + .sanity_checker + .lock() + .unwrap() + .clear_roots_cache(); for mutator in ::VMActivePlan::mutators() { mmtk.get().scheduler.work_buckets[WorkBucketStage::Release] .add(ReleaseMutator::::new(mutator)); @@ -175,7 +179,7 @@ impl ProcessEdgesWork for SanityGCProcessEdges { if object.is_null() { return object; } - let mut sanity_checker = self.mmtk().sanity_checker.lock().unwrap(); + let mut sanity_checker = self.mmtk().get().sanity_checker.lock().unwrap(); if !sanity_checker.refs.contains(&object) { // FIXME steveb consider VM-specific integrity check on reference. assert!(object.is_sane(), "Invalid reference {:?}", object); diff --git a/vmbindings/dummyvm/api/mmtk.h b/vmbindings/dummyvm/api/mmtk.h index 6f0843d729..a7ddc02253 100644 --- a/vmbindings/dummyvm/api/mmtk.h +++ b/vmbindings/dummyvm/api/mmtk.h @@ -18,7 +18,8 @@ extern "C" { typedef void* MMTk_Mutator; // Initialize an MMTk instance -extern void mmtk_gc_init(size_t heap_size); +extern void mmtk_gc_init(); +extern void mmtk_set_heap_size(size_t size); // Request MMTk to create a new mutator for the given `tls` thread extern MMTk_Mutator mmtk_bind_mutator(void* tls); diff --git a/vmbindings/dummyvm/src/api.rs b/vmbindings/dummyvm/src/api.rs index dfdd01b639..9bfbe99985 100644 --- a/vmbindings/dummyvm/src/api.rs +++ b/vmbindings/dummyvm/src/api.rs @@ -14,12 +14,17 @@ use crate::DummyVM; use crate::SINGLETON; #[no_mangle] -pub extern "C" fn mmtk_gc_init(heap_size: usize) { +pub extern "C" fn mmtk_gc_init() { // # Safety // Casting `SINGLETON` as mutable is safe because `gc_init` will only be executed once by a single thread during startup. #[allow(clippy::cast_ref_to_mut)] let singleton_mut = unsafe { &mut *(&*SINGLETON as *const MMTK as *mut MMTK) }; - memory_manager::gc_init(singleton_mut, heap_size) + memory_manager::gc_init(singleton_mut) +} + +#[no_mangle] +pub extern "C" fn mmtk_set_heap_size(size: usize) { + memory_manager::process(&SINGLETON, "heap_size", size.to_string().as_str()); } #[no_mangle] diff --git a/vmbindings/dummyvm/src/tests/allocate_with_disable_collection.rs b/vmbindings/dummyvm/src/tests/allocate_with_disable_collection.rs index 7f70d00684..613e0e6ffb 100644 --- a/vmbindings/dummyvm/src/tests/allocate_with_disable_collection.rs +++ b/vmbindings/dummyvm/src/tests/allocate_with_disable_collection.rs @@ -8,7 +8,8 @@ use mmtk::AllocationSemantics; pub fn allocate_with_disable_collection() { const MB: usize = 1024 * 1024; // 1MB heap - mmtk_gc_init(MB); + mmtk_set_heap_size(MB); + mmtk_gc_init(); mmtk_initialize_collection(VMThread::UNINITIALIZED); let handle = mmtk_bind_mutator(VMMutatorThread(VMThread::UNINITIALIZED)); // Allocate 1MB. It should be fine. diff --git a/vmbindings/dummyvm/src/tests/allocate_with_initialize_collection.rs b/vmbindings/dummyvm/src/tests/allocate_with_initialize_collection.rs index 837b6aa268..149fe16362 100644 --- a/vmbindings/dummyvm/src/tests/allocate_with_initialize_collection.rs +++ b/vmbindings/dummyvm/src/tests/allocate_with_initialize_collection.rs @@ -9,7 +9,8 @@ use mmtk::AllocationSemantics; pub fn allocate_with_initialize_collection() { const MB: usize = 1024 * 1024; // 1MB heap - mmtk_gc_init(MB); + mmtk_set_heap_size(MB); + mmtk_gc_init(); mmtk_initialize_collection(VMThread::UNINITIALIZED); let handle = mmtk_bind_mutator(VMMutatorThread(VMThread::UNINITIALIZED)); // Attempt to allocate 2MB. This will trigger GC. diff --git a/vmbindings/dummyvm/src/tests/allocate_with_re_enable_collection.rs b/vmbindings/dummyvm/src/tests/allocate_with_re_enable_collection.rs index 6ad75a426e..0c5ad7e460 100644 --- a/vmbindings/dummyvm/src/tests/allocate_with_re_enable_collection.rs +++ b/vmbindings/dummyvm/src/tests/allocate_with_re_enable_collection.rs @@ -9,7 +9,8 @@ use mmtk::AllocationSemantics; pub fn allocate_with_re_enable_collection() { const MB: usize = 1024 * 1024; // 1MB heap - mmtk_gc_init(MB); + mmtk_set_heap_size(MB); + mmtk_gc_init(); mmtk_initialize_collection(VMThread::UNINITIALIZED); let handle = mmtk_bind_mutator(VMMutatorThread(VMThread::UNINITIALIZED)); // Allocate 1MB. It should be fine. diff --git a/vmbindings/dummyvm/src/tests/allocate_without_initialize_collection.rs b/vmbindings/dummyvm/src/tests/allocate_without_initialize_collection.rs index 2bfd5e5c11..86a2508c7c 100644 --- a/vmbindings/dummyvm/src/tests/allocate_without_initialize_collection.rs +++ b/vmbindings/dummyvm/src/tests/allocate_without_initialize_collection.rs @@ -9,7 +9,8 @@ use mmtk::AllocationSemantics; pub fn allocate_without_initialize_collection() { const MB: usize = 1024 * 1024; // 1MB heap - mmtk_gc_init(MB); + mmtk_set_heap_size(MB); + mmtk_gc_init(); let handle = mmtk_bind_mutator(VMMutatorThread(VMThread::UNINITIALIZED)); // Attempt to allocate 2MB memory. This should trigger a GC, but as we never call initialize_collection(), we cannot do GC. let addr = mmtk_alloc(handle, 2 * MB, 8, 0, AllocationSemantics::Default); diff --git a/vmbindings/dummyvm/src/tests/fixtures/mod.rs b/vmbindings/dummyvm/src/tests/fixtures/mod.rs index a3d30fbe50..308a2e1ed3 100644 --- a/vmbindings/dummyvm/src/tests/fixtures/mod.rs +++ b/vmbindings/dummyvm/src/tests/fixtures/mod.rs @@ -73,7 +73,8 @@ impl FixtureContent for SingleObject { fn create() -> Self { const MB: usize = 1024 * 1024; // 1MB heap - mmtk_gc_init(MB); + mmtk_set_heap_size(MB); + mmtk_gc_init(); mmtk_initialize_collection(VMThread::UNINITIALIZED); // Make sure GC does not run during test. mmtk_disable_collection(); @@ -101,7 +102,8 @@ impl FixtureContent for MMTKSingleton { fn create() -> Self { const MB: usize = 1024 * 1024; // 1MB heap - mmtk_gc_init(MB); + mmtk_set_heap_size(MB); + mmtk_gc_init(); mmtk_initialize_collection(VMThread::UNINITIALIZED); MMTKSingleton { diff --git a/vmbindings/dummyvm/src/tests/issue139.rs b/vmbindings/dummyvm/src/tests/issue139.rs index ed6d4e8998..2d5ace31ed 100644 --- a/vmbindings/dummyvm/src/tests/issue139.rs +++ b/vmbindings/dummyvm/src/tests/issue139.rs @@ -4,7 +4,8 @@ use mmtk::AllocationSemantics; #[test] pub fn issue139_alloc_non_multiple_of_min_alignment() { - mmtk_gc_init(200*1024*1024); + mmtk_set_heap_size(200*1024*1024); + mmtk_gc_init(); let handle = mmtk_bind_mutator(VMMutatorThread(VMThread::UNINITIALIZED)); // Allocate 6 bytes with 8 bytes ailgnment required From 69aa4fb2edc41c265a1c4abae38b84b0bf02a29c Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Mon, 18 Jul 2022 13:14:40 +1000 Subject: [PATCH 05/14] Add some comments --- src/mmtk.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/mmtk.rs b/src/mmtk.rs index a66d91f9c8..1416436df2 100644 --- a/src/mmtk.rs +++ b/src/mmtk.rs @@ -44,11 +44,16 @@ pub static SFT_MAP: InitializeOnce> = InitializeOnce::new(); /// An MMTk instance. MMTk allows multiple instances to run independently, and each instance gives users a separate heap. /// *Note that multi-instances is not fully supported yet* pub struct MMTK { + /// The options for this instance. pub(crate) options: Arc, + /// The actual instance. This field starts as uninitialized, and will be initialized in `gc_init()`. As we will use + /// options to initialize the instance, initializing this later allows users to set command line options before they call `gc_init()`. instance: MaybeUninit>, + /// Track if the `instance` field is initialized. pub(crate) is_initialized: AtomicBool, } +/// The actual MMTk instance. pub struct MMTKInner { pub(crate) plan: Box>, pub(crate) reference_processors: ReferenceProcessors, @@ -110,6 +115,8 @@ impl MMTK { self.is_initialized.store(true, Ordering::SeqCst); let heap_size = *self.options.heap_size; + // TODO: We should remove Plan.gc_init(). We create plan in `MMTKInner::new()`, and we + // should be able move whatever we do in gc_init() to Plan::new(). self.get_mut().plan.gc_init(heap_size, &crate::VM_MAP); } From c0430d2d685c290e28b617e4f2277a44ba8564d9 Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Tue, 19 Jul 2022 11:28:22 +1000 Subject: [PATCH 06/14] Add MMTKBuilder --- docs/tutorial/src/mygc/create.md | 6 +- src/lib.rs | 1 + src/memory_manager.rs | 103 ++++++++--------- src/mmtk.rs | 135 +++++++++++------------ src/plan/barriers.rs | 4 +- src/plan/generational/copying/mutator.rs | 9 +- src/plan/generational/immix/mutator.rs | 9 +- src/plan/global.rs | 18 ++- src/plan/markcompact/gc_work.rs | 11 +- src/plan/marksweep/gc_work.rs | 2 +- src/scheduler/gc_work.rs | 56 ++++------ src/scheduler/scheduler.rs | 4 +- src/scheduler/worker.rs | 2 +- src/util/analysis/mod.rs | 2 +- src/util/finalizable_processor.rs | 10 +- src/util/malloc/mod.rs | 10 +- src/util/reference_processor.rs | 32 +++--- src/util/sanity/sanity_checker.rs | 26 ++--- src/util/statistics/stats.rs | 2 +- vmbindings/dummyvm/src/api.rs | 14 +-- vmbindings/dummyvm/src/lib.rs | 17 ++- 21 files changed, 218 insertions(+), 255 deletions(-) diff --git a/docs/tutorial/src/mygc/create.md b/docs/tutorial/src/mygc/create.md index de7fbe965e..fa54825142 100644 --- a/docs/tutorial/src/mygc/create.md +++ b/docs/tutorial/src/mygc/create.md @@ -53,15 +53,15 @@ files. mmtk: &'static MMTK, ) -> Box> { Box::new(match mmtk.options.plan { - PlanSelector::NoGC => crate::plan::nogc::mutator::create_nogc_mutator(tls, &*mmtk.get().plan), + PlanSelector::NoGC => crate::plan::nogc::mutator::create_nogc_mutator(tls, &*mmtk.plan), PlanSelector::SemiSpace => { - crate::plan::semispace::mutator::create_ss_mutator(tls, &*mmtk.get().plan) + crate::plan::semispace::mutator::create_ss_mutator(tls, &*mmtk.plan) } // ... // Create MyGC mutator based on selector - PlanSelector::MyGC => crate::plan::mygc::mutator::create_mygc_mutator(tls, &*mmtk.get().plan), }) + PlanSelector::MyGC => crate::plan::mygc::mutator::create_mygc_mutator(tls, &*mmtk.plan), }) } pub fn create_plan( diff --git a/src/lib.rs b/src/lib.rs index 47ffb1eaae..fa5b84c6d5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -45,6 +45,7 @@ extern crate num_cpus; extern crate downcast_rs; mod mmtk; +pub use mmtk::MMTKBuilder; pub(crate) use mmtk::MMAPPER; pub use mmtk::MMTK; pub(crate) use mmtk::VM_MAP; diff --git a/src/memory_manager.rs b/src/memory_manager.rs index 569465e6df..a81475dfc6 100644 --- a/src/memory_manager.rs +++ b/src/memory_manager.rs @@ -11,6 +11,7 @@ //! it can turn the `Box` pointer to a native pointer (`*mut Mutator`), and forge a mut reference from the native //! pointer. Either way, the VM binding code needs to guarantee the safety. +use crate::mmtk::MMTKBuilder; use crate::mmtk::MMTK; use crate::plan::AllocationSemantics; use crate::plan::{Mutator, MutatorContext}; @@ -31,22 +32,27 @@ use std::sync::atomic::Ordering; /// /// We expect a binding to ininitialize MMTk in the following steps: /// -/// 1. Create an [MMTK](../mmtk/struct.MMTK.html) instance. Usually this is done statically, i.e. a binding holds one singleton as a static variable for MMTk. -/// 2. Set command line options for MMTk by [process()](./fn.process.html) or [process_bulk()](./fn.process_bulk.html). At this point, -/// other APIs should not be used, as MMTk is not yet initialized. -/// 3. Initialize MMTk by calling this function, `gc_init()`. After this call, most MMTk APIs are avialable for use. But MMTk no longer expects -/// new command line options. If a binding calls `process()` after this point, it may have no effect. +/// 1. Create an [MMTKBuilder](../mmtk/struct.MMTKBuilder.html) instance. +/// 2. Set command line options for MMTKBuilder by [process()](./fn.process.html) or [process_bulk()](./fn.process_bulk.html). +/// 3. Initialize MMTk by calling this function, `gc_init()`, and pass the builder earlier. This call will return an MMTK instance. +/// Usually a binding store the MMTK instance statically as a singleton. We plan to allow multiple instances, but this is not yet fully +/// supported. Currently we assume a binding will only need one MMTk instance. /// 4. Enable garbage collection in MMTk by [enable_collection()](./fn.enable_collection.html). A binding should only call this once its /// thread system is ready. MMTk will not trigger garbage collection before this call. /// -/// Not that This method will attempt to initialize a logger. If the VM would like to use its own logger, it should initialize the logger before calling this method. +/// Note that this method will attempt to initialize a logger. If the VM would like to use its own logger, it should initialize the logger before calling this method. /// Note that, to allow MMTk to do GC properly, `initialize_collection()` needs to be called after this call when /// the VM's thread system is ready to spawn GC workers. /// +/// Note that this method returns a boxed pointer of MMTK, which means MMTk has a bound lifetime with the box pointer. However, some of our current APIs assume +/// that MMTk has a static lifetime, which presents a mismatch with this API. We plan to address the lifetime issue in the future. At this point, we recommend a binding +/// to 'expand' the lifetime for the boxed pointer to static. There could be multiple ways to achieve it: 1. `Box::leak()` will turn the box pointer to raw pointer +/// which has static lifetime, 2. create MMTK as a lazily initialized static variable +/// (see [what we do for our dummy binding](https://github.com/mmtk/mmtk-core/blob/master/vmbindings/dummyvm/src/lib.rs#L42)) +/// /// Arguments: -/// * `mmtk`: A reference to an MMTk instance to initialize. -/// * `heap_size`: The heap size for the MMTk instance in bytes. -pub fn gc_init(mmtk: &'static mut MMTK) { +/// * `builder`: The reference to a MMTk builder. +pub fn gc_init(builder: &MMTKBuilder) -> Box> { match crate::util::logger::try_init() { Ok(_) => debug!("MMTk initialized the logger."), Err(_) => debug!( @@ -70,15 +76,11 @@ pub fn gc_init(mmtk: &'static mut MMTK) { } } } - mmtk.initialize(); + let mmtk = builder.build(); info!("Initialized MMTk with {:?}", *mmtk.options.plan); #[cfg(feature = "extreme_assertions")] warn!("The feature 'extreme_assertions' is enabled. MMTk will run expensive run-time checks. Slow performance should be expected."); -} - -/// Is MMTk initialized? MMTk is initialized once `gc_init()` is called. -pub fn is_gc_initialized(mmtk: &'static MMTK) -> bool { - mmtk.is_initialized.load(Ordering::SeqCst) + Box::new(mmtk) } /// Request MMTk to create a mutator for the given thread. For performance reasons, A VM should @@ -165,7 +167,7 @@ pub fn get_allocator_mapping( mmtk: &MMTK, semantics: AllocationSemantics, ) -> AllocatorSelector { - mmtk.get().plan.get_allocator_mapping()[semantics] + mmtk.plan.get_allocator_mapping()[semantics] } /// The standard malloc. MMTk either uses its own allocator, or forward the call to a @@ -287,15 +289,11 @@ pub fn start_worker( /// Collection::spawn_gc_thread() so that the VM knows the context. pub fn initialize_collection(mmtk: &'static MMTK, tls: VMThread) { assert!( - !mmtk.get().plan.is_initialized(), + !mmtk.plan.is_initialized(), "MMTk collection has been initialized (was initialize_collection() already called before?)" ); - mmtk.get().scheduler.spawn_gc_threads(mmtk, tls); - mmtk.get() - .plan - .base() - .initialized - .store(true, Ordering::SeqCst); + mmtk.scheduler.spawn_gc_threads(mmtk, tls); + mmtk.plan.base().initialized.store(true, Ordering::SeqCst); } /// Allow MMTk to trigger garbage collection when heap is full. This should only be used in pair with disable_collection(). @@ -306,11 +304,10 @@ pub fn initialize_collection(mmtk: &'static MMTK, tls: VMThre /// * `mmtk`: A reference to an MMTk instance. pub fn enable_collection(mmtk: &'static MMTK) { debug_assert!( - !mmtk.get().plan.should_trigger_gc_when_heap_is_full(), + !mmtk.plan.should_trigger_gc_when_heap_is_full(), "enable_collection() is called when GC is already enabled." ); - mmtk.get() - .plan + mmtk.plan .base() .trigger_gc_when_heap_is_full .store(true, Ordering::SeqCst); @@ -328,11 +325,10 @@ pub fn enable_collection(mmtk: &'static MMTK) { /// * `mmtk`: A reference to an MMTk instance. pub fn disable_collection(mmtk: &'static MMTK) { debug_assert!( - mmtk.get().plan.should_trigger_gc_when_heap_is_full(), + mmtk.plan.should_trigger_gc_when_heap_is_full(), "disable_collection() is called when GC is not enabled." ); - mmtk.get() - .plan + mmtk.plan .base() .trigger_gc_when_heap_is_full .store(false, Ordering::SeqCst); @@ -345,8 +341,8 @@ pub fn disable_collection(mmtk: &'static MMTK) { /// * `mmtk`: A reference to an MMTk instance. /// * `name`: The name of the option. /// * `value`: The value of the option (as a string). -pub fn process(mmtk: &'static MMTK, name: &str, value: &str) -> bool { - unsafe { mmtk.options.process(name, value) } +pub fn process(builder: &'static MMTKBuilder, name: &str, value: &str) -> bool { + unsafe { builder.set_option(name, value) } } /// Process multiple MMTk run-time options. Returns true if all the options are processed successfully. @@ -355,8 +351,8 @@ pub fn process(mmtk: &'static MMTK, name: &str, value: &str) /// Arguments: /// * `mmtk`: A reference to an MMTk instance. /// * `options`: a string that is key value pairs separated by white spaces, e.g. "threads=1 stress_factor=4096" -pub fn process_bulk(mmtk: &'static MMTK, options: &str) -> bool { - unsafe { mmtk.options.process_bulk(options) } +pub fn process_bulk(builder: &'static MMTKBuilder, options: &str) -> bool { + unsafe { builder.set_options_bulk_by_str(options) } } /// Return used memory in bytes. @@ -364,7 +360,7 @@ pub fn process_bulk(mmtk: &'static MMTK, options: &str) -> bo /// Arguments: /// * `mmtk`: A reference to an MMTk instance. pub fn used_bytes(mmtk: &MMTK) -> usize { - mmtk.get().plan.get_used_pages() << LOG_BYTES_IN_PAGE + mmtk.plan.get_used_pages() << LOG_BYTES_IN_PAGE } /// Return free memory in bytes. @@ -372,7 +368,7 @@ pub fn used_bytes(mmtk: &MMTK) -> usize { /// Arguments: /// * `mmtk`: A reference to an MMTk instance. pub fn free_bytes(mmtk: &MMTK) -> usize { - mmtk.get().plan.get_free_pages() << LOG_BYTES_IN_PAGE + mmtk.plan.get_free_pages() << LOG_BYTES_IN_PAGE } /// Return the starting address of the heap. *Note that currently MMTk uses @@ -392,7 +388,7 @@ pub fn last_heap_address() -> Address { /// Arguments: /// * `mmtk`: A reference to an MMTk instance. pub fn total_bytes(mmtk: &MMTK) -> usize { - mmtk.get().plan.get_total_pages() << LOG_BYTES_IN_PAGE + mmtk.plan.get_total_pages() << LOG_BYTES_IN_PAGE } /// Trigger a garbage collection as requested by the user. @@ -401,7 +397,7 @@ pub fn total_bytes(mmtk: &MMTK) -> usize { /// * `mmtk`: A reference to an MMTk instance. /// * `tls`: The thread that triggers this collection request. pub fn handle_user_collection_request(mmtk: &MMTK, tls: VMMutatorThread) { - mmtk.get().plan.handle_user_collection_request(tls, false); + mmtk.plan.handle_user_collection_request(tls, false); } /// Is the object alive? @@ -513,7 +509,7 @@ pub fn is_mapped_address(address: Address) -> bool { /// * `mmtk`: A reference to an MMTk instance. /// * `object`: The object to check. pub fn modify_check(mmtk: &MMTK, object: ObjectReference) { - mmtk.get().plan.modify_check(object); + mmtk.plan.modify_check(object); } /// Add a reference to the list of weak references. A binding may @@ -523,9 +519,7 @@ pub fn modify_check(mmtk: &MMTK, object: ObjectReference) { /// * `mmtk`: A reference to an MMTk instance. /// * `reff`: The weak reference to add. pub fn add_weak_candidate(mmtk: &MMTK, reff: ObjectReference) { - mmtk.get() - .reference_processors - .add_weak_candidate::(reff); + mmtk.reference_processors.add_weak_candidate::(reff); } /// Add a reference to the list of soft references. A binding may @@ -535,9 +529,7 @@ pub fn add_weak_candidate(mmtk: &MMTK, reff: ObjectReference) /// * `mmtk`: A reference to an MMTk instance. /// * `reff`: The soft reference to add. pub fn add_soft_candidate(mmtk: &MMTK, reff: ObjectReference) { - mmtk.get() - .reference_processors - .add_soft_candidate::(reff); + mmtk.reference_processors.add_soft_candidate::(reff); } /// Add a reference to the list of phantom references. A binding may @@ -547,9 +539,7 @@ pub fn add_soft_candidate(mmtk: &MMTK, reff: ObjectReference) /// * `mmtk`: A reference to an MMTk instance. /// * `reff`: The phantom reference to add. pub fn add_phantom_candidate(mmtk: &MMTK, reff: ObjectReference) { - mmtk.get() - .reference_processors - .add_phantom_candidate::(reff); + mmtk.reference_processors.add_phantom_candidate::(reff); } /// Generic hook to allow benchmarks to be harnessed. We do a full heap @@ -586,7 +576,7 @@ pub fn add_finalizer( warn!("add_finalizer() is called when no_finalizer = true"); } - mmtk.get().finalizable_processor.lock().unwrap().add(object); + mmtk.finalizable_processor.lock().unwrap().add(object); } /// Get an object that is ready for finalization. After each GC, if any registered object is not @@ -604,8 +594,7 @@ pub fn get_finalized_object( warn!("get_finalized_object() is called when no_finalizer = true"); } - mmtk.get() - .finalizable_processor + mmtk.finalizable_processor .lock() .unwrap() .get_ready_object() @@ -625,8 +614,7 @@ pub fn get_all_finalizers( warn!("get_all_finalizers() is called when no_finalizer = true"); } - mmtk.get() - .finalizable_processor + mmtk.finalizable_processor .lock() .unwrap() .get_all_finalizers() @@ -646,8 +634,7 @@ pub fn get_finalizers_for( warn!("get_finalizers() is called when no_finalizer = true"); } - mmtk.get() - .finalizable_processor + mmtk.finalizable_processor .lock() .unwrap() .get_finalizers_for(object) @@ -660,7 +647,7 @@ pub fn get_finalizers_for( /// Arguments: /// * `mmtk`: A reference to an MMTk instance. pub fn num_of_workers(mmtk: &'static MMTK) -> usize { - mmtk.get().scheduler.num_workers() + mmtk.scheduler.num_workers() } /// Add a work packet to the given work bucket. Note that this simply adds the work packet to the given @@ -675,7 +662,7 @@ pub fn add_work_packet>( bucket: WorkBucketStage, packet: W, ) { - mmtk.get().scheduler.work_buckets[bucket].add(packet) + mmtk.scheduler.work_buckets[bucket].add(packet) } /// Bulk add a number of work packets to the given work bucket. Note that this simply adds the work packets @@ -690,11 +677,11 @@ pub fn add_work_packets( bucket: WorkBucketStage, packets: Vec>>, ) { - mmtk.get().scheduler.work_buckets[bucket].bulk_add(packets) + mmtk.scheduler.work_buckets[bucket].bulk_add(packets) } /// Add a callback to be notified after the transitive closure is finished. /// The callback should return true if it add more work packets to the closure bucket. pub fn on_closure_end(mmtk: &'static MMTK, f: Box bool>) { - mmtk.get().scheduler.on_closure_end(f) + mmtk.scheduler.on_closure_end(f) } diff --git a/src/mmtk.rs b/src/mmtk.rs index 1416436df2..4ce5703175 100644 --- a/src/mmtk.rs +++ b/src/mmtk.rs @@ -18,8 +18,6 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use std::sync::Mutex; -use std::mem::MaybeUninit; - lazy_static! { // I am not sure if we should include these mmappers as part of MMTk struct. // The considerations are: @@ -41,20 +39,61 @@ use crate::util::rust_util::InitializeOnce; // A global space function table that allows efficient dispatch space specific code for addresses in our heap. pub static SFT_MAP: InitializeOnce> = InitializeOnce::new(); +// MMTk builder. This is used to set options before actually creating an MMTk instance. +pub struct MMTKBuilder { + /// The options for this instance. + options: Arc, +} + +impl MMTKBuilder { + /// Create an MMTK builder with default options + pub fn new() -> Self { + let options = Arc::new(UnsafeOptionsWrapper::new(Options::default())); + MMTKBuilder { options } + } + + /// Set an option. + /// + /// # Safety + /// This method is not thread safe, as internally it acquires a mutable reference to self. + /// It is supposed to be used by one thread during boot time. + pub unsafe fn set_option(&self, name: &str, val: &str) -> bool { + self.options.process(name, val) + } + + /// Set multiple options by a string. The string should be key-value pairs separated by white spaces, + /// such as `threads=1 stress_factor=4096`. + /// + /// # Safety + /// This method is not thread safe, as internally it acquires a mutable reference to self. + /// It is supposed to be used by one thread during boot time. + pub unsafe fn set_options_bulk_by_str(&self, options: &str) -> bool { + self.options.process_bulk(options) + } + + /// Build an MMTk instance from the builder. + pub fn build(&self) -> MMTK { + let mut mmtk = MMTK::new(self.options.clone()); + + let heap_size = *self.options.heap_size; + // TODO: We should remove Plan.gc_init(). We create plan in `MMTKInner::new()`, and we + // should be able move whatever we do in gc_init() to Plan::new(). + mmtk.plan.gc_init(heap_size, &crate::VM_MAP); + + mmtk + } +} + +impl Default for MMTKBuilder { + fn default() -> Self { + Self::new() + } +} + /// An MMTk instance. MMTk allows multiple instances to run independently, and each instance gives users a separate heap. /// *Note that multi-instances is not fully supported yet* pub struct MMTK { - /// The options for this instance. pub(crate) options: Arc, - /// The actual instance. This field starts as uninitialized, and will be initialized in `gc_init()`. As we will use - /// options to initialize the instance, initializing this later allows users to set command line options before they call `gc_init()`. - instance: MaybeUninit>, - /// Track if the `instance` field is initialized. - pub(crate) is_initialized: AtomicBool, -} - -/// The actual MMTk instance. -pub struct MMTKInner { pub(crate) plan: Box>, pub(crate) reference_processors: ReferenceProcessors, pub(crate) finalizable_processor: @@ -65,8 +104,12 @@ pub struct MMTKInner { inside_harness: AtomicBool, } -impl MMTKInner { +impl MMTK { pub fn new(options: Arc) -> Self { + // Initialize SFT first in case we need to use this in the constructor. + // The first call will initialize SFT map. Other calls will be blocked until SFT map is initialized. + SFT_MAP.initialize_once(&SFTMap::new); + let num_workers = if cfg!(feature = "single_worker") { 1 } else { @@ -82,7 +125,8 @@ impl MMTKInner { scheduler.clone(), ); - MMTKInner { + MMTK { + options, plan, reference_processors: ReferenceProcessors::new(), finalizable_processor: Mutex::new(FinalizableProcessor::< @@ -94,65 +138,22 @@ impl MMTKInner { inside_harness: AtomicBool::new(false), } } -} - -impl MMTK { - pub fn new() -> Self { - // Initialize SFT first in case we need to use this in the constructor. - // The first call will initialize SFT map. Other calls will be blocked until SFT map is initialized. - SFT_MAP.initialize_once(&SFTMap::new); - - let options = Arc::new(UnsafeOptionsWrapper::new(Options::default())); - MMTK { - options, - instance: MaybeUninit::>::uninit(), - is_initialized: AtomicBool::new(false), - } - } - - pub(crate) fn initialize(&mut self) { - self.instance.write(MMTKInner::new(self.options.clone())); - self.is_initialized.store(true, Ordering::SeqCst); - - let heap_size = *self.options.heap_size; - // TODO: We should remove Plan.gc_init(). We create plan in `MMTKInner::new()`, and we - // should be able move whatever we do in gc_init() to Plan::new(). - self.get_mut().plan.gc_init(heap_size, &crate::VM_MAP); - } - - #[inline(always)] - pub fn get(&self) -> &MMTKInner { - debug_assert!( - self.is_initialized.load(Ordering::SeqCst), - "MMTK is not initialized (is gc_init() called?)" - ); - unsafe { self.instance.assume_init_ref() } - } - - #[inline(always)] - pub fn get_mut(&mut self) -> &mut MMTKInner { - debug_assert!( - self.is_initialized.load(Ordering::SeqCst), - "MMTK is not initialized (is gc_init() called?)" - ); - unsafe { self.instance.assume_init_mut() } - } pub fn harness_begin(&self, tls: VMMutatorThread) { // FIXME Do a full heap GC if we have generational GC - self.get().plan.handle_user_collection_request(tls, true); - self.get().inside_harness.store(true, Ordering::SeqCst); - self.get().plan.base().stats.start_all(); - self.get().scheduler.enable_stat(); + self.plan.handle_user_collection_request(tls, true); + self.inside_harness.store(true, Ordering::SeqCst); + self.plan.base().stats.start_all(); + self.scheduler.enable_stat(); } pub fn harness_end(&'static self) { - self.get().plan.base().stats.stop_all(self); - self.get().inside_harness.store(false, Ordering::SeqCst); + self.plan.base().stats.stop_all(self); + self.inside_harness.store(false, Ordering::SeqCst); } pub fn get_plan(&self) -> &dyn Plan { - self.get().plan.as_ref() + self.plan.as_ref() } #[inline(always)] @@ -160,9 +161,3 @@ impl MMTK { &self.options } } - -impl Default for MMTK { - fn default() -> Self { - Self::new() - } -} diff --git a/src/plan/barriers.rs b/src/plan/barriers.rs index e1cc19863f..f1245131bf 100644 --- a/src/plan/barriers.rs +++ b/src/plan/barriers.rs @@ -101,12 +101,12 @@ impl Barrier for ObjectRememberingBarrier { let mut modbuf = vec![]; std::mem::swap(&mut modbuf, &mut self.modbuf); debug_assert!( - !self.mmtk.get().scheduler.work_buckets[WorkBucketStage::Final].is_activated(), + !self.mmtk.scheduler.work_buckets[WorkBucketStage::Final].is_activated(), "{:?}", self as *const _ ); if !modbuf.is_empty() { - self.mmtk.get().scheduler.work_buckets[WorkBucketStage::Closure] + self.mmtk.scheduler.work_buckets[WorkBucketStage::Closure] .add(ProcessModBuf::::new(modbuf, self.meta)); } } diff --git a/src/plan/generational/copying/mutator.rs b/src/plan/generational/copying/mutator.rs index 950861ab8b..b1bb2ecaa6 100644 --- a/src/plan/generational/copying/mutator.rs +++ b/src/plan/generational/copying/mutator.rs @@ -32,19 +32,16 @@ pub fn create_gencopy_mutator( mutator_tls: VMMutatorThread, mmtk: &'static MMTK, ) -> Mutator { - let gencopy = mmtk.get().plan.downcast_ref::>().unwrap(); + let gencopy = mmtk.plan.downcast_ref::>().unwrap(); let config = MutatorConfig { allocator_mapping: &*ALLOCATOR_MAPPING, - space_mapping: Box::new(create_gen_space_mapping( - &*mmtk.get().plan, - &gencopy.gen.nursery, - )), + space_mapping: Box::new(create_gen_space_mapping(&*mmtk.plan, &gencopy.gen.nursery)), prepare_func: &gencopy_mutator_prepare, release_func: &gencopy_mutator_release, }; Mutator { - allocators: Allocators::::new(mutator_tls, &*mmtk.get().plan, &config.space_mapping), + allocators: Allocators::::new(mutator_tls, &*mmtk.plan, &config.space_mapping), barrier: Box::new(ObjectRememberingBarrier::>::new( mmtk, *VM::VMObjectModel::GLOBAL_LOG_BIT_SPEC, diff --git a/src/plan/generational/immix/mutator.rs b/src/plan/generational/immix/mutator.rs index 1779e4a66c..ab38569713 100644 --- a/src/plan/generational/immix/mutator.rs +++ b/src/plan/generational/immix/mutator.rs @@ -30,19 +30,16 @@ pub fn create_genimmix_mutator( mutator_tls: VMMutatorThread, mmtk: &'static MMTK, ) -> Mutator { - let genimmix = mmtk.get().plan.downcast_ref::>().unwrap(); + let genimmix = mmtk.plan.downcast_ref::>().unwrap(); let config = MutatorConfig { allocator_mapping: &*ALLOCATOR_MAPPING, - space_mapping: Box::new(create_gen_space_mapping( - &*mmtk.get().plan, - &genimmix.gen.nursery, - )), + space_mapping: Box::new(create_gen_space_mapping(&*mmtk.plan, &genimmix.gen.nursery)), prepare_func: &genimmix_mutator_prepare, release_func: &genimmix_mutator_release, }; Mutator { - allocators: Allocators::::new(mutator_tls, &*mmtk.get().plan, &config.space_mapping), + allocators: Allocators::::new(mutator_tls, &*mmtk.plan, &config.space_mapping), barrier: Box::new(ObjectRememberingBarrier::>::new( mmtk, *VM::VMObjectModel::GLOBAL_LOG_BIT_SPEC, diff --git a/src/plan/global.rs b/src/plan/global.rs index fc7773caf6..871c2c7472 100644 --- a/src/plan/global.rs +++ b/src/plan/global.rs @@ -40,11 +40,9 @@ pub fn create_mutator( mmtk: &'static MMTK, ) -> Box> { Box::new(match *mmtk.options.plan { - PlanSelector::NoGC => { - crate::plan::nogc::mutator::create_nogc_mutator(tls, &*mmtk.get().plan) - } + PlanSelector::NoGC => crate::plan::nogc::mutator::create_nogc_mutator(tls, &*mmtk.plan), PlanSelector::SemiSpace => { - crate::plan::semispace::mutator::create_ss_mutator(tls, &*mmtk.get().plan) + crate::plan::semispace::mutator::create_ss_mutator(tls, &*mmtk.plan) } PlanSelector::GenCopy => { crate::plan::generational::copying::mutator::create_gencopy_mutator(tls, mmtk) @@ -53,16 +51,14 @@ pub fn create_mutator( crate::plan::generational::immix::mutator::create_genimmix_mutator(tls, mmtk) } PlanSelector::MarkSweep => { - crate::plan::marksweep::mutator::create_ms_mutator(tls, &*mmtk.get().plan) - } - PlanSelector::Immix => { - crate::plan::immix::mutator::create_immix_mutator(tls, &*mmtk.get().plan) + crate::plan::marksweep::mutator::create_ms_mutator(tls, &*mmtk.plan) } + PlanSelector::Immix => crate::plan::immix::mutator::create_immix_mutator(tls, &*mmtk.plan), PlanSelector::PageProtect => { - crate::plan::pageprotect::mutator::create_pp_mutator(tls, &*mmtk.get().plan) + crate::plan::pageprotect::mutator::create_pp_mutator(tls, &*mmtk.plan) } PlanSelector::MarkCompact => { - crate::plan::markcompact::mutator::create_markcompact_mutator(tls, &*mmtk.get().plan) + crate::plan::markcompact::mutator::create_markcompact_mutator(tls, &*mmtk.plan) } }) } @@ -105,7 +101,7 @@ pub fn create_gc_worker_context( tls: VMWorkerThread, mmtk: &'static MMTK, ) -> GCWorkerCopyContext { - GCWorkerCopyContext::::new(tls, &*mmtk.get().plan, mmtk.get().plan.create_copy_config()) + GCWorkerCopyContext::::new(tls, &*mmtk.plan, mmtk.plan.create_copy_config()) } /// A plan describes the global core functionality for all memory management schemes. diff --git a/src/plan/markcompact/gc_work.rs b/src/plan/markcompact/gc_work.rs index 38312630df..d7f76df34e 100644 --- a/src/plan/markcompact/gc_work.rs +++ b/src/plan/markcompact/gc_work.rs @@ -41,7 +41,7 @@ impl GCWork for UpdateReferences { fn do_work(&mut self, _worker: &mut GCWorker, mmtk: &'static MMTK) { // The following needs to be done right before the second round of root scanning VM::VMScanning::prepare_for_roots_re_scanning(); - mmtk.get().plan.base().prepare_for_stack_scanning(); + mmtk.plan.base().prepare_for_stack_scanning(); #[cfg(feature = "extreme_assertions")] crate::util::edge_logger::reset(); @@ -49,14 +49,11 @@ impl GCWork for UpdateReferences { // scheduler.work_buckets[WorkBucketStage::RefForwarding] // .add(ScanStackRoots::>::new()); for mutator in VM::VMActivePlan::mutators() { - mmtk.get().scheduler.work_buckets[WorkBucketStage::SecondRoots].add(ScanStackRoot::< - ForwardingProcessEdges, - >( - mutator - )); + mmtk.scheduler.work_buckets[WorkBucketStage::SecondRoots] + .add(ScanStackRoot::>(mutator)); } - mmtk.get().scheduler.work_buckets[WorkBucketStage::SecondRoots] + mmtk.scheduler.work_buckets[WorkBucketStage::SecondRoots] .add(ScanVMSpecificRoots::>::new()); } } diff --git a/src/plan/marksweep/gc_work.rs b/src/plan/marksweep/gc_work.rs index 05b92df4b4..01933edebd 100644 --- a/src/plan/marksweep/gc_work.rs +++ b/src/plan/marksweep/gc_work.rs @@ -64,7 +64,7 @@ impl GCWork for MSSweepChunks { ms.work_live_bytes.store(0, Ordering::SeqCst); } - mmtk.get().scheduler.work_buckets[WorkBucketStage::Release].bulk_add(work_packets); + mmtk.scheduler.work_buckets[WorkBucketStage::Release].bulk_add(work_packets); } } diff --git a/src/scheduler/gc_work.rs b/src/scheduler/gc_work.rs index 75cd3c7e65..badaa2999b 100644 --- a/src/scheduler/gc_work.rs +++ b/src/scheduler/gc_work.rs @@ -15,7 +15,7 @@ pub struct ScheduleCollection; impl GCWork for ScheduleCollection { fn do_work(&mut self, worker: &mut GCWorker, mmtk: &'static MMTK) { - mmtk.get().plan.schedule_collection(worker.scheduler()); + mmtk.plan.schedule_collection(worker.scheduler()); } } @@ -47,10 +47,10 @@ impl GCWork for Prepare { plan_mut.prepare(worker.tls); for mutator in ::VMActivePlan::mutators() { - mmtk.get().scheduler.work_buckets[WorkBucketStage::Prepare] + mmtk.scheduler.work_buckets[WorkBucketStage::Prepare] .add(PrepareMutator::::new(mutator)); } - for w in &mmtk.get().scheduler.worker_group.workers_shared { + for w in &mmtk.scheduler.worker_group.workers_shared { let result = w.designated_work.push(Box::new(PrepareCollector)); debug_assert!(result.is_ok()); } @@ -85,7 +85,7 @@ impl GCWork for PrepareCollector { fn do_work(&mut self, worker: &mut GCWorker, mmtk: &'static MMTK) { trace!("Prepare Collector"); worker.get_copy_context_mut().prepare(); - mmtk.get().plan.prepare_worker(worker); + mmtk.plan.prepare_worker(worker); } } @@ -116,10 +116,10 @@ impl GCWork for Release { plan_mut.release(worker.tls); for mutator in ::VMActivePlan::mutators() { - mmtk.get().scheduler.work_buckets[WorkBucketStage::Release] + mmtk.scheduler.work_buckets[WorkBucketStage::Release] .add(ReleaseMutator::::new(mutator)); } - for w in &mmtk.get().scheduler.worker_group.workers_shared { + for w in &mmtk.scheduler.worker_group.workers_shared { let result = w.designated_work.push(Box::new(ReleaseCollector)); debug_assert!(result.is_ok()); } @@ -176,24 +176,22 @@ impl GCWork for StopMutators { // If the VM requires that only the coordinator thread can stop the world, // we delegate the work to the coordinator. if ::VMCollection::COORDINATOR_ONLY_STW && !worker.is_coordinator() { - mmtk.get() - .scheduler + mmtk.scheduler .add_coordinator_work(StopMutators::::new(), worker); return; } trace!("stop_all_mutators start"); - mmtk.get().plan.base().prepare_for_stack_scanning(); + mmtk.plan.base().prepare_for_stack_scanning(); ::VMCollection::stop_all_mutators(worker.tls, |mutator| { - mmtk.get().scheduler.work_buckets[WorkBucketStage::Prepare] - .add(ScanStackRoot::(mutator)); + mmtk.scheduler.work_buckets[WorkBucketStage::Prepare].add(ScanStackRoot::(mutator)); }); trace!("stop_all_mutators end"); - mmtk.get().scheduler.notify_mutators_paused(mmtk); + mmtk.scheduler.notify_mutators_paused(mmtk); if ::VMScanning::SCAN_MUTATORS_IN_SAFEPOINT { // Prepare mutators if necessary // FIXME: This test is probably redundant. JikesRVM requires to call `prepare_mutator` once after mutators are paused - if !mmtk.get().plan.base().stacks_prepared() { + if !mmtk.plan.base().stacks_prepared() { for mutator in ::VMActivePlan::mutators() { ::VMCollection::prepare_mutator( worker.tls, @@ -204,17 +202,16 @@ impl GCWork for StopMutators { } // Scan mutators if ::VMScanning::SINGLE_THREAD_MUTATOR_SCANNING { - mmtk.get().scheduler.work_buckets[WorkBucketStage::Prepare] + mmtk.scheduler.work_buckets[WorkBucketStage::Prepare] .add(ScanStackRoots::::new()); } else { for mutator in ::VMActivePlan::mutators() { - mmtk.get().scheduler.work_buckets[WorkBucketStage::Prepare] + mmtk.scheduler.work_buckets[WorkBucketStage::Prepare] .add(ScanStackRoot::(mutator)); } } } - mmtk.get().scheduler.work_buckets[WorkBucketStage::Prepare] - .add(ScanVMSpecificRoots::::new()); + mmtk.scheduler.work_buckets[WorkBucketStage::Prepare].add(ScanVMSpecificRoots::::new()); } } @@ -228,7 +225,7 @@ impl GCWork for EndOfGC { info!("End of GC"); #[cfg(feature = "extreme_assertions")] - if crate::util::edge_logger::should_check_duplicate_edges(&*mmtk.get().plan) { + if crate::util::edge_logger::should_check_duplicate_edges(&*mmtk.plan) { // reset the logging info at the end of each GC crate::util::edge_logger::reset(); } @@ -238,10 +235,10 @@ impl GCWork for EndOfGC { "VM only allows coordinator to resume mutators, but the current worker is not the coordinator."); } - mmtk.get().plan.base().set_gc_status(GcStatus::NotInGC); + mmtk.plan.base().set_gc_status(GcStatus::NotInGC); // Reset the triggering information. - mmtk.get().plan.base().reset_collection_trigger(); + mmtk.plan.base().reset_collection_trigger(); ::VMCollection::resume_mutators(worker.tls); } @@ -288,11 +285,7 @@ impl GCWork for ScanStackRoots { for mutator in ::VMActivePlan::mutators() { mutator.flush(); } - mmtk.get() - .plan - .common() - .base - .set_gc_status(GcStatus::GcProper); + mmtk.plan.common().base.set_gc_status(GcStatus::GcProper); } } @@ -301,7 +294,7 @@ pub struct ScanStackRoot(pub &'static mut Mutator GCWork for ScanStackRoot { fn do_work(&mut self, worker: &mut GCWorker, mmtk: &'static MMTK) { trace!("ScanStackRoot for mutator {:?}", self.0.get_tls()); - let base = &mmtk.get().plan.base(); + let base = &mmtk.plan.base(); let mutators = ::VMActivePlan::number_of_mutators(); let factory = ProcessEdgesWorkRootsWorkFactory::::new(mmtk); ::VMScanning::scan_thread_root( @@ -311,7 +304,7 @@ impl GCWork for ScanStackRoot { ); self.0.flush(); - if mmtk.get().plan.base().inform_stack_scanned(mutators) { + if mmtk.plan.base().inform_stack_scanned(mutators) { ::VMScanning::notify_initial_thread_scan_complete( false, worker.tls, ); @@ -354,7 +347,7 @@ impl ProcessEdgesBase { // at creation. This avoids overhead for dynamic dispatch or downcasting plan for each object traced. pub fn new(edges: Vec

, roots: bool, mmtk: &'static MMTK) -> Self { #[cfg(feature = "extreme_assertions")] - if crate::util::edge_logger::should_check_duplicate_edges(&*mmtk.get().plan) { + if crate::util::edge_logger::should_check_duplicate_edges(&*mmtk.plan) { for edge in &edges { // log edge, panic if already logged crate::util::edge_logger::log_edge(*edge); @@ -381,7 +374,7 @@ impl ProcessEdgesBase { } #[inline] pub fn plan(&self) -> &'static dyn Plan { - &*self.mmtk.get().plan + &*self.mmtk.plan } /// Pop all nodes from nodes, and clear nodes to an empty vector. #[inline] @@ -423,7 +416,6 @@ pub trait ProcessEdgesWork: fn cache_roots_for_sanity_gc(&mut self) { assert!(self.roots); self.mmtk() - .get() .sanity_checker .lock() .unwrap() @@ -442,7 +434,7 @@ pub trait ProcessEdgesWork: // Executing these work packets now can remarkably reduce the global synchronization time. self.worker().do_boxed_work(work_packet); } else { - self.mmtk.get().scheduler.work_buckets[WorkBucketStage::Closure].add_boxed(work_packet); + self.mmtk.scheduler.work_buckets[WorkBucketStage::Closure].add_boxed(work_packet); } } @@ -641,7 +633,7 @@ impl GCWork for ProcessModBuf { store_metadata::(&self.meta, *obj, 1, None, Some(Ordering::SeqCst)); } } - if mmtk.get().plan.is_current_gc_nursery() { + if mmtk.plan.is_current_gc_nursery() { if !self.modbuf.is_empty() { let mut modbuf = vec![]; ::std::mem::swap(&mut modbuf, &mut self.modbuf); diff --git a/src/scheduler/scheduler.rs b/src/scheduler/scheduler.rs index f374825e9d..e37ee213a6 100644 --- a/src/scheduler/scheduler.rs +++ b/src/scheduler/scheduler.rs @@ -141,7 +141,7 @@ impl GCWorkScheduler { ); let gc_controller = GCController::new( mmtk, - mmtk.get().plan.base().gc_requester.clone(), + mmtk.plan.base().gc_requester.clone(), self.clone(), receiver, coordinator_worker, @@ -449,7 +449,7 @@ impl GCWorkScheduler { } pub fn notify_mutators_paused(&self, mmtk: &'static MMTK) { - mmtk.get().plan.base().gc_requester.clear_request(); + mmtk.plan.base().gc_requester.clear_request(); let first_stw_bucket = &self.work_buckets[WorkBucketStage::first_stw_stage()]; debug_assert!(!first_stw_bucket.is_activated()); first_stw_bucket.activate(); diff --git a/src/scheduler/worker.rs b/src/scheduler/worker.rs index edc74b619e..55cccc7dfa 100644 --- a/src/scheduler/worker.rs +++ b/src/scheduler/worker.rs @@ -222,7 +222,7 @@ impl WorkerGroup { let worker = Box::new(GCWorker::new( mmtk, ordinal, - mmtk.get().scheduler.clone(), + mmtk.scheduler.clone(), false, sender.clone(), shared.clone(), diff --git a/src/util/analysis/mod.rs b/src/util/analysis/mod.rs index a2fe1c6471..4814afe880 100644 --- a/src/util/analysis/mod.rs +++ b/src/util/analysis/mod.rs @@ -33,7 +33,7 @@ pub struct GcHookWork; impl GCWork for GcHookWork { fn do_work(&mut self, _worker: &mut GCWorker, mmtk: &'static MMTK) { - let base = &mmtk.get().plan.base(); + let base = &mmtk.plan.base(); base.analysis_manager.gc_hook(mmtk); } } diff --git a/src/util/finalizable_processor.rs b/src/util/finalizable_processor.rs index 086f98b418..a409d86231 100644 --- a/src/util/finalizable_processor.rs +++ b/src/util/finalizable_processor.rs @@ -131,7 +131,7 @@ pub struct Finalization(PhantomData); impl GCWork for Finalization { fn do_work(&mut self, worker: &mut GCWorker, mmtk: &'static MMTK) { - let mut finalizable_processor = mmtk.get().finalizable_processor.lock().unwrap(); + let mut finalizable_processor = mmtk.finalizable_processor.lock().unwrap(); debug!( "Finalization, {} objects in candidates, {} objects ready to finalize", finalizable_processor.candidates.len(), @@ -140,7 +140,7 @@ impl GCWork for Finalization { let mut w = E::new(vec![], false, mmtk); w.set_worker(worker); - finalizable_processor.scan(worker.tls, &mut w, mmtk.get().plan.is_current_gc_nursery()); + finalizable_processor.scan(worker.tls, &mut w, mmtk.plan.is_current_gc_nursery()); debug!( "Finished finalization, {} objects in candidates, {} objects ready to finalize", finalizable_processor.candidates.len(), @@ -160,12 +160,12 @@ pub struct ForwardFinalization(PhantomData); impl GCWork for ForwardFinalization { fn do_work(&mut self, worker: &mut GCWorker, mmtk: &'static MMTK) { trace!("Forward finalization"); - let mut finalizable_processor = mmtk.get().finalizable_processor.lock().unwrap(); + let mut finalizable_processor = mmtk.finalizable_processor.lock().unwrap(); let mut w = E::new(vec![], false, mmtk); w.set_worker(worker); - finalizable_processor.forward_candidate(&mut w, mmtk.get().plan.is_current_gc_nursery()); + finalizable_processor.forward_candidate(&mut w, mmtk.plan.is_current_gc_nursery()); - finalizable_processor.forward_finalizable(&mut w, mmtk.get().plan.is_current_gc_nursery()); + finalizable_processor.forward_finalizable(&mut w, mmtk.plan.is_current_gc_nursery()); trace!("Finished forwarding finlizable"); } } diff --git a/src/util/malloc/mod.rs b/src/util/malloc/mod.rs index 26214ccc97..551b014026 100644 --- a/src/util/malloc/mod.rs +++ b/src/util/malloc/mod.rs @@ -29,7 +29,7 @@ pub fn malloc(size: usize) -> Address { pub fn counted_malloc(mmtk: &MMTK, size: usize) -> Address { let res = malloc(size); if !res.is_zero() { - mmtk.get().plan.base().increase_malloc_bytes_by(size); + mmtk.plan.base().increase_malloc_bytes_by(size); } res } @@ -44,7 +44,7 @@ pub fn calloc(num: usize, size: usize) -> Address { pub fn counted_calloc(mmtk: &MMTK, num: usize, size: usize) -> Address { let res = calloc(num, size); if !res.is_zero() { - mmtk.get().plan.base().increase_malloc_bytes_by(num * size); + mmtk.plan.base().increase_malloc_bytes_by(num * size); } res } @@ -65,10 +65,10 @@ pub fn realloc_with_old_size( let res = realloc(addr, size); if !addr.is_zero() { - mmtk.get().plan.base().decrease_malloc_bytes_by(old_size); + mmtk.plan.base().decrease_malloc_bytes_by(old_size); } if size != 0 && !res.is_zero() { - mmtk.get().plan.base().increase_malloc_bytes_by(size); + mmtk.plan.base().increase_malloc_bytes_by(size); } res @@ -84,6 +84,6 @@ pub fn free(addr: Address) { pub fn free_with_size(mmtk: &MMTK, addr: Address, old_size: usize) { free(addr); if !addr.is_zero() { - mmtk.get().plan.base().decrease_malloc_bytes_by(old_size); + mmtk.plan.base().decrease_malloc_bytes_by(old_size); } } diff --git a/src/util/reference_processor.rs b/src/util/reference_processor.rs index ba49ec0604..7998a7bba9 100644 --- a/src/util/reference_processor.rs +++ b/src/util/reference_processor.rs @@ -66,15 +66,15 @@ impl ReferenceProcessors { /// plans, this separate step is required. pub fn forward_refs(&self, trace: &mut E, mmtk: &'static MMTK) { debug_assert!( - mmtk.get().plan.constraints().needs_forward_after_liveness, + mmtk.plan.constraints().needs_forward_after_liveness, "A plan with needs_forward_after_liveness=false does not need a separate forward step" ); self.soft - .forward::(trace, mmtk.get().plan.is_current_gc_nursery()); + .forward::(trace, mmtk.plan.is_current_gc_nursery()); self.weak - .forward::(trace, mmtk.get().plan.is_current_gc_nursery()); + .forward::(trace, mmtk.plan.is_current_gc_nursery()); self.phantom - .forward::(trace, mmtk.get().plan.is_current_gc_nursery()); + .forward::(trace, mmtk.plan.is_current_gc_nursery()); } // Methods for scanning weak references. It needs to be called in a decreasing order of reference strengths, i.e. soft > weak > phantom @@ -83,21 +83,21 @@ impl ReferenceProcessors { pub fn scan_soft_refs(&self, trace: &mut E, mmtk: &'static MMTK) { // For soft refs, it is up to the VM to decide when to reclaim this. // If this is not an emergency collection, we have no heap stress. We simply retain soft refs. - if !mmtk.get().plan.is_emergency_collection() { + if !mmtk.plan.is_emergency_collection() { // This step only retains the referents (keep the referents alive), it does not update its addresses. // We will call soft.scan() again with retain=false to update its addresses based on liveness. self.soft - .retain::(trace, mmtk.get().plan.is_current_gc_nursery()); + .retain::(trace, mmtk.plan.is_current_gc_nursery()); } // This will update the references (and the referents). self.soft - .scan::(trace, mmtk.get().plan.is_current_gc_nursery()); + .scan::(trace, mmtk.plan.is_current_gc_nursery()); } /// Scan weak references. pub fn scan_weak_refs(&self, trace: &mut E, mmtk: &'static MMTK) { self.weak - .scan::(trace, mmtk.get().plan.is_current_gc_nursery()); + .scan::(trace, mmtk.plan.is_current_gc_nursery()); } /// Scan phantom references. @@ -107,7 +107,7 @@ impl ReferenceProcessors { mmtk: &'static MMTK, ) { self.phantom - .scan::(trace, mmtk.get().plan.is_current_gc_nursery()); + .scan::(trace, mmtk.plan.is_current_gc_nursery()); } } @@ -491,7 +491,7 @@ impl GCWork for SoftRefProcessing { fn do_work(&mut self, worker: &mut GCWorker, mmtk: &'static MMTK) { let mut w = E::new(vec![], false, mmtk); w.set_worker(worker); - mmtk.get().reference_processors.scan_soft_refs(&mut w, mmtk); + mmtk.reference_processors.scan_soft_refs(&mut w, mmtk); w.flush(); } } @@ -507,7 +507,7 @@ impl GCWork for WeakRefProcessing { fn do_work(&mut self, worker: &mut GCWorker, mmtk: &'static MMTK) { let mut w = E::new(vec![], false, mmtk); w.set_worker(worker); - mmtk.get().reference_processors.scan_weak_refs(&mut w, mmtk); + mmtk.reference_processors.scan_weak_refs(&mut w, mmtk); w.flush(); } } @@ -523,9 +523,7 @@ impl GCWork for PhantomRefProcessing { fn do_work(&mut self, worker: &mut GCWorker, mmtk: &'static MMTK) { let mut w = E::new(vec![], false, mmtk); w.set_worker(worker); - mmtk.get() - .reference_processors - .scan_phantom_refs(&mut w, mmtk); + mmtk.reference_processors.scan_phantom_refs(&mut w, mmtk); w.flush(); } } @@ -541,7 +539,7 @@ impl GCWork for RefForwarding { fn do_work(&mut self, worker: &mut GCWorker, mmtk: &'static MMTK) { let mut w = E::new(vec![], false, mmtk); w.set_worker(worker); - mmtk.get().reference_processors.forward_refs(&mut w, mmtk); + mmtk.reference_processors.forward_refs(&mut w, mmtk); w.flush(); } } @@ -555,9 +553,7 @@ impl RefForwarding { pub struct RefEnqueue(PhantomData); impl GCWork for RefEnqueue { fn do_work(&mut self, worker: &mut GCWorker, mmtk: &'static MMTK) { - mmtk.get() - .reference_processors - .enqueue_refs::(worker.tls); + mmtk.reference_processors.enqueue_refs::(worker.tls); } } impl RefEnqueue { diff --git a/src/util/sanity/sanity_checker.rs b/src/util/sanity/sanity_checker.rs index 6247f845a7..78fb3130f1 100644 --- a/src/util/sanity/sanity_checker.rs +++ b/src/util/sanity/sanity_checker.rs @@ -54,7 +54,7 @@ impl ScheduleSanityGC

{ impl GCWork for ScheduleSanityGC

{ fn do_work(&mut self, worker: &mut GCWorker, mmtk: &'static MMTK) { let scheduler = worker.scheduler(); - let plan = &mmtk.get().plan; + let plan = &mmtk.plan; scheduler.reset_state(); @@ -72,7 +72,7 @@ impl GCWork for ScheduleSanityGC

{ // scheduler.work_buckets[WorkBucketStage::Prepare] // .add(ScanStackRoot::>(mutator)); // } - for roots in &mmtk.get().sanity_checker.lock().unwrap().roots { + for roots in &mmtk.sanity_checker.lock().unwrap().roots { scheduler.work_buckets[WorkBucketStage::Closure].add( SanityGCProcessEdges::::new(roots.clone(), true, mmtk), ); @@ -100,16 +100,16 @@ impl SanityPrepare

{ impl GCWork for SanityPrepare

{ fn do_work(&mut self, _worker: &mut GCWorker, mmtk: &'static MMTK) { - mmtk.get().plan.enter_sanity(); + mmtk.plan.enter_sanity(); { - let mut sanity_checker = mmtk.get().sanity_checker.lock().unwrap(); + let mut sanity_checker = mmtk.sanity_checker.lock().unwrap(); sanity_checker.refs.clear(); } for mutator in ::VMActivePlan::mutators() { - mmtk.get().scheduler.work_buckets[WorkBucketStage::Prepare] + mmtk.scheduler.work_buckets[WorkBucketStage::Prepare] .add(PrepareMutator::::new(mutator)); } - for w in &mmtk.get().scheduler.worker_group.workers_shared { + for w in &mmtk.scheduler.worker_group.workers_shared { let result = w.designated_work.push(Box::new(PrepareCollector)); debug_assert!(result.is_ok()); } @@ -128,17 +128,13 @@ impl SanityRelease

{ impl GCWork for SanityRelease

{ fn do_work(&mut self, _worker: &mut GCWorker, mmtk: &'static MMTK) { - mmtk.get().plan.leave_sanity(); - mmtk.get() - .sanity_checker - .lock() - .unwrap() - .clear_roots_cache(); + mmtk.plan.leave_sanity(); + mmtk.sanity_checker.lock().unwrap().clear_roots_cache(); for mutator in ::VMActivePlan::mutators() { - mmtk.get().scheduler.work_buckets[WorkBucketStage::Release] + mmtk.scheduler.work_buckets[WorkBucketStage::Release] .add(ReleaseMutator::::new(mutator)); } - for w in &mmtk.get().scheduler.worker_group.workers_shared { + for w in &mmtk.scheduler.worker_group.workers_shared { let result = w.designated_work.push(Box::new(ReleaseCollector)); debug_assert!(result.is_ok()); } @@ -179,7 +175,7 @@ impl ProcessEdgesWork for SanityGCProcessEdges { if object.is_null() { return object; } - let mut sanity_checker = self.mmtk().get().sanity_checker.lock().unwrap(); + let mut sanity_checker = self.mmtk().sanity_checker.lock().unwrap(); if !sanity_checker.refs.contains(&object) { // FIXME steveb consider VM-specific integrity check on reference. assert!(object.is_sane(), "Invalid reference {:?}", object); diff --git a/src/util/statistics/stats.rs b/src/util/statistics/stats.rs index 44ec532e8f..f89ec538e4 100644 --- a/src/util/statistics/stats.rs +++ b/src/util/statistics/stats.rs @@ -187,7 +187,7 @@ impl Stats { println!( "============================ MMTk Statistics Totals ============================" ); - let scheduler_stat = mmtk.get().scheduler.statistics(); + let scheduler_stat = mmtk.scheduler.statistics(); self.print_column_names(&scheduler_stat); print!("{}\t", self.get_phase() / 2); let counter = self.counters.lock().unwrap(); diff --git a/vmbindings/dummyvm/src/api.rs b/vmbindings/dummyvm/src/api.rs index 9bfbe99985..236975fe47 100644 --- a/vmbindings/dummyvm/src/api.rs +++ b/vmbindings/dummyvm/src/api.rs @@ -2,6 +2,7 @@ #![allow(clippy::not_unsafe_ptr_arg_deref)] use libc::c_char; +use std::sync::atomic::Ordering; use std::ffi::CStr; use mmtk::memory_manager; use mmtk::AllocationSemantics; @@ -9,22 +10,19 @@ use mmtk::util::{ObjectReference, Address}; use mmtk::util::opaque_pointer::*; use mmtk::scheduler::{GCController, GCWorker}; use mmtk::Mutator; -use mmtk::MMTK; use crate::DummyVM; use crate::SINGLETON; +use crate::BUILDER; #[no_mangle] pub extern "C" fn mmtk_gc_init() { - // # Safety - // Casting `SINGLETON` as mutable is safe because `gc_init` will only be executed once by a single thread during startup. - #[allow(clippy::cast_ref_to_mut)] - let singleton_mut = unsafe { &mut *(&*SINGLETON as *const MMTK as *mut MMTK) }; - memory_manager::gc_init(singleton_mut) + assert!(!crate::MMTK_INITIALIZED.load(Ordering::Relaxed)); + lazy_static::initialize(&SINGLETON); } #[no_mangle] pub extern "C" fn mmtk_set_heap_size(size: usize) { - memory_manager::process(&SINGLETON, "heap_size", size.to_string().as_str()); + memory_manager::process(&BUILDER, "heap_size", size.to_string().as_str()); } #[no_mangle] @@ -160,7 +158,7 @@ pub extern "C" fn mmtk_harness_end() { pub extern "C" fn mmtk_process(name: *const c_char, value: *const c_char) -> bool { let name_str: &CStr = unsafe { CStr::from_ptr(name) }; let value_str: &CStr = unsafe { CStr::from_ptr(value) }; - memory_manager::process(&SINGLETON, name_str.to_str().unwrap(), value_str.to_str().unwrap()) + memory_manager::process(&BUILDER, name_str.to_str().unwrap(), value_str.to_str().unwrap()) } #[no_mangle] diff --git a/vmbindings/dummyvm/src/lib.rs b/vmbindings/dummyvm/src/lib.rs index c5d5a8a827..e2eac91f36 100644 --- a/vmbindings/dummyvm/src/lib.rs +++ b/vmbindings/dummyvm/src/lib.rs @@ -5,6 +5,7 @@ extern crate lazy_static; use mmtk::vm::VMBinding; use mmtk::MMTK; +use mmtk::MMTKBuilder; pub mod scanning; pub mod collection; @@ -26,14 +27,24 @@ impl VMBinding for DummyVM { type VMActivePlan = active_plan::VMActivePlan; type VMReferenceGlue = reference_glue::VMReferenceGlue; - /// Allowed maximum alignment as shift by min alignment. + /// Allowed maximum alignment as shift by min alignment. const MAX_ALIGNMENT_SHIFT: usize = 6_usize - Self::LOG_MIN_ALIGNMENT as usize; /// Allowed maximum alignment in bytes. const MAX_ALIGNMENT: usize = Self::MIN_ALIGNMENT << Self::MAX_ALIGNMENT_SHIFT; } -//#[cfg(feature = "dummyvm")] +use std::sync::atomic::{AtomicBool, Ordering}; + +/// This is used to ensure we initialize MMTk at a specified timing. +pub static MMTK_INITIALIZED: AtomicBool = AtomicBool::new(false); + lazy_static! { - pub static ref SINGLETON: MMTK = MMTK::new(); + pub static ref BUILDER: MMTKBuilder = MMTKBuilder::new(); + pub static ref SINGLETON: MMTK = { + debug_assert!(!MMTK_INITIALIZED.load(Ordering::Relaxed)); + let ret = mmtk::memory_manager::gc_init(&BUILDER); + MMTK_INITIALIZED.store(true, std::sync::atomic::Ordering::Relaxed); + *ret + }; } From 609c1ae0e61f8fec689cb0d32096a8e268e6c480 Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Tue, 19 Jul 2022 11:38:25 +1000 Subject: [PATCH 07/14] Update examples/mmtk.h --- examples/mmtk.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/examples/mmtk.h b/examples/mmtk.h index a7ddc02253..b930c257bf 100644 --- a/examples/mmtk.h +++ b/examples/mmtk.h @@ -16,10 +16,11 @@ extern "C" { #endif typedef void* MMTk_Mutator; +typedef void* MMTk_Builder; +typedef void* MMTk; // Initialize an MMTk instance -extern void mmtk_gc_init(); -extern void mmtk_set_heap_size(size_t size); +extern MMTk mmtk_gc_init(MMTK_Builder builder); // Request MMTk to create a new mutator for the given `tls` thread extern MMTk_Mutator mmtk_bind_mutator(void* tls); @@ -75,10 +76,10 @@ extern void mmtk_modify_check(void* ref); extern bool mmtk_will_never_move(void* object); // Process an MMTk option. Return true if option was processed successfully -extern bool mmtk_process(char* name, char* value); +extern bool mmtk_process(MMTk_Builder builder, char* name, char* value); // Process MMTk options. Return true if all options were processed successfully -extern bool mmtk_process_bulk(char* options); +extern bool mmtk_process_bulk(MMTk_Builder builder, char* options); // Sanity only. Scan heap for discrepancies and errors extern void mmtk_scan_region(); From c8f3ce04f123b8c71379be186d6178910ece0164 Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Tue, 19 Jul 2022 11:58:30 +1000 Subject: [PATCH 08/14] Move examples/mmtk.h to docs/ --- {examples => docs/header}/mmtk.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename {examples => docs/header}/mmtk.h (98%) diff --git a/examples/mmtk.h b/docs/header/mmtk.h similarity index 98% rename from examples/mmtk.h rename to docs/header/mmtk.h index b930c257bf..8af54aadcf 100644 --- a/examples/mmtk.h +++ b/docs/header/mmtk.h @@ -20,7 +20,7 @@ typedef void* MMTk_Builder; typedef void* MMTk; // Initialize an MMTk instance -extern MMTk mmtk_gc_init(MMTK_Builder builder); +extern MMTk mmtk_gc_init(MMTk_Builder builder); // Request MMTk to create a new mutator for the given `tls` thread extern MMTk_Mutator mmtk_bind_mutator(void* tls); From 1490f657770491bebba48ab50fc3596a62ef1f43 Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Tue, 19 Jul 2022 12:43:16 +1000 Subject: [PATCH 09/14] builder in process() does not need to be static --- src/memory_manager.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/memory_manager.rs b/src/memory_manager.rs index a81475dfc6..3b69319ec5 100644 --- a/src/memory_manager.rs +++ b/src/memory_manager.rs @@ -341,7 +341,7 @@ pub fn disable_collection(mmtk: &'static MMTK) { /// * `mmtk`: A reference to an MMTk instance. /// * `name`: The name of the option. /// * `value`: The value of the option (as a string). -pub fn process(builder: &'static MMTKBuilder, name: &str, value: &str) -> bool { +pub fn process(builder: &MMTKBuilder, name: &str, value: &str) -> bool { unsafe { builder.set_option(name, value) } } @@ -351,7 +351,7 @@ pub fn process(builder: &'static MMTKBuilder, name: &str, value: &str) -> bool { /// Arguments: /// * `mmtk`: A reference to an MMTk instance. /// * `options`: a string that is key value pairs separated by white spaces, e.g. "threads=1 stress_factor=4096" -pub fn process_bulk(builder: &'static MMTKBuilder, options: &str) -> bool { +pub fn process_bulk(builder: &MMTKBuilder, options: &str) -> bool { unsafe { builder.set_options_bulk_by_str(options) } } From 6b538d3e9ce7c6ed89628f19ee55bd690415c70f Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Wed, 20 Jul 2022 11:39:01 +1000 Subject: [PATCH 10/14] Remove UnsafeOptionsWrapper. Allow options to be set directly. --- docs/tutorial/code/mygc_semispace/global.rs | 4 +- src/memory_manager.rs | 8 +- src/mmtk.rs | 31 +-- src/plan/generational/copying/global.rs | 8 +- src/plan/generational/global.rs | 4 +- src/plan/generational/immix/global.rs | 4 +- src/plan/global.rs | 10 +- src/plan/immix/global.rs | 4 +- src/plan/markcompact/global.rs | 8 +- src/plan/marksweep/global.rs | 8 +- src/plan/nogc/global.rs | 8 +- src/plan/pageprotect/global.rs | 8 +- src/plan/semispace/global.rs | 8 +- src/util/options.rs | 285 ++++++++++---------- vmbindings/dummyvm/src/api.rs | 6 +- vmbindings/dummyvm/src/lib.rs | 6 +- 16 files changed, 197 insertions(+), 213 deletions(-) diff --git a/docs/tutorial/code/mygc_semispace/global.rs b/docs/tutorial/code/mygc_semispace/global.rs index b7d6233cfb..b1efc01c4e 100644 --- a/docs/tutorial/code/mygc_semispace/global.rs +++ b/docs/tutorial/code/mygc_semispace/global.rs @@ -18,7 +18,7 @@ use crate::util::heap::layout::vm_layout_constants::{HEAP_END, HEAP_START}; use crate::util::heap::HeapMeta; use crate::util::heap::VMRequest; use crate::util::metadata::side_metadata::{SideMetadataSanity, SideMetadataContext}; -use crate::util::options::UnsafeOptionsWrapper; +use crate::util::options::Options; use crate::util::opaque_pointer::*; use crate::vm::VMBinding; use enum_map::EnumMap; @@ -179,7 +179,7 @@ impl MyGC { fn new( vm_map: &'static VMMap, mmapper: &'static Mmapper, - options: Arc, + options: Arc, ) -> Self { // Modify let mut heap = HeapMeta::new(HEAP_START, HEAP_END); diff --git a/src/memory_manager.rs b/src/memory_manager.rs index 3b69319ec5..e709b16851 100644 --- a/src/memory_manager.rs +++ b/src/memory_manager.rs @@ -341,8 +341,8 @@ pub fn disable_collection(mmtk: &'static MMTK) { /// * `mmtk`: A reference to an MMTk instance. /// * `name`: The name of the option. /// * `value`: The value of the option (as a string). -pub fn process(builder: &MMTKBuilder, name: &str, value: &str) -> bool { - unsafe { builder.set_option(name, value) } +pub fn process(builder: &mut MMTKBuilder, name: &str, value: &str) -> bool { + builder.set_option(name, value) } /// Process multiple MMTk run-time options. Returns true if all the options are processed successfully. @@ -351,8 +351,8 @@ pub fn process(builder: &MMTKBuilder, name: &str, value: &str) -> bool { /// Arguments: /// * `mmtk`: A reference to an MMTk instance. /// * `options`: a string that is key value pairs separated by white spaces, e.g. "threads=1 stress_factor=4096" -pub fn process_bulk(builder: &MMTKBuilder, options: &str) -> bool { - unsafe { builder.set_options_bulk_by_str(options) } +pub fn process_bulk(builder: &mut MMTKBuilder, options: &str) -> bool { + builder.set_options_bulk_by_str(options) } /// Return used memory in bytes. diff --git a/src/mmtk.rs b/src/mmtk.rs index 4ce5703175..9969539823 100644 --- a/src/mmtk.rs +++ b/src/mmtk.rs @@ -7,7 +7,7 @@ use crate::util::heap::layout::heap_layout::Mmapper; use crate::util::heap::layout::heap_layout::VMMap; use crate::util::heap::layout::map::Map; use crate::util::opaque_pointer::*; -use crate::util::options::{Options, UnsafeOptionsWrapper}; +use crate::util::options::Options; use crate::util::reference_processor::ReferenceProcessors; #[cfg(feature = "sanity")] use crate::util::sanity::sanity_checker::SanityChecker; @@ -42,38 +42,31 @@ pub static SFT_MAP: InitializeOnce> = InitializeOnce::new(); // MMTk builder. This is used to set options before actually creating an MMTk instance. pub struct MMTKBuilder { /// The options for this instance. - options: Arc, + pub options: Options, } impl MMTKBuilder { /// Create an MMTK builder with default options pub fn new() -> Self { - let options = Arc::new(UnsafeOptionsWrapper::new(Options::default())); - MMTKBuilder { options } + MMTKBuilder { + options: Options::default(), + } } /// Set an option. - /// - /// # Safety - /// This method is not thread safe, as internally it acquires a mutable reference to self. - /// It is supposed to be used by one thread during boot time. - pub unsafe fn set_option(&self, name: &str, val: &str) -> bool { - self.options.process(name, val) + pub fn set_option(&mut self, name: &str, val: &str) -> bool { + self.options.set_from_command_line(name, val) } /// Set multiple options by a string. The string should be key-value pairs separated by white spaces, /// such as `threads=1 stress_factor=4096`. - /// - /// # Safety - /// This method is not thread safe, as internally it acquires a mutable reference to self. - /// It is supposed to be used by one thread during boot time. - pub unsafe fn set_options_bulk_by_str(&self, options: &str) -> bool { - self.options.process_bulk(options) + pub fn set_options_bulk_by_str(&mut self, options: &str) -> bool { + self.options.set_bulk_from_command_line(options) } /// Build an MMTk instance from the builder. pub fn build(&self) -> MMTK { - let mut mmtk = MMTK::new(self.options.clone()); + let mut mmtk = MMTK::new(Arc::new(self.options.clone())); let heap_size = *self.options.heap_size; // TODO: We should remove Plan.gc_init(). We create plan in `MMTKInner::new()`, and we @@ -93,7 +86,7 @@ impl Default for MMTKBuilder { /// An MMTk instance. MMTk allows multiple instances to run independently, and each instance gives users a separate heap. /// *Note that multi-instances is not fully supported yet* pub struct MMTK { - pub(crate) options: Arc, + pub(crate) options: Arc, pub(crate) plan: Box>, pub(crate) reference_processors: ReferenceProcessors, pub(crate) finalizable_processor: @@ -105,7 +98,7 @@ pub struct MMTK { } impl MMTK { - pub fn new(options: Arc) -> Self { + pub fn new(options: Arc) -> Self { // Initialize SFT first in case we need to use this in the constructor. // The first call will initialize SFT map. Other calls will be blocked until SFT map is initialized. SFT_MAP.initialize_once(&SFTMap::new); diff --git a/src/plan/generational/copying/global.rs b/src/plan/generational/copying/global.rs index d53fc97580..32a9c27d81 100644 --- a/src/plan/generational/copying/global.rs +++ b/src/plan/generational/copying/global.rs @@ -19,7 +19,7 @@ use crate::util::heap::layout::vm_layout_constants::{HEAP_END, HEAP_START}; use crate::util::heap::HeapMeta; use crate::util::heap::VMRequest; use crate::util::metadata::side_metadata::SideMetadataSanity; -use crate::util::options::UnsafeOptionsWrapper; +use crate::util::options::Options; use crate::util::VMWorkerThread; use crate::vm::*; use enum_map::EnumMap; @@ -166,11 +166,7 @@ impl Plan for GenCopy { } impl GenCopy { - pub fn new( - vm_map: &'static VMMap, - mmapper: &'static Mmapper, - options: Arc, - ) -> Self { + pub fn new(vm_map: &'static VMMap, mmapper: &'static Mmapper, options: Arc) -> Self { let mut heap = HeapMeta::new(HEAP_START, HEAP_END); // We have no specific side metadata for copying. So just use the ones from generational. let global_metadata_specs = diff --git a/src/plan/generational/global.rs b/src/plan/generational/global.rs index 6ff2657c84..7e9b9db933 100644 --- a/src/plan/generational/global.rs +++ b/src/plan/generational/global.rs @@ -13,7 +13,7 @@ use crate::util::heap::HeapMeta; use crate::util::heap::VMRequest; use crate::util::metadata::side_metadata::SideMetadataSanity; use crate::util::metadata::side_metadata::SideMetadataSpec; -use crate::util::options::UnsafeOptionsWrapper; +use crate::util::options::Options; use crate::util::ObjectReference; use crate::util::VMWorkerThread; use crate::vm::VMBinding; @@ -46,7 +46,7 @@ impl Gen { constraints: &'static PlanConstraints, vm_map: &'static VMMap, mmapper: &'static Mmapper, - options: Arc, + options: Arc, ) -> Self { Gen { nursery: CopySpace::new( diff --git a/src/plan/generational/immix/global.rs b/src/plan/generational/immix/global.rs index 8e26cbf15a..a3cebd410c 100644 --- a/src/plan/generational/immix/global.rs +++ b/src/plan/generational/immix/global.rs @@ -17,7 +17,7 @@ use crate::util::heap::layout::heap_layout::Mmapper; use crate::util::heap::layout::heap_layout::VMMap; use crate::util::heap::layout::vm_layout_constants::{HEAP_END, HEAP_START}; use crate::util::heap::HeapMeta; -use crate::util::options::UnsafeOptionsWrapper; +use crate::util::options::Options; use crate::util::VMWorkerThread; use crate::vm::*; @@ -207,7 +207,7 @@ impl GenImmix { pub fn new( vm_map: &'static VMMap, mmapper: &'static Mmapper, - options: Arc, + options: Arc, scheduler: Arc>, ) -> Self { let mut heap = HeapMeta::new(HEAP_START, HEAP_END); diff --git a/src/plan/global.rs b/src/plan/global.rs index 871c2c7472..ae3b28c284 100644 --- a/src/plan/global.rs +++ b/src/plan/global.rs @@ -22,8 +22,8 @@ use crate::util::heap::HeapMeta; use crate::util::heap::VMRequest; use crate::util::metadata::side_metadata::SideMetadataSanity; use crate::util::metadata::side_metadata::SideMetadataSpec; +use crate::util::options::Options; use crate::util::options::PlanSelector; -use crate::util::options::{Options, UnsafeOptionsWrapper}; use crate::util::statistics::stats::Stats; use crate::util::ObjectReference; use crate::util::{VMMutatorThread, VMWorkerThread}; @@ -67,7 +67,7 @@ pub fn create_plan( plan: PlanSelector, vm_map: &'static VMMap, mmapper: &'static Mmapper, - options: Arc, + options: Arc, scheduler: Arc>, ) -> Box> { match plan { @@ -374,7 +374,7 @@ pub struct BasePlan { pub stats: Stats, mmapper: &'static Mmapper, pub vm_map: &'static VMMap, - pub options: Arc, + pub options: Arc, pub heap: HeapMeta, #[cfg(feature = "sanity")] pub inside_sanity: AtomicBool, @@ -456,7 +456,7 @@ impl BasePlan { pub fn new( vm_map: &'static VMMap, mmapper: &'static Mmapper, - options: Arc, + options: Arc, mut heap: HeapMeta, constraints: &'static PlanConstraints, global_side_metadata_specs: Vec, @@ -889,7 +889,7 @@ impl CommonPlan { pub fn new( vm_map: &'static VMMap, mmapper: &'static Mmapper, - options: Arc, + options: Arc, mut heap: HeapMeta, constraints: &'static PlanConstraints, global_side_metadata_specs: Vec, diff --git a/src/plan/immix/global.rs b/src/plan/immix/global.rs index c910d097e8..256de448ec 100644 --- a/src/plan/immix/global.rs +++ b/src/plan/immix/global.rs @@ -17,7 +17,7 @@ use crate::util::heap::layout::vm_layout_constants::{HEAP_END, HEAP_START}; use crate::util::heap::HeapMeta; use crate::util::metadata::side_metadata::SideMetadataContext; use crate::util::metadata::side_metadata::SideMetadataSanity; -use crate::util::options::UnsafeOptionsWrapper; +use crate::util::options::Options; use crate::vm::VMBinding; use crate::{policy::immix::ImmixSpace, util::opaque_pointer::VMWorkerThread}; use std::sync::atomic::AtomicBool; @@ -137,7 +137,7 @@ impl Immix { pub fn new( vm_map: &'static VMMap, mmapper: &'static Mmapper, - options: Arc, + options: Arc, scheduler: Arc>, ) -> Self { let mut heap = HeapMeta::new(HEAP_START, HEAP_END); diff --git a/src/plan/markcompact/global.rs b/src/plan/markcompact/global.rs index 5650c07494..728eca768d 100644 --- a/src/plan/markcompact/global.rs +++ b/src/plan/markcompact/global.rs @@ -25,7 +25,7 @@ use crate::util::heap::HeapMeta; use crate::util::heap::VMRequest; use crate::util::metadata::side_metadata::{SideMetadataContext, SideMetadataSanity}; use crate::util::opaque_pointer::*; -use crate::util::options::UnsafeOptionsWrapper; +use crate::util::options::Options; use crate::vm::VMBinding; use enum_map::EnumMap; @@ -177,11 +177,7 @@ impl Plan for MarkCompact { } impl MarkCompact { - pub fn new( - vm_map: &'static VMMap, - mmapper: &'static Mmapper, - options: Arc, - ) -> Self { + pub fn new(vm_map: &'static VMMap, mmapper: &'static Mmapper, options: Arc) -> Self { let mut heap = HeapMeta::new(HEAP_START, HEAP_END); // if global_alloc_bit is enabled, ALLOC_SIDE_METADATA_SPEC will be added to // SideMetadataContext by default, so we don't need to add it here. diff --git a/src/plan/marksweep/global.rs b/src/plan/marksweep/global.rs index 963702b823..c1207b204d 100644 --- a/src/plan/marksweep/global.rs +++ b/src/plan/marksweep/global.rs @@ -18,7 +18,7 @@ use crate::util::heap::layout::heap_layout::VMMap; use crate::util::heap::layout::vm_layout_constants::{HEAP_END, HEAP_START}; use crate::util::heap::HeapMeta; use crate::util::metadata::side_metadata::{SideMetadataContext, SideMetadataSanity}; -use crate::util::options::UnsafeOptionsWrapper; +use crate::util::options::Options; use crate::util::VMWorkerThread; use crate::vm::VMBinding; use std::sync::Arc; @@ -94,11 +94,7 @@ impl Plan for MarkSweep { } impl MarkSweep { - pub fn new( - vm_map: &'static VMMap, - mmapper: &'static Mmapper, - options: Arc, - ) -> Self { + pub fn new(vm_map: &'static VMMap, mmapper: &'static Mmapper, options: Arc) -> Self { let heap = HeapMeta::new(HEAP_START, HEAP_END); // if global_alloc_bit is enabled, ALLOC_SIDE_METADATA_SPEC will be added to // SideMetadataContext by default, so we don't need to add it here. diff --git a/src/plan/nogc/global.rs b/src/plan/nogc/global.rs index def42bb34e..79f7696f35 100644 --- a/src/plan/nogc/global.rs +++ b/src/plan/nogc/global.rs @@ -15,7 +15,7 @@ use crate::util::heap::HeapMeta; use crate::util::heap::VMRequest; use crate::util::metadata::side_metadata::{SideMetadataContext, SideMetadataSanity}; use crate::util::opaque_pointer::*; -use crate::util::options::UnsafeOptionsWrapper; +use crate::util::options::Options; use crate::vm::VMBinding; use enum_map::EnumMap; use std::sync::Arc; @@ -85,11 +85,7 @@ impl Plan for NoGC { } impl NoGC { - pub fn new( - vm_map: &'static VMMap, - mmapper: &'static Mmapper, - options: Arc, - ) -> Self { + pub fn new(vm_map: &'static VMMap, mmapper: &'static Mmapper, options: Arc) -> Self { #[cfg(not(feature = "nogc_lock_free"))] let mut heap = HeapMeta::new(HEAP_START, HEAP_END); #[cfg(feature = "nogc_lock_free")] diff --git a/src/plan/pageprotect/global.rs b/src/plan/pageprotect/global.rs index 1da5c53dfe..9fdfa1b3bb 100644 --- a/src/plan/pageprotect/global.rs +++ b/src/plan/pageprotect/global.rs @@ -13,7 +13,7 @@ use crate::util::heap::layout::vm_layout_constants::{HEAP_END, HEAP_START}; use crate::util::heap::HeapMeta; use crate::util::heap::VMRequest; use crate::util::metadata::side_metadata::SideMetadataContext; -use crate::util::options::UnsafeOptionsWrapper; +use crate::util::options::Options; use crate::{plan::global::BasePlan, vm::VMBinding}; use crate::{ plan::global::CommonPlan, policy::largeobjectspace::LargeObjectSpace, @@ -97,11 +97,7 @@ impl Plan for PageProtect { } impl PageProtect { - pub fn new( - vm_map: &'static VMMap, - mmapper: &'static Mmapper, - options: Arc, - ) -> Self { + pub fn new(vm_map: &'static VMMap, mmapper: &'static Mmapper, options: Arc) -> Self { let mut heap = HeapMeta::new(HEAP_START, HEAP_END); let global_metadata_specs = SideMetadataContext::new_global_specs(&[]); diff --git a/src/plan/semispace/global.rs b/src/plan/semispace/global.rs index 526a8b4543..8197b39a74 100644 --- a/src/plan/semispace/global.rs +++ b/src/plan/semispace/global.rs @@ -17,7 +17,7 @@ use crate::util::heap::HeapMeta; use crate::util::heap::VMRequest; use crate::util::metadata::side_metadata::{SideMetadataContext, SideMetadataSanity}; use crate::util::opaque_pointer::VMWorkerThread; -use crate::util::options::UnsafeOptionsWrapper; +use crate::util::options::Options; use crate::{plan::global::BasePlan, vm::VMBinding}; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; @@ -132,11 +132,7 @@ impl Plan for SemiSpace { } impl SemiSpace { - pub fn new( - vm_map: &'static VMMap, - mmapper: &'static Mmapper, - options: Arc, - ) -> Self { + pub fn new(vm_map: &'static VMMap, mmapper: &'static Mmapper, options: Arc) -> Self { let mut heap = HeapMeta::new(HEAP_START, HEAP_END); let global_metadata_specs = SideMetadataContext::new_global_specs(&[]); diff --git a/src/util/options.rs b/src/util/options.rs index 0c10390797..5d0cf1349b 100644 --- a/src/util/options.rs +++ b/src/util/options.rs @@ -1,8 +1,7 @@ use crate::util::constants::DEFAULT_STRESS_FACTOR; use crate::util::constants::LOG_BYTES_IN_MBYTE; -use std::cell::UnsafeCell; use std::default::Default; -use std::ops::Deref; +use std::fmt::Debug; use std::str::FromStr; use strum_macros::EnumString; @@ -79,119 +78,6 @@ pub const DEFAULT_MIN_NURSERY: usize = 32 << LOG_BYTES_IN_MBYTE; /// This does not affect the actual space we create as nursery. It is only used in GC trigger check. pub const DEFAULT_MAX_NURSERY: usize = 32 << LOG_BYTES_IN_MBYTE; -pub struct UnsafeOptionsWrapper(UnsafeCell); - -// TODO: We should carefully examine the unsync with UnsafeCell. We should be able to provide a safe implementation. -unsafe impl Sync for UnsafeOptionsWrapper {} - -impl UnsafeOptionsWrapper { - pub const fn new(o: Options) -> UnsafeOptionsWrapper { - UnsafeOptionsWrapper(UnsafeCell::new(o)) - } - - /// Process option. Returns true if the key and the value are both valid. - /// - /// Arguments: - /// * `name`: the name of the option. See `options!` for all the valid options. - /// * `value`: the value of the option in string format. - /// - /// # Safety - /// This method is not thread safe, as internally it acquires a mutable reference to self. - /// It is supposed to be used by one thread during boot time. - pub unsafe fn process(&self, name: &str, value: &str) -> bool { - (*self.0.get()).set_from_command_line(name, value) - } - - /// Bulk process options. Returns true if all the options are processed successfully. - /// This method returns false if the option string is invalid, or if it includes any invalid option. - /// - /// Arguments: - /// * `options`: a string that is key value pairs separated by white spaces, e.g. "threads=1 stress_factor=4096" - /// - /// # Safety - /// This method is not thread safe, as internally it acquires a mutable reference to self. - /// It is supposed to be used by one thread during boot time. - pub unsafe fn process_bulk(&self, options: &str) -> bool { - for opt in options.split_ascii_whitespace() { - let kv_pair: Vec<&str> = opt.split('=').collect(); - if kv_pair.len() != 2 { - return false; - } - - let key = kv_pair[0]; - let val = kv_pair[1]; - if !self.process(key, val) { - return false; - } - } - - true - } -} -impl Deref for UnsafeOptionsWrapper { - type Target = Options; - fn deref(&self) -> &Options { - unsafe { &*self.0.get() } - } -} - -#[cfg(test)] -mod process_tests { - use super::*; - use crate::util::options::Options; - use crate::util::test_util::serial_test; - - #[test] - fn test_process_valid() { - serial_test(|| { - let options = UnsafeOptionsWrapper::new(Options::default()); - let success = unsafe { options.process("no_finalizer", "true") }; - assert!(success); - assert!(*options.no_finalizer); - }) - } - - #[test] - fn test_process_invalid() { - serial_test(|| { - let options = UnsafeOptionsWrapper::new(Options::default()); - let default_no_finalizer = *options.no_finalizer; - let success = unsafe { options.process("no_finalizer", "100") }; - assert!(!success); - assert_eq!(*options.no_finalizer, default_no_finalizer); - }) - } - - #[test] - fn test_process_bulk_empty() { - serial_test(|| { - let options = UnsafeOptionsWrapper::new(Options::default()); - let success = unsafe { options.process_bulk("") }; - assert!(success); - }) - } - - #[test] - fn test_process_bulk_valid() { - serial_test(|| { - let options = UnsafeOptionsWrapper::new(Options::default()); - let success = unsafe { options.process_bulk("no_finalizer=true stress_factor=42") }; - assert!(success); - assert!(*options.no_finalizer); - assert_eq!(*options.stress_factor, 42); - }) - } - - #[test] - fn test_process_bulk_invalid() { - serial_test(|| { - let options = UnsafeOptionsWrapper::new(Options::default()); - let success = unsafe { options.process_bulk("no_finalizer=true stress_factor=a") }; - assert!(!success); - }) - } -} - fn always_valid(_: &T) -> bool { true } @@ -199,18 +85,58 @@ fn always_valid(_: &T) -> bool { /// An MMTk option of a given type. /// This type allows us to store some metadata for the option. To get the value of an option, /// you can simply dereference it (for example, *options.threads). -#[derive(Debug, Clone)] -pub struct MMTKOption { - pub value: T, - +#[derive(Clone)] +pub struct MMTKOption { + /// The actual value for the option + value: T, + /// The validator to ensure the value is valid. + validator: fn(&T) -> bool, /// Can we set this option through env vars? - pub from_env_var: bool, + from_env_var: bool, /// Can we set this option through command line options/API? - pub from_command_line: bool, + from_command_line: bool, +} + +impl MMTKOption { + /// Create a new MMTKOption + pub fn new( + value: T, + validator: fn(&T) -> bool, + from_env_var: bool, + from_command_line: bool, + ) -> Self { + // FIXME: We should enable the following check to make sure the initial value is valid. + // However, we cannot enable it now. For options like perf events, the validator checks + // if the perf event feature is enabled. So when the perf event features are not enabled, + // the validator will fail whatever value we try to set (including the initial value). + // Ideally, we conditionally compile options based on the feature. But options! marcro + // does not allow attributes in it, so we cannot conditionally compile options. + // let is_valid = validator(&value); + // assert!( + // is_valid, + // "Unable to create MMTKOption: initial value {:?} is invalid", + // value + // ); + MMTKOption { + value, + validator, + from_env_var, + from_command_line, + } + } + + /// Set the option to the given value. Returns true if the value is valid, and we set the option to the value. + pub fn set(&mut self, value: T) -> bool { + if (self.validator)(&value) { + self.value = value; + return true; + } + false + } } // Dereference an option to get its value. -impl std::ops::Deref for MMTKOption { +impl std::ops::Deref for MMTKOption { type Target = T; fn deref(&self) -> &Self::Target { @@ -231,6 +157,7 @@ macro_rules! options { options!($($name: $type[env_var: $env_var, command_line: $command_line, mutable: $mutable][$validator] = $default),*); ]; ($($name:ident: $type:ty[env_var: $env_var:expr, command_line: $command_line:expr][$validator:expr] = $default:expr),*) => [ + #[derive(Clone)] pub struct Options { $(pub $name: MMTKOption<$type>),* } @@ -247,21 +174,38 @@ macro_rules! options { self.set_inner(s, val) } + /// Bulk process options. Returns true if all the options are processed successfully. + /// This method returns false if the option string is invalid, or if it includes any invalid option. + /// + /// Arguments: + /// * `options`: a string that is key value pairs separated by white spaces, e.g. "threads=1 stress_factor=4096" + pub fn set_bulk_from_command_line(&mut self, options: &str) -> bool { + for opt in options.split_ascii_whitespace() { + let kv_pair: Vec<&str> = opt.split('=').collect(); + if kv_pair.len() != 2 { + return false; + } + + let key = kv_pair[0]; + let val = kv_pair[1]; + if !self.set_from_command_line(key, val) { + return false; + } + } + + true + } + /// Set an option and run its validator for its value. fn set_inner(&mut self, s: &str, val: &str)->bool { match s { // Parse the given value from str (by env vars or by calling process()) to the right type - $(stringify!($name) => if let Ok(ref val) = val.parse::<$type>() { - // Validate - let validate_fn = $validator; - let is_valid = validate_fn(val); - if is_valid { - // Only set value if valid. - self.$name.value = val.clone(); - } else { + $(stringify!($name) => if let Ok(typed_val) = val.parse::<$type>() { + let is_set = self.$name.set(typed_val); + if !is_set { eprintln!("Warn: unable to set {}={:?}. Invalid value. Default value will be used.", s, val); } - is_valid + is_set } else { eprintln!("Warn: unable to set {}={:?}. Cant parse value. Default value will be used.", s, val); false @@ -273,11 +217,7 @@ macro_rules! options { impl Default for Options { fn default() -> Self { let mut options = Options { - $($name: MMTKOption { - value: $default, - from_env_var: $env_var, - from_command_line: $command_line, - }),* + $($name: MMTKOption::new($default, $validator, $env_var,$command_line)),* }; // If we have env vars that start with MMTK_ and match any option (such as MMTK_STRESS_FACTOR), @@ -300,7 +240,7 @@ macro_rules! options { } // Currently we allow all the options to be set by env var for the sake of convenience. -// At some point, we may disallow this and most options can only be set by command line. +// At some point, we may disallow this and all the options can only be set by command line. options! { // The plan to use. plan: PlanSelector [env_var: true, command_line: true] [always_valid] = PlanSelector::NoGC, @@ -532,4 +472,75 @@ mod tests { ) }) } + + #[test] + fn test_process_valid() { + serial_test(|| { + let mut options = Options::default(); + let success = options.set_from_command_line("no_finalizer", "true"); + assert!(success); + assert!(*options.no_finalizer); + }) + } + + #[test] + fn test_process_invalid() { + serial_test(|| { + let mut options = Options::default(); + let default_no_finalizer = *options.no_finalizer; + let success = options.set_from_command_line("no_finalizer", "100"); + assert!(!success); + assert_eq!(*options.no_finalizer, default_no_finalizer); + }) + } + + #[test] + fn test_process_bulk_empty() { + serial_test(|| { + let mut options = Options::default(); + let success = options.set_bulk_from_command_line(""); + assert!(success); + }) + } + + #[test] + fn test_process_bulk_valid() { + serial_test(|| { + let mut options = Options::default(); + let success = options.set_bulk_from_command_line("no_finalizer=true stress_factor=42"); + assert!(success); + assert!(*options.no_finalizer); + assert_eq!(*options.stress_factor, 42); + }) + } + + #[test] + fn test_process_bulk_invalid() { + serial_test(|| { + let mut options = Options::default(); + let success = options.set_bulk_from_command_line("no_finalizer=true stress_factor=a"); + assert!(!success); + }) + } + + #[test] + fn test_set_typed_option_valid() { + serial_test(|| { + let mut options = Options::default(); + let success = options.no_finalizer.set(true); + assert!(success); + assert!(*options.no_finalizer); + }) + } + + #[test] + fn test_set_typed_option_invalid() { + serial_test(|| { + let mut options = Options::default(); + let threads = *options.threads; + let success = options.threads.set(0); + assert!(!success); + assert_eq!(*options.threads, threads); + }) + } } diff --git a/vmbindings/dummyvm/src/api.rs b/vmbindings/dummyvm/src/api.rs index 236975fe47..c3709ad72f 100644 --- a/vmbindings/dummyvm/src/api.rs +++ b/vmbindings/dummyvm/src/api.rs @@ -22,7 +22,8 @@ pub extern "C" fn mmtk_gc_init() { #[no_mangle] pub extern "C" fn mmtk_set_heap_size(size: usize) { - memory_manager::process(&BUILDER, "heap_size", size.to_string().as_str()); + let mut builder = BUILDER.lock().unwrap(); + assert!(builder.options.heap_size.set(size)); } #[no_mangle] @@ -158,7 +159,8 @@ pub extern "C" fn mmtk_harness_end() { pub extern "C" fn mmtk_process(name: *const c_char, value: *const c_char) -> bool { let name_str: &CStr = unsafe { CStr::from_ptr(name) }; let value_str: &CStr = unsafe { CStr::from_ptr(value) }; - memory_manager::process(&BUILDER, name_str.to_str().unwrap(), value_str.to_str().unwrap()) + let mut builder = BUILDER.lock().unwrap(); + memory_manager::process(&mut builder, name_str.to_str().unwrap(), value_str.to_str().unwrap()) } #[no_mangle] diff --git a/vmbindings/dummyvm/src/lib.rs b/vmbindings/dummyvm/src/lib.rs index e2eac91f36..ef2fd484f0 100644 --- a/vmbindings/dummyvm/src/lib.rs +++ b/vmbindings/dummyvm/src/lib.rs @@ -35,15 +35,17 @@ impl VMBinding for DummyVM { } use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Mutex; /// This is used to ensure we initialize MMTk at a specified timing. pub static MMTK_INITIALIZED: AtomicBool = AtomicBool::new(false); lazy_static! { - pub static ref BUILDER: MMTKBuilder = MMTKBuilder::new(); + pub static ref BUILDER: Mutex = Mutex::new(MMTKBuilder::new()); pub static ref SINGLETON: MMTK = { + let builder = BUILDER.lock().unwrap(); debug_assert!(!MMTK_INITIALIZED.load(Ordering::Relaxed)); - let ret = mmtk::memory_manager::gc_init(&BUILDER); + let ret = mmtk::memory_manager::gc_init(&builder); MMTK_INITIALIZED.store(true, std::sync::atomic::Ordering::Relaxed); *ret }; From f4961b925a02eb2118174ac6546bdc64f3b6fda2 Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Wed, 20 Jul 2022 15:43:44 +1000 Subject: [PATCH 11/14] Rename gc_init to mmtk_init. Fix ordering for MMTK_INITIALIZED in the dummyVM. --- docs/header/mmtk.h | 2 +- docs/portingguide/src/howto/nogc.md | 6 +++--- examples/allocation_benchmark.c | 3 +-- examples/main.c | 3 +-- src/memory_manager.rs | 6 ++---- vmbindings/dummyvm/api/mmtk.h | 3 +-- vmbindings/dummyvm/src/api.rs | 18 ++++++++++-------- vmbindings/dummyvm/src/lib.rs | 4 ++-- .../tests/allocate_with_disable_collection.rs | 3 +-- .../allocate_with_initialize_collection.rs | 3 +-- .../allocate_with_re_enable_collection.rs | 3 +-- .../allocate_without_initialize_collection.rs | 3 +-- vmbindings/dummyvm/src/tests/fixtures/mod.rs | 6 ++---- vmbindings/dummyvm/src/tests/issue139.rs | 3 +-- 14 files changed, 28 insertions(+), 38 deletions(-) diff --git a/docs/header/mmtk.h b/docs/header/mmtk.h index 8af54aadcf..29b982eca2 100644 --- a/docs/header/mmtk.h +++ b/docs/header/mmtk.h @@ -20,7 +20,7 @@ typedef void* MMTk_Builder; typedef void* MMTk; // Initialize an MMTk instance -extern MMTk mmtk_gc_init(MMTk_Builder builder); +extern MMTk mmtk_init(MMTk_Builder builder); // Request MMTk to create a new mutator for the given `tls` thread extern MMTk_Mutator mmtk_bind_mutator(void* tls); diff --git a/docs/portingguide/src/howto/nogc.md b/docs/portingguide/src/howto/nogc.md index d8740d5d95..84dab66cb6 100644 --- a/docs/portingguide/src/howto/nogc.md +++ b/docs/portingguide/src/howto/nogc.md @@ -6,7 +6,7 @@ Although this appears trivial, depending on the complexity of the runtime and ho In the case of V8, the refactoring within V8 required to get a simple NoGC plan working was substantial, touching over 100 files. So it’s a good idea not to underestimate the difficulty of a NoGC port! -In order to implement NoGC, we only need to handle MMTk initialisation (`gc_init`), mutator initialisation (`bind_mutator`), and memory allocation (`alloc`). +In order to implement NoGC, we only need to handle MMTk initialisation (`mmtk_init`), mutator initialisation (`bind_mutator`), and memory allocation (`alloc`). You may want to take the following steps. @@ -34,8 +34,8 @@ You may want to take the following steps. - Change all alloc calls in the GC to calloc (https://www.tutorialspoint.com/c_standard_library/c_function_calloc.htm). Note: calloc is used instead of malloc as it zero-initialises memory. - The purpose of this step is simply to help you find all allocation calls. 4. Single Threaded MMTk Allocation - 1. Create a `mmtk.h` header file which exposes the functions required to implement NoGC (`gc_init`, `alloc`, `bind_mutator`), and `include` it. You can use the [DummyVM `mmtk.h` header file](https://github.com/mmtk/mmtk-core/blob/master/vmbindings/dummyvm/api/mmtk.h) as an example. - 2. Initialise MMTk by calling `gc_init`, with the size of the heap. In the future, you may wish to make this value configurable via a command line argument or environment variable. + 1. Create a `mmtk.h` header file which exposes the functions required to implement NoGC (`mmtk_init`, `alloc`, `bind_mutator`), and `include` it. You can use the [DummyVM `mmtk.h` header file](https://github.com/mmtk/mmtk-core/blob/master/vmbindings/dummyvm/api/mmtk.h) as an example. + 2. Initialise MMTk by calling `mmtk_init`, with the size of the heap. In the future, you may wish to make this value configurable via a command line argument or environment variable. 2. You can set [options for MMTk](https://www.mmtk.io/mmtk-core/mmtk/util/options/struct.Options.html) by using `process` to pass options, or simply by setting environtment variables. For example, to use the NoGC plan, you can set the env var `MMTK_PLAN=NoGC`. 3. Create a MMTk mutator instance using `bind_mutator` and pass the return value of `gc_init`. diff --git a/examples/allocation_benchmark.c b/examples/allocation_benchmark.c index 693ac11457..a23860dfd5 100644 --- a/examples/allocation_benchmark.c +++ b/examples/allocation_benchmark.c @@ -5,8 +5,7 @@ int main() { volatile uint64_t * tmp; - mmtk_set_heap_size(1024*1024*1024); - mmtk_gc_init(); + mmtk_init24*1024*1024); MMTk_Mutator handle = mmtk_bind_mutator(0); for (int i=0; i<1024*1024*100; i++) { diff --git a/examples/main.c b/examples/main.c index 2366e5edaf..be9820d2c9 100644 --- a/examples/main.c +++ b/examples/main.c @@ -2,8 +2,7 @@ #include "mmtk.h" int main(int argc, char* argv[]){ - mmtk_set_heap_size(1024*1024); - mmtk_gc_init(); + mmtk_init(1024*1024); MMTk_Mutator handle = mmtk_bind_mutator(0); diff --git a/src/memory_manager.rs b/src/memory_manager.rs index e709b16851..31d294a9ff 100644 --- a/src/memory_manager.rs +++ b/src/memory_manager.rs @@ -34,7 +34,7 @@ use std::sync::atomic::Ordering; /// /// 1. Create an [MMTKBuilder](../mmtk/struct.MMTKBuilder.html) instance. /// 2. Set command line options for MMTKBuilder by [process()](./fn.process.html) or [process_bulk()](./fn.process_bulk.html). -/// 3. Initialize MMTk by calling this function, `gc_init()`, and pass the builder earlier. This call will return an MMTK instance. +/// 3. Initialize MMTk by calling this function, `mmtk_init()`, and pass the builder earlier. This call will return an MMTK instance. /// Usually a binding store the MMTK instance statically as a singleton. We plan to allow multiple instances, but this is not yet fully /// supported. Currently we assume a binding will only need one MMTk instance. /// 4. Enable garbage collection in MMTk by [enable_collection()](./fn.enable_collection.html). A binding should only call this once its @@ -52,7 +52,7 @@ use std::sync::atomic::Ordering; /// /// Arguments: /// * `builder`: The reference to a MMTk builder. -pub fn gc_init(builder: &MMTKBuilder) -> Box> { +pub fn mmtk_init(builder: &MMTKBuilder) -> Box> { match crate::util::logger::try_init() { Ok(_) => debug!("MMTk initialized the logger."), Err(_) => debug!( @@ -335,7 +335,6 @@ pub fn disable_collection(mmtk: &'static MMTK) { } /// Process MMTk run-time options. Returns true if the option is processed successfully. -/// We expect that only one thread should call `process()` or `process_bulk()` before `gc_init()` is called. /// /// Arguments: /// * `mmtk`: A reference to an MMTk instance. @@ -346,7 +345,6 @@ pub fn process(builder: &mut MMTKBuilder, name: &str, value: &str) -> bool { } /// Process multiple MMTk run-time options. Returns true if all the options are processed successfully. -/// We expect that only one thread should call `process()` or `process_bulk()` before `gc_init()` is called. /// /// Arguments: /// * `mmtk`: A reference to an MMTk instance. diff --git a/vmbindings/dummyvm/api/mmtk.h b/vmbindings/dummyvm/api/mmtk.h index a7ddc02253..4283d218e5 100644 --- a/vmbindings/dummyvm/api/mmtk.h +++ b/vmbindings/dummyvm/api/mmtk.h @@ -18,8 +18,7 @@ extern "C" { typedef void* MMTk_Mutator; // Initialize an MMTk instance -extern void mmtk_gc_init(); -extern void mmtk_set_heap_size(size_t size); +extern void mmtk_init(size_t heap_size); // Request MMTk to create a new mutator for the given `tls` thread extern MMTk_Mutator mmtk_bind_mutator(void* tls); diff --git a/vmbindings/dummyvm/src/api.rs b/vmbindings/dummyvm/src/api.rs index c3709ad72f..29dc0d0a06 100644 --- a/vmbindings/dummyvm/src/api.rs +++ b/vmbindings/dummyvm/src/api.rs @@ -15,15 +15,17 @@ use crate::SINGLETON; use crate::BUILDER; #[no_mangle] -pub extern "C" fn mmtk_gc_init() { - assert!(!crate::MMTK_INITIALIZED.load(Ordering::Relaxed)); - lazy_static::initialize(&SINGLETON); -} +pub extern "C" fn mmtk_init(heap_size: usize) { + // set heap size first + { + let mut builder = BUILDER.lock().unwrap(); + assert!(builder.options.heap_size.set(heap_size)); + } -#[no_mangle] -pub extern "C" fn mmtk_set_heap_size(size: usize) { - let mut builder = BUILDER.lock().unwrap(); - assert!(builder.options.heap_size.set(size)); + // Make sure MMTk has not yet been initialized + assert!(!crate::MMTK_INITIALIZED.load(Ordering::SeqCst)); + // Initialize MMTk here + lazy_static::initialize(&SINGLETON); } #[no_mangle] diff --git a/vmbindings/dummyvm/src/lib.rs b/vmbindings/dummyvm/src/lib.rs index ef2fd484f0..fb68fcd16e 100644 --- a/vmbindings/dummyvm/src/lib.rs +++ b/vmbindings/dummyvm/src/lib.rs @@ -44,8 +44,8 @@ lazy_static! { pub static ref BUILDER: Mutex = Mutex::new(MMTKBuilder::new()); pub static ref SINGLETON: MMTK = { let builder = BUILDER.lock().unwrap(); - debug_assert!(!MMTK_INITIALIZED.load(Ordering::Relaxed)); - let ret = mmtk::memory_manager::gc_init(&builder); + debug_assert!(!MMTK_INITIALIZED.load(Ordering::SeqCst)); + let ret = mmtk::memory_manager::mmtk_init(&builder); MMTK_INITIALIZED.store(true, std::sync::atomic::Ordering::Relaxed); *ret }; diff --git a/vmbindings/dummyvm/src/tests/allocate_with_disable_collection.rs b/vmbindings/dummyvm/src/tests/allocate_with_disable_collection.rs index 613e0e6ffb..37094ff370 100644 --- a/vmbindings/dummyvm/src/tests/allocate_with_disable_collection.rs +++ b/vmbindings/dummyvm/src/tests/allocate_with_disable_collection.rs @@ -8,8 +8,7 @@ use mmtk::AllocationSemantics; pub fn allocate_with_disable_collection() { const MB: usize = 1024 * 1024; // 1MB heap - mmtk_set_heap_size(MB); - mmtk_gc_init(); + mmtk_init(MB); mmtk_initialize_collection(VMThread::UNINITIALIZED); let handle = mmtk_bind_mutator(VMMutatorThread(VMThread::UNINITIALIZED)); // Allocate 1MB. It should be fine. diff --git a/vmbindings/dummyvm/src/tests/allocate_with_initialize_collection.rs b/vmbindings/dummyvm/src/tests/allocate_with_initialize_collection.rs index 149fe16362..18e7dc432d 100644 --- a/vmbindings/dummyvm/src/tests/allocate_with_initialize_collection.rs +++ b/vmbindings/dummyvm/src/tests/allocate_with_initialize_collection.rs @@ -9,8 +9,7 @@ use mmtk::AllocationSemantics; pub fn allocate_with_initialize_collection() { const MB: usize = 1024 * 1024; // 1MB heap - mmtk_set_heap_size(MB); - mmtk_gc_init(); + mmtk_init(MB); mmtk_initialize_collection(VMThread::UNINITIALIZED); let handle = mmtk_bind_mutator(VMMutatorThread(VMThread::UNINITIALIZED)); // Attempt to allocate 2MB. This will trigger GC. diff --git a/vmbindings/dummyvm/src/tests/allocate_with_re_enable_collection.rs b/vmbindings/dummyvm/src/tests/allocate_with_re_enable_collection.rs index 0c5ad7e460..cf49532577 100644 --- a/vmbindings/dummyvm/src/tests/allocate_with_re_enable_collection.rs +++ b/vmbindings/dummyvm/src/tests/allocate_with_re_enable_collection.rs @@ -9,8 +9,7 @@ use mmtk::AllocationSemantics; pub fn allocate_with_re_enable_collection() { const MB: usize = 1024 * 1024; // 1MB heap - mmtk_set_heap_size(MB); - mmtk_gc_init(); + mmtk_init(MB); mmtk_initialize_collection(VMThread::UNINITIALIZED); let handle = mmtk_bind_mutator(VMMutatorThread(VMThread::UNINITIALIZED)); // Allocate 1MB. It should be fine. diff --git a/vmbindings/dummyvm/src/tests/allocate_without_initialize_collection.rs b/vmbindings/dummyvm/src/tests/allocate_without_initialize_collection.rs index 86a2508c7c..98124db11c 100644 --- a/vmbindings/dummyvm/src/tests/allocate_without_initialize_collection.rs +++ b/vmbindings/dummyvm/src/tests/allocate_without_initialize_collection.rs @@ -9,8 +9,7 @@ use mmtk::AllocationSemantics; pub fn allocate_without_initialize_collection() { const MB: usize = 1024 * 1024; // 1MB heap - mmtk_set_heap_size(MB); - mmtk_gc_init(); + mmtk_init(MB); let handle = mmtk_bind_mutator(VMMutatorThread(VMThread::UNINITIALIZED)); // Attempt to allocate 2MB memory. This should trigger a GC, but as we never call initialize_collection(), we cannot do GC. let addr = mmtk_alloc(handle, 2 * MB, 8, 0, AllocationSemantics::Default); diff --git a/vmbindings/dummyvm/src/tests/fixtures/mod.rs b/vmbindings/dummyvm/src/tests/fixtures/mod.rs index 308a2e1ed3..a433f231b7 100644 --- a/vmbindings/dummyvm/src/tests/fixtures/mod.rs +++ b/vmbindings/dummyvm/src/tests/fixtures/mod.rs @@ -73,8 +73,7 @@ impl FixtureContent for SingleObject { fn create() -> Self { const MB: usize = 1024 * 1024; // 1MB heap - mmtk_set_heap_size(MB); - mmtk_gc_init(); + mmtk_init(MB); mmtk_initialize_collection(VMThread::UNINITIALIZED); // Make sure GC does not run during test. mmtk_disable_collection(); @@ -102,8 +101,7 @@ impl FixtureContent for MMTKSingleton { fn create() -> Self { const MB: usize = 1024 * 1024; // 1MB heap - mmtk_set_heap_size(MB); - mmtk_gc_init(); + mmtk_init(MB); mmtk_initialize_collection(VMThread::UNINITIALIZED); MMTKSingleton { diff --git a/vmbindings/dummyvm/src/tests/issue139.rs b/vmbindings/dummyvm/src/tests/issue139.rs index 2d5ace31ed..7d073e731f 100644 --- a/vmbindings/dummyvm/src/tests/issue139.rs +++ b/vmbindings/dummyvm/src/tests/issue139.rs @@ -4,8 +4,7 @@ use mmtk::AllocationSemantics; #[test] pub fn issue139_alloc_non_multiple_of_min_alignment() { - mmtk_set_heap_size(200*1024*1024); - mmtk_gc_init(); + mmtk_init(200*1024*1024); let handle = mmtk_bind_mutator(VMMutatorThread(VMThread::UNINITIALIZED)); // Allocate 6 bytes with 8 bytes ailgnment required From ace021c6e7d67690b8003f8f5eed46cbf1ca9807 Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Wed, 20 Jul 2022 16:27:30 +1000 Subject: [PATCH 12/14] Fix typo. Move setting heap size out of assert! --- examples/allocation_benchmark.c | 2 +- vmbindings/dummyvm/src/api.rs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/allocation_benchmark.c b/examples/allocation_benchmark.c index a23860dfd5..ce5c206263 100644 --- a/examples/allocation_benchmark.c +++ b/examples/allocation_benchmark.c @@ -5,7 +5,7 @@ int main() { volatile uint64_t * tmp; - mmtk_init24*1024*1024); + mmtk_init(24*1024*1024); MMTk_Mutator handle = mmtk_bind_mutator(0); for (int i=0; i<1024*1024*100; i++) { diff --git a/vmbindings/dummyvm/src/api.rs b/vmbindings/dummyvm/src/api.rs index 29dc0d0a06..d68508c7bf 100644 --- a/vmbindings/dummyvm/src/api.rs +++ b/vmbindings/dummyvm/src/api.rs @@ -19,7 +19,8 @@ pub extern "C" fn mmtk_init(heap_size: usize) { // set heap size first { let mut builder = BUILDER.lock().unwrap(); - assert!(builder.options.heap_size.set(heap_size)); + let success = builder.options.heap_size.set(heap_size); + assert!(success, "Failed to set heap size to {}", heap_size); } // Make sure MMTk has not yet been initialized From c81a10509c84f9c4b24dfaf3c5ea8e7bf805bee9 Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Wed, 20 Jul 2022 16:47:14 +1000 Subject: [PATCH 13/14] Make util::options public so bindings can access enums for options. --- src/util/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util/mod.rs b/src/util/mod.rs index f0895d41f1..771e253ab4 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -23,6 +23,8 @@ pub mod linear_scan; pub mod memory; /// Opaque pointers used in MMTk, e.g. VMThread. pub mod opaque_pointer; +/// MMTk command line options. +pub mod options; /// Reference processing implementation. pub mod reference_processor; @@ -51,8 +53,6 @@ pub mod malloc; pub mod metadata; /// Forwarding word in object copying. pub(crate) mod object_forwarding; -/// MMTk command line options. -pub(crate) mod options; /// Utilities funcitons for Rust pub(crate) mod rust_util; /// Sanity checker for GC. From 5e2689bacc402a48d015ee8fd9ddb118118e02be Mon Sep 17 00:00:00 2001 From: Yi Lin Date: Wed, 20 Jul 2022 16:59:32 +1000 Subject: [PATCH 14/14] Fix typo --- examples/allocation_benchmark.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/allocation_benchmark.c b/examples/allocation_benchmark.c index ce5c206263..e113aa8763 100644 --- a/examples/allocation_benchmark.c +++ b/examples/allocation_benchmark.c @@ -5,7 +5,7 @@ int main() { volatile uint64_t * tmp; - mmtk_init(24*1024*1024); + mmtk_init(1024*1024*1024); MMTk_Mutator handle = mmtk_bind_mutator(0); for (int i=0; i<1024*1024*100; i++) {