diff options
Diffstat (limited to 'crates/tor-memquota/src/config.rs')
| -rw-r--r-- | crates/tor-memquota/src/config.rs | 103 |
1 files changed, 103 insertions, 0 deletions
diff --git a/crates/tor-memquota/src/config.rs b/crates/tor-memquota/src/config.rs new file mode 100644 index 000000000..ce8fd364c --- /dev/null +++ b/crates/tor-memquota/src/config.rs @@ -0,0 +1,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) + } +} |
