1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
|
//! Configuration (private module)
use crate::internal_prelude::*;
/// We want to support at least this many participants with a cache each
///
/// This is not a recommended value; it's probably too lax
const MIN_MAX_PARTICIPANTS: usize = 10;
/// Minimum hysteresis
///
/// This is not a recommended value; it's probably far too lax for sensible performance!
const MAX_LOW_WATER_RATIO: f32 = 0.98;
define_derive_deftly! {
/// Define setters on the builder for every field of type `Qty`
///
/// The field type must be spelled precisely that way:
/// we use `approx_equal(...)`.
QtySetters:
impl $< $ttype Builder > {
$(
${when approx_equal($ftype, Qty)}
${fattrs doc}
///
/// (Setter method.)
pub fn $fname(&mut self, value: usize) -> &mut Self {
self.$fname = Some(Qty(value));
self
}
)
}
}
/// Configuration for a memory data tracker
///
/// This is where the quota is specified.
#[derive(Debug, Clone, Builder, Eq, PartialEq, Deftly)]
#[derive_deftly(QtySetters)]
#[builder(build_fn(private, name = "build_unvalidated", error = "ConfigBuildError"))]
#[builder(derive(Serialize, Deserialize, Debug, Deftly, Eq, PartialEq))]
#[builder_struct_attr(derive_deftly(tor_config::Flattenable))]
pub struct Config {
/// Maximum memory usage tolerated before reclamation starts
///
/// Note that this is not a hard limit.
/// See Approximate in [the overview](crate).
///
///
#[builder(setter(custom))]
pub(crate) max: Qty,
/// Reclamation will stop when memory use is reduced to below this value
///
/// Default is 75% of the maximum.
#[builder(setter(custom))]
pub(crate) low_water: Qty,
}
impl Config {
/// Start building a [`Config`]
///
/// Returns a fresh default [`ConfigBuilder`].
pub fn builder() -> ConfigBuilder {
ConfigBuilder::default()
}
}
impl ConfigBuilder {
/// Builds a new `Config` from a builder
///
/// Returns an error unless at least `max` has been specified,
/// or if the fields values are invalid or inconsistent.
pub fn build(&self) -> Result<Config, ConfigBuildError> {
let mut builder = self.clone();
if let (Some(max), None) = (builder.max, builder.low_water) {
builder.low_water = Some(Qty((*max as f32 * 0.75) as _));
}
let config = builder.build_unvalidated()?;
let min_low_water = crate::mtracker::MAX_CACHE.as_usize() * MIN_MAX_PARTICIPANTS;
if *config.low_water < min_low_water {
return Err(ConfigBuildError::Invalid {
field: "low_water".into(),
problem: format!("must be at least {min_low_water}"),
});
}
let ratio: f32 = *config.low_water as f32 / *config.max as f32;
if ratio > MAX_LOW_WATER_RATIO {
return Err(ConfigBuildError::Inconsistent {
fields: vec!["low_water".into(), "max".into()],
problem: format!(
"low_water / max = {ratio}; must be <= {MAX_LOW_WATER_RATIO}, ideally considerably lower"
),
});
}
Ok(config)
}
}
|