Loading crates/tor-memquota/src/config.rs +112 −49 Original line number Diff line number Diff line Loading @@ -169,18 +169,56 @@ impl ConfigBuilder { // but we have no way of knowing whether we are a relay or not here. let max = match max { ExplicitOrAuto::Explicit(x) => x, ExplicitOrAuto::Auto => 'auto: { ExplicitOrAuto::Auto => compute_max_from_total_system_mem(total_available_memory()), }; let low_water = match low_water { ExplicitOrAuto::Explicit(x) => x, ExplicitOrAuto::Auto => Qty((*max as f32 * 0.75) as _), }; let config = ConfigInner { max, low_water }; /// Minimum low water. `const` so that overflows are compile-time. const MIN_LOW_WATER: usize = crate::mtracker::MAX_CACHE.as_usize() * MIN_MAX_PARTICIPANTS; let min_low_water = MIN_LOW_WATER; 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(IfEnabled::Enabled(config, enabled))) } } /// Determine a max given the system's total available memory. /// /// This is used when `max` is configured as "auto". /// It takes a `Result` so that we can handle the case where the total memory isn't available. fn compute_max_from_total_system_mem(mem: Result<usize, MemQueryError>) -> Qty { const MIB: usize = 1024 * 1024; const GIB: usize = 1024 * 1024 * 1024; let mem = match total_available_memory() { let mem = match mem { Ok(x) => x, Err(e) => { warn!("Unable to get the total available memory. Using a constant max instead: {e}"); // Can't get the total available memory, // so we return a max depending on whether the architecture is 32-bit or 64-bit. break 'auto Qty({ return Qty({ cfg_if::cfg_if! { if #[cfg(target_pointer_width = "64")] { 8 * GIB Loading Loading @@ -218,38 +256,6 @@ impl ConfigBuilder { Qty(mem) } }; let low_water = match low_water { ExplicitOrAuto::Explicit(x) => x, ExplicitOrAuto::Auto => Qty((*max as f32 * 0.75) as _), }; let config = ConfigInner { max, low_water }; /// Minimum low water. `const` so that overflows are compile-time. const MIN_LOW_WATER: usize = crate::mtracker::MAX_CACHE.as_usize() * MIN_MAX_PARTICIPANTS; let min_low_water = MIN_LOW_WATER; 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(IfEnabled::Enabled(config, enabled))) } } /// The total available memory in bytes. /// Loading Loading @@ -443,4 +449,61 @@ mod test { b.build().unwrap(); } } /// Test the logic that computes the `max` when configured as "auto". #[test] // We do some `1 * X` operations below for readability. #[allow(clippy::identity_op)] fn auto_max() { #[allow(unused)] fn check_helper(val: Qty, expected_32: Qty, expected_64: Qty) { assert_eq!(val, { cfg_if::cfg_if! { if #[cfg(target_pointer_width = "64")] { expected_64 } else if #[cfg(target_pointer_width = "32")] { expected_32 } else { panic!("Unsupported architecture :("); } } }); } check_helper( compute_max_from_total_system_mem(Err(MemQueryError::Unavailable)), /* 32-bit */ Qty(1 * 1024 * 1024 * 1024), /* 64-bit */ Qty(8 * 1024 * 1024 * 1024), ); check_helper( compute_max_from_total_system_mem(Ok(8 * 1024 * 1024 * 1024)), /* 32-bit */ Qty(2 * 1024 * 1024 * 1024), /* 64-bit */ Qty(3435973836), ); check_helper( compute_max_from_total_system_mem(Ok(7 * 1024 * 1024 * 1024)), /* 32-bit */ Qty(2 * 1024 * 1024 * 1024), /* 64-bit */ Qty(5637144576), ); check_helper( compute_max_from_total_system_mem(Ok(1 * 1024 * 1024 * 1024)), /* 32-bit */ Qty(805306368), /* 64-bit */ Qty(805306368), ); check_helper( compute_max_from_total_system_mem(Ok(7 * 1024)), /* 32-bit */ Qty(256 * 1024 * 1024), /* 64-bit */ Qty(256 * 1024 * 1024), ); check_helper( compute_max_from_total_system_mem(Ok(0)), /* 32-bit */ Qty(256 * 1024 * 1024), /* 64-bit */ Qty(256 * 1024 * 1024), ); check_helper( compute_max_from_total_system_mem(Ok(usize::MAX)), /* 32-bit */ Qty(2 * 1024 * 1024 * 1024), /* 64-bit */ Qty(8 * 1024 * 1024 * 1024), ); } } Loading
crates/tor-memquota/src/config.rs +112 −49 Original line number Diff line number Diff line Loading @@ -169,18 +169,56 @@ impl ConfigBuilder { // but we have no way of knowing whether we are a relay or not here. let max = match max { ExplicitOrAuto::Explicit(x) => x, ExplicitOrAuto::Auto => 'auto: { ExplicitOrAuto::Auto => compute_max_from_total_system_mem(total_available_memory()), }; let low_water = match low_water { ExplicitOrAuto::Explicit(x) => x, ExplicitOrAuto::Auto => Qty((*max as f32 * 0.75) as _), }; let config = ConfigInner { max, low_water }; /// Minimum low water. `const` so that overflows are compile-time. const MIN_LOW_WATER: usize = crate::mtracker::MAX_CACHE.as_usize() * MIN_MAX_PARTICIPANTS; let min_low_water = MIN_LOW_WATER; 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(IfEnabled::Enabled(config, enabled))) } } /// Determine a max given the system's total available memory. /// /// This is used when `max` is configured as "auto". /// It takes a `Result` so that we can handle the case where the total memory isn't available. fn compute_max_from_total_system_mem(mem: Result<usize, MemQueryError>) -> Qty { const MIB: usize = 1024 * 1024; const GIB: usize = 1024 * 1024 * 1024; let mem = match total_available_memory() { let mem = match mem { Ok(x) => x, Err(e) => { warn!("Unable to get the total available memory. Using a constant max instead: {e}"); // Can't get the total available memory, // so we return a max depending on whether the architecture is 32-bit or 64-bit. break 'auto Qty({ return Qty({ cfg_if::cfg_if! { if #[cfg(target_pointer_width = "64")] { 8 * GIB Loading Loading @@ -218,38 +256,6 @@ impl ConfigBuilder { Qty(mem) } }; let low_water = match low_water { ExplicitOrAuto::Explicit(x) => x, ExplicitOrAuto::Auto => Qty((*max as f32 * 0.75) as _), }; let config = ConfigInner { max, low_water }; /// Minimum low water. `const` so that overflows are compile-time. const MIN_LOW_WATER: usize = crate::mtracker::MAX_CACHE.as_usize() * MIN_MAX_PARTICIPANTS; let min_low_water = MIN_LOW_WATER; 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(IfEnabled::Enabled(config, enabled))) } } /// The total available memory in bytes. /// Loading Loading @@ -443,4 +449,61 @@ mod test { b.build().unwrap(); } } /// Test the logic that computes the `max` when configured as "auto". #[test] // We do some `1 * X` operations below for readability. #[allow(clippy::identity_op)] fn auto_max() { #[allow(unused)] fn check_helper(val: Qty, expected_32: Qty, expected_64: Qty) { assert_eq!(val, { cfg_if::cfg_if! { if #[cfg(target_pointer_width = "64")] { expected_64 } else if #[cfg(target_pointer_width = "32")] { expected_32 } else { panic!("Unsupported architecture :("); } } }); } check_helper( compute_max_from_total_system_mem(Err(MemQueryError::Unavailable)), /* 32-bit */ Qty(1 * 1024 * 1024 * 1024), /* 64-bit */ Qty(8 * 1024 * 1024 * 1024), ); check_helper( compute_max_from_total_system_mem(Ok(8 * 1024 * 1024 * 1024)), /* 32-bit */ Qty(2 * 1024 * 1024 * 1024), /* 64-bit */ Qty(3435973836), ); check_helper( compute_max_from_total_system_mem(Ok(7 * 1024 * 1024 * 1024)), /* 32-bit */ Qty(2 * 1024 * 1024 * 1024), /* 64-bit */ Qty(5637144576), ); check_helper( compute_max_from_total_system_mem(Ok(1 * 1024 * 1024 * 1024)), /* 32-bit */ Qty(805306368), /* 64-bit */ Qty(805306368), ); check_helper( compute_max_from_total_system_mem(Ok(7 * 1024)), /* 32-bit */ Qty(256 * 1024 * 1024), /* 64-bit */ Qty(256 * 1024 * 1024), ); check_helper( compute_max_from_total_system_mem(Ok(0)), /* 32-bit */ Qty(256 * 1024 * 1024), /* 64-bit */ Qty(256 * 1024 * 1024), ); check_helper( compute_max_from_total_system_mem(Ok(usize::MAX)), /* 32-bit */ Qty(2 * 1024 * 1024 * 1024), /* 64-bit */ Qty(8 * 1024 * 1024 * 1024), ); } }