use crate::util::constants::DEFAULT_STRESS_FACTOR;
use std::cell::UnsafeCell;
use std::default::Default;
use std::ops::Deref;
custom_derive! {
#[derive(Copy, Clone, EnumFromStr)]
pub enum NurseryZeroingOptions {
Temporal,
Nontemporal,
Concurrent,
Adaptive,
}
}
custom_derive! {
#[derive(Copy, Clone, EnumFromStr, Debug)]
pub enum PlanSelector {
NoGC,
SemiSpace,
GenCopy,
MarkSweep
}
}
pub struct UnsafeOptionsWrapper(UnsafeCell<Options>);
unsafe impl Sync for UnsafeOptionsWrapper {}
impl UnsafeOptionsWrapper {
pub const fn new(o: Options) -> UnsafeOptionsWrapper {
UnsafeOptionsWrapper(UnsafeCell::new(o))
}
pub unsafe fn process(&self, name: &str, value: &str) -> bool {
(&mut *self.0.get()).set_from_camelcase_str(name, value)
}
}
impl Deref for UnsafeOptionsWrapper {
type Target = Options;
fn deref(&self) -> &Options {
unsafe { &*self.0.get() }
}
}
fn always_valid<T>(_: T) -> bool {
true
}
macro_rules! options {
($($name:ident: $type:ty[$validator:expr] = $default:expr),*,) => [
options!($($name: $type[$validator] = $default),*);
];
($($name:ident: $type:ty[$validator:expr] = $default:expr),*) => [
pub struct Options {
$(pub $name: $type),*
}
impl Options {
pub fn set_from_str(&mut self, s: &str, val: &str)->bool {
match s {
$(stringify!($name) => if let Ok(val) = val.parse() {
self.$name = val;
let validate_fn = $validator;
validate_fn(val)
} else {
eprintln!("Warn: unable to set {}={}. Default value will be used.", s, val);
false
})*
_ => panic!("Invalid Options key")
}
}
}
impl Default for Options {
fn default() -> Self {
let mut options = Options {
$($name: $default),*
};
const PREFIX: &str = "MMTK_";
for (key, val) in std::env::vars() {
if let Some(rest_of_key) = key.strip_prefix(PREFIX) {
let lowercase: &str = &rest_of_key.to_lowercase();
match lowercase {
$(stringify!($name) => { options.set_from_str(lowercase, &val); },)*
_ => {}
}
}
}
return options;
}
}
]
}
options! {
plan: PlanSelector [always_valid] = PlanSelector::NoGC,
threads: usize [|v| v > 0] = num_cpus::get(),
use_short_stack_scans: bool [always_valid] = false,
use_return_barrier: bool [always_valid] = false,
eager_complete_sweep: bool [always_valid] = false,
ignore_system_g_c: bool [always_valid] = false,
variable_size_heap: bool [always_valid] = true,
no_finalizer: bool [always_valid] = false,
no_reference_types: bool [always_valid] = false,
nursery_zeroing: NurseryZeroingOptions[always_valid] = NurseryZeroingOptions::Temporal,
verbose: usize [always_valid] = 0,
stress_factor: usize [always_valid] = DEFAULT_STRESS_FACTOR,
analysis_factor: usize [always_valid] = DEFAULT_STRESS_FACTOR,
vm_space: bool [always_valid] = true,
vm_space_size: usize [|v| v > 0] = 0x7cc_cccc,
}
impl Options {
fn set_from_camelcase_str(&mut self, s: &str, val: &str) -> bool {
trace!("Trying to process option pair: ({}, {})", s, val);
let mut sr = String::with_capacity(s.len());
for c in s.chars() {
if c.is_uppercase() {
sr.push('_');
for c in c.to_lowercase() {
sr.push(c);
}
} else {
sr.push(c)
}
}
let result = self.set_from_str(sr.as_str(), val);
trace!("Trying to process option pair: ({})", sr);
if result {
trace!("Validation passed");
} else {
trace!("Validation failed")
}
result
}
}
#[cfg(test)]
mod tests {
use crate::util::constants::DEFAULT_STRESS_FACTOR;
use crate::util::options::Options;
use crate::util::test_util::serial_test;
#[test]
fn no_env_var() {
serial_test(|| {
let options = Options::default();
assert_eq!(options.stress_factor, DEFAULT_STRESS_FACTOR);
})
}
#[test]
fn with_valid_env_var() {
serial_test(|| {
std::env::set_var("MMTK_STRESS_FACTOR", "4096");
let res = std::panic::catch_unwind(|| {
let options = Options::default();
assert_eq!(options.stress_factor, 4096);
});
assert!(res.is_ok());
std::env::remove_var("MMTK_STRESS_FACTOR");
})
}
#[test]
fn with_multiple_valid_env_vars() {
serial_test(|| {
std::env::set_var("MMTK_STRESS_FACTOR", "4096");
std::env::set_var("MMTK_VM_SPACE", "false");
let res = std::panic::catch_unwind(|| {
let options = Options::default();
assert_eq!(options.stress_factor, 4096);
assert_eq!(options.vm_space, false);
});
assert!(res.is_ok());
std::env::remove_var("MMTK_STRESS_FACTOR");
std::env::remove_var("MMTK_VM_SPACE");
})
}
#[test]
fn with_invalid_env_var_value() {
serial_test(|| {
std::env::set_var("MMTK_STRESS_FACTOR", "abc");
let res = std::panic::catch_unwind(|| {
let options = Options::default();
assert_eq!(options.stress_factor, DEFAULT_STRESS_FACTOR);
});
assert!(res.is_ok());
std::env::remove_var("MMTK_STRESS_FACTOR");
})
}
#[test]
fn with_invalid_env_var_key() {
serial_test(|| {
std::env::set_var("MMTK_ABC", "42");
let res = std::panic::catch_unwind(|| {
let options = Options::default();
assert_eq!(options.stress_factor, DEFAULT_STRESS_FACTOR);
});
assert!(res.is_ok());
std::env::remove_var("MMTK_ABC");
})
}
}