Skip to content

Commit 471ae81

Browse files
committed
Fix environment variable parsing
Ues more idiomatic rust approaches.
1 parent 4922551 commit 471ae81

File tree

2 files changed

+72
-93
lines changed

2 files changed

+72
-93
lines changed

gc/mmtk/src/api.rs

Lines changed: 49 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#![allow(clippy::missing_safety_doc)]
44

55
use mmtk::util::options::PlanSelector;
6+
use std::str::FromStr;
67
use std::sync::atomic::Ordering;
78

89
use crate::abi::RawVecOfObjRef;
@@ -41,76 +42,77 @@ pub extern "C" fn mmtk_is_reachable(object: ObjectReference) -> bool {
4142

4243
// =============== Bootup ===============
4344

44-
fn mmtk_builder_default_parse_threads() -> usize {
45-
let threads_str = std::env::var("MMTK_THREADS").unwrap_or("0".to_string());
45+
fn parse_env_var_with<T, F: FnOnce(&str) -> Option<T>>(key: &str, parse: F) -> Option<T> {
46+
let val = match std::env::var(key) {
47+
Ok(val) => val,
48+
Err(std::env::VarError::NotPresent) => return None,
49+
Err(std::env::VarError::NotUnicode(os_string)) => {
50+
eprintln!("[FATAL] Invalid {key} {os_string:?}");
51+
std::process::exit(1);
52+
}
53+
};
4654

47-
threads_str.parse::<usize>().unwrap_or_else(|_err| {
48-
eprintln!("[FATAL] Invalid MMTK_THREADS {}", threads_str);
55+
let parsed = parse(&val).unwrap_or_else(|| {
56+
eprintln!("[FATAL] Invalid {key} {val}");
4957
std::process::exit(1);
50-
})
51-
}
58+
});
5259

53-
fn mmtk_builder_default_parse_heap_min() -> usize {
54-
const DEFAULT_HEAP_MIN: usize = 1 << 20;
60+
Some(parsed)
61+
}
5562

56-
let heap_min_str = std::env::var("MMTK_HEAP_MIN").unwrap_or(DEFAULT_HEAP_MIN.to_string());
63+
fn parse_env_var<T: FromStr>(key: &str) -> Option<T> {
64+
parse_env_var_with(key, |s| s.parse().ok())
65+
}
5766

58-
let size = parse_capacity(&heap_min_str, 0);
59-
if size == 0 {
60-
eprintln!("[FATAL] Invalid MMTK_HEAP_MIN {}", heap_min_str);
61-
std::process::exit(1);
62-
}
67+
fn mmtk_builder_default_parse_threads() -> Option<usize> {
68+
parse_env_var("MMTK_THREADS")
69+
}
6370

64-
size
71+
fn mmtk_builder_default_parse_heap_min() -> usize {
72+
const DEFAULT_HEAP_MIN: usize = 1 << 20;
73+
parse_env_var_with("MMTK_HEAP_MIN", parse_capacity).unwrap_or(DEFAULT_HEAP_MIN)
6574
}
6675

6776
fn mmtk_builder_default_parse_heap_max() -> usize {
68-
let heap_max_str = std::env::var("MMTK_HEAP_MAX").unwrap_or(default_heap_max().to_string());
69-
70-
let size = parse_capacity(&heap_max_str, 0);
71-
if size == 0 {
72-
eprintln!("[FATAL] Invalid MMTK_HEAP_MAX {}", heap_max_str);
73-
std::process::exit(1);
74-
}
75-
76-
size
77+
parse_env_var_with("MMTK_HEAP_MAX", parse_capacity).unwrap_or_else(default_heap_max)
7778
}
7879

7980
fn mmtk_builder_default_parse_heap_mode(heap_min: usize, heap_max: usize) -> GCTriggerSelector {
80-
let heap_mode_str = std::env::var("MMTK_HEAP_MODE").unwrap_or("dynamic".to_string());
81+
let make_fixed = || GCTriggerSelector::FixedHeapSize(heap_max);
82+
let make_dynamic = || GCTriggerSelector::DynamicHeapSize(heap_min, heap_max);
8183

82-
match heap_mode_str.as_str() {
83-
"fixed" => GCTriggerSelector::FixedHeapSize(heap_max),
84-
"dynamic" => GCTriggerSelector::DynamicHeapSize(heap_min, heap_max),
85-
_ => {
86-
eprintln!("[FATAL] Invalid MMTK_HEAP_MODE {}", heap_mode_str);
87-
std::process::exit(1);
88-
}
89-
}
84+
parse_env_var_with("MMTK_HEAP_MODE", |s| match s {
85+
"fixed" => Some(make_fixed()),
86+
"dynamic" => Some(make_dynamic()),
87+
_ => None,
88+
})
89+
.unwrap_or_else(make_dynamic)
9090
}
9191

9292
fn mmtk_builder_default_parse_plan() -> PlanSelector {
93-
let plan_str = std::env::var("MMTK_PLAN").unwrap_or("Immix".to_string());
94-
95-
match plan_str.as_str() {
96-
"NoGC" => PlanSelector::NoGC,
97-
"MarkSweep" => PlanSelector::MarkSweep,
98-
"Immix" => PlanSelector::Immix,
99-
_ => {
100-
eprintln!("[FATAL] Invalid MMTK_PLAN {}", plan_str);
101-
std::process::exit(1);
102-
}
103-
}
93+
parse_env_var_with("MMTK_PLAN", |s| match s {
94+
"NoGC" => Some(PlanSelector::NoGC),
95+
"MarkSweep" => Some(PlanSelector::MarkSweep),
96+
"Immix" => Some(PlanSelector::Immix),
97+
_ => None,
98+
})
99+
.unwrap_or(PlanSelector::Immix)
104100
}
105101

106102
#[no_mangle]
107103
pub extern "C" fn mmtk_builder_default() -> *mut MMTKBuilder {
108104
let mut builder = MMTKBuilder::new_no_env_vars();
109105
builder.options.no_finalizer.set(true);
110106

111-
let threads = mmtk_builder_default_parse_threads();
112-
if threads > 0 {
113-
builder.options.threads.set(threads);
107+
if let Some(threads) = mmtk_builder_default_parse_threads() {
108+
if !builder.options.threads.set(threads) {
109+
// MMTk will validate it and reject 0.
110+
eprintln!(
111+
"[FATAL] Failed to set the number of MMTk threads to {}",
112+
threads
113+
);
114+
std::process::exit(1);
115+
}
114116
}
115117

116118
let heap_min = mmtk_builder_default_parse_heap_min();

gc/mmtk/src/utils.rs

Lines changed: 23 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -97,35 +97,29 @@ pub fn default_heap_max() -> usize {
9797
.expect("Invalid Memory size") as usize
9898
}
9999

100-
pub fn parse_capacity(input: &str, default: usize) -> usize {
100+
pub fn parse_capacity(input: &str) -> Option<usize> {
101101
let trimmed = input.trim();
102102

103103
const KIBIBYTE: usize = 1024;
104104
const MEBIBYTE: usize = 1024 * KIBIBYTE;
105105
const GIBIBYTE: usize = 1024 * MEBIBYTE;
106106

107-
let (val, suffix) = if let Some(pos) = trimmed.find(|c: char| !c.is_numeric()) {
108-
(&trimmed[..pos], &trimmed[pos..])
107+
let (number, suffix) = if let Some(pos) = trimmed.find(|c: char| !c.is_numeric()) {
108+
trimmed.split_at(pos)
109109
} else {
110110
(trimmed, "")
111111
};
112112

113-
// 1MiB is the default heap size
114-
match (val, suffix) {
115-
(number, "GiB") => number
116-
.parse::<usize>()
117-
.map(|v| v * GIBIBYTE)
118-
.unwrap_or(default),
119-
(number, "MiB") => number
120-
.parse::<usize>()
121-
.map(|v| v * MEBIBYTE)
122-
.unwrap_or(default),
123-
(number, "KiB") => number
124-
.parse::<usize>()
125-
.map(|v| v * KIBIBYTE)
126-
.unwrap_or(default),
127-
(number, "") => number.parse::<usize>().unwrap_or(default),
128-
(_, _) => default,
113+
let Ok(v) = number.parse::<usize>() else {
114+
return None;
115+
};
116+
117+
match suffix {
118+
"GiB" => Some(v * GIBIBYTE),
119+
"MiB" => Some(v * MEBIBYTE),
120+
"KiB" => Some(v * KIBIBYTE),
121+
"" => Some(v),
122+
_ => None,
129123
}
130124
}
131125

@@ -135,47 +129,30 @@ mod tests {
135129

136130
#[test]
137131
fn test_parse_capacity_parses_bare_bytes() {
138-
assert_eq!(1234, parse_capacity(&String::from("1234"), 0));
132+
assert_eq!(Some(1234), parse_capacity("1234"));
139133
}
140134

141135
#[test]
142136
fn test_parse_capacity_parses_kibibytes() {
143-
assert_eq!(10240, parse_capacity(&String::from("10KiB"), 0))
137+
assert_eq!(Some(10240), parse_capacity("10KiB"));
144138
}
145139

146140
#[test]
147141
fn test_parse_capacity_parses_mebibytes() {
148-
assert_eq!(10485760, parse_capacity(&String::from("10MiB"), 0))
142+
assert_eq!(Some(10485760), parse_capacity("10MiB"))
149143
}
150144

151145
#[test]
152146
fn test_parse_capacity_parses_gibibytes() {
153-
assert_eq!(10737418240, parse_capacity(&String::from("10GiB"), 0))
147+
assert_eq!(Some(10737418240), parse_capacity("10GiB"))
154148
}
155149

156150
#[test]
157-
fn test_parses_nonsense_value_as_default_max() {
158-
let default = 100;
159-
160-
assert_eq!(
161-
default,
162-
parse_capacity(&String::from("notanumber"), default)
163-
);
164-
assert_eq!(
165-
default,
166-
parse_capacity(&String::from("5tartswithanumber"), default)
167-
);
168-
assert_eq!(
169-
default,
170-
parse_capacity(&String::from("number1nthemiddle"), default)
171-
);
172-
assert_eq!(
173-
default,
174-
parse_capacity(&String::from("numberattheend111"), default)
175-
);
176-
assert_eq!(
177-
default,
178-
parse_capacity(&String::from("mult1pl3numb3r5"), default)
179-
);
151+
fn test_parse_capacity_parses_nonsense_values() {
152+
assert_eq!(None, parse_capacity("notanumber"));
153+
assert_eq!(None, parse_capacity("5tartswithanumber"));
154+
assert_eq!(None, parse_capacity("number1nthemiddle"));
155+
assert_eq!(None, parse_capacity("numberattheend111"));
156+
assert_eq!(None, parse_capacity("mult1pl3numb3r5"));
180157
}
181158
}

0 commit comments

Comments
 (0)