Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add MMTKBuilder and allow all options to be set from command line #625

Merged
merged 14 commits into from
Jul 20, 2022
8 changes: 5 additions & 3 deletions examples/mmtk.h → docs/header/mmtk.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +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(size_t heap_size);
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);
Expand Down Expand Up @@ -74,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();
Expand Down
6 changes: 3 additions & 3 deletions docs/portingguide/src/howto/nogc.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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`.
Expand Down
4 changes: 2 additions & 2 deletions docs/tutorial/code/mygc_semispace/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -179,7 +179,7 @@ impl<VM: VMBinding> MyGC<VM> {
fn new(
vm_map: &'static VMMap,
mmapper: &'static Mmapper,
options: Arc<UnsafeOptionsWrapper>,
options: Arc<Options>,
) -> Self {
// Modify
let mut heap = HeapMeta::new(HEAP_START, HEAP_END);
Expand Down
2 changes: 1 addition & 1 deletion examples/allocation_benchmark.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

int main() {
volatile uint64_t * tmp;
mmtk_gc_init(1024*1024*1024);
mmtk_init24*1024*1024);
qinsoon marked this conversation as resolved.
Show resolved Hide resolved
MMTk_Mutator handle = mmtk_bind_mutator(0);

for (int i=0; i<1024*1024*100; i++) {
Expand Down
2 changes: 1 addition & 1 deletion examples/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#include "mmtk.h"

int main(int argc, char* argv[]){
mmtk_gc_init(1024*1024);
mmtk_init(1024*1024);

MMTk_Mutator handle = mmtk_bind_mutator(0);

Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
41 changes: 28 additions & 13 deletions src/memory_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -27,15 +28,31 @@ 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 [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, `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
/// thread system is ready. MMTk will not trigger garbage collection before this call.
///
/// 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<VM: VMBinding>(mmtk: &'static mut MMTK<VM>, heap_size: usize) {
/// * `builder`: The reference to a MMTk builder.
pub fn mmtk_init<VM: VMBinding>(builder: &MMTKBuilder) -> Box<MMTK<VM>> {
match crate::util::logger::try_init() {
Ok(_) => debug!("MMTk initialized the logger."),
Err(_) => debug!(
Expand All @@ -59,11 +76,11 @@ pub fn gc_init<VM: VMBinding>(mmtk: &'static mut MMTK<VM>, heap_size: usize) {
}
}
}
assert!(heap_size > 0, "Invalid heap size");
mmtk.plan.gc_init(heap_size, &crate::VM_MAP);
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.");
Box::new(mmtk)
}

/// Request MMTk to create a mutator for the given thread. For performance reasons, A VM should
Expand Down Expand Up @@ -318,24 +335,22 @@ pub fn disable_collection<VM: VMBinding>(mmtk: &'static MMTK<VM>) {
}

/// 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.
/// * `name`: The name of the option.
/// * `value`: The value of the option (as a string).
pub fn process<VM: VMBinding>(mmtk: &'static MMTK<VM>, name: &str, value: &str) -> bool {
unsafe { mmtk.options.process(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.
/// 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.
/// * `options`: a string that is key value pairs separated by white spaces, e.g. "threads=1 stress_factor=4096"
pub fn process_bulk<VM: VMBinding>(mmtk: &'static MMTK<VM>, options: &str) -> bool {
unsafe { mmtk.options.process_bulk(options) }
pub fn process_bulk(builder: &mut MMTKBuilder, options: &str) -> bool {
builder.set_options_bulk_by_str(options)
}

/// Return used memory in bytes.
Expand Down
61 changes: 49 additions & 12 deletions src/mmtk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -39,28 +39,70 @@ 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<SFTMap<'static>> = InitializeOnce::new();

// MMTk builder. This is used to set options before actually creating an MMTk instance.
pub struct MMTKBuilder {
/// The options for this instance.
pub options: Options,
}

impl MMTKBuilder {
/// Create an MMTK builder with default options
pub fn new() -> Self {
MMTKBuilder {
options: Options::default(),
}
}

/// Set an option.
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`.
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<VM: VMBinding>(&self) -> MMTK<VM> {
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
// 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<VM: VMBinding> {
pub(crate) options: Arc<Options>,
pub(crate) plan: Box<dyn Plan<VM = VM>>,
pub(crate) reference_processors: ReferenceProcessors,
pub(crate) finalizable_processor:
Mutex<FinalizableProcessor<<VM::VMReferenceGlue as ReferenceGlue<VM>>::FinalizableType>>,
pub(crate) options: Arc<UnsafeOptionsWrapper>,
pub(crate) scheduler: Arc<GCWorkScheduler<VM>>,
#[cfg(feature = "sanity")]
pub(crate) sanity_checker: Mutex<SanityChecker>,
inside_harness: AtomicBool,
}

impl<VM: VMBinding> MMTK<VM> {
pub fn new() -> Self {
pub fn new(options: Arc<Options>) -> 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()));

let num_workers = if cfg!(feature = "single_worker") {
1
} else {
Expand All @@ -75,13 +117,14 @@ impl<VM: VMBinding> MMTK<VM> {
options.clone(),
scheduler.clone(),
);

MMTK {
options,
plan,
reference_processors: ReferenceProcessors::new(),
finalizable_processor: Mutex::new(FinalizableProcessor::<
<VM::VMReferenceGlue as ReferenceGlue<VM>>::FinalizableType,
>::new()),
options,
scheduler,
#[cfg(feature = "sanity")]
sanity_checker: Mutex::new(SanityChecker::new()),
Expand Down Expand Up @@ -111,9 +154,3 @@ impl<VM: VMBinding> MMTK<VM> {
&self.options
}
}

impl<VM: VMBinding> Default for MMTK<VM> {
fn default() -> Self {
Self::new()
}
}
8 changes: 2 additions & 6 deletions src/plan/generational/copying/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -166,11 +166,7 @@ impl<VM: VMBinding> Plan for GenCopy<VM> {
}

impl<VM: VMBinding> GenCopy<VM> {
pub fn new(
vm_map: &'static VMMap,
mmapper: &'static Mmapper,
options: Arc<UnsafeOptionsWrapper>,
) -> Self {
pub fn new(vm_map: &'static VMMap, mmapper: &'static Mmapper, options: Arc<Options>) -> 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 =
Expand Down
4 changes: 2 additions & 2 deletions src/plan/generational/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -46,7 +46,7 @@ impl<VM: VMBinding> Gen<VM> {
constraints: &'static PlanConstraints,
vm_map: &'static VMMap,
mmapper: &'static Mmapper,
options: Arc<UnsafeOptionsWrapper>,
options: Arc<Options>,
) -> Self {
Gen {
nursery: CopySpace::new(
Expand Down
4 changes: 2 additions & 2 deletions src/plan/generational/immix/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;

Expand Down Expand Up @@ -207,7 +207,7 @@ impl<VM: VMBinding> GenImmix<VM> {
pub fn new(
vm_map: &'static VMMap,
mmapper: &'static Mmapper,
options: Arc<UnsafeOptionsWrapper>,
options: Arc<Options>,
scheduler: Arc<GCWorkScheduler<VM>>,
) -> Self {
let mut heap = HeapMeta::new(HEAP_START, HEAP_END);
Expand Down
10 changes: 5 additions & 5 deletions src/plan/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -67,7 +67,7 @@ pub fn create_plan<VM: VMBinding>(
plan: PlanSelector,
vm_map: &'static VMMap,
mmapper: &'static Mmapper,
options: Arc<UnsafeOptionsWrapper>,
options: Arc<Options>,
scheduler: Arc<GCWorkScheduler<VM>>,
) -> Box<dyn Plan<VM = VM>> {
match plan {
Expand Down Expand Up @@ -374,7 +374,7 @@ pub struct BasePlan<VM: VMBinding> {
pub stats: Stats,
mmapper: &'static Mmapper,
pub vm_map: &'static VMMap,
pub options: Arc<UnsafeOptionsWrapper>,
pub options: Arc<Options>,
pub heap: HeapMeta,
#[cfg(feature = "sanity")]
pub inside_sanity: AtomicBool,
Expand Down Expand Up @@ -456,7 +456,7 @@ impl<VM: VMBinding> BasePlan<VM> {
pub fn new(
vm_map: &'static VMMap,
mmapper: &'static Mmapper,
options: Arc<UnsafeOptionsWrapper>,
options: Arc<Options>,
mut heap: HeapMeta,
constraints: &'static PlanConstraints,
global_side_metadata_specs: Vec<SideMetadataSpec>,
Expand Down Expand Up @@ -889,7 +889,7 @@ impl<VM: VMBinding> CommonPlan<VM> {
pub fn new(
vm_map: &'static VMMap,
mmapper: &'static Mmapper,
options: Arc<UnsafeOptionsWrapper>,
options: Arc<Options>,
mut heap: HeapMeta,
constraints: &'static PlanConstraints,
global_side_metadata_specs: Vec<SideMetadataSpec>,
Expand Down
Loading