diff --git a/benches/basic.rs b/benches/basic.rs index 5148d2911..e7646224b 100644 --- a/benches/basic.rs +++ b/benches/basic.rs @@ -118,17 +118,7 @@ fn bench_refresh_components(b: &mut test::Bencher) { let mut c = sysinfo::Components::new_with_refreshed_list(); b.iter(move || { - c.refresh(); - }); -} - -#[cfg(feature = "component")] -#[bench] -fn bench_refresh_components_list(b: &mut test::Bencher) { - let mut c = sysinfo::Components::new_with_refreshed_list(); - - b.iter(move || { - c.refresh_list(); + c.refresh(false); }); } diff --git a/examples/simple.rs b/examples/simple.rs index ee2250481..c35a181b8 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -173,7 +173,7 @@ fn interpret_input( } "refresh_components" => { writeln!(&mut io::stdout(), "Refreshing component list..."); - components.refresh_list(); + components.refresh(true); writeln!(&mut io::stdout(), "Done."); } "refresh_cpu" => { diff --git a/src/common/component.rs b/src/common/component.rs index 0c477144a..7a532c597 100644 --- a/src/common/component.rs +++ b/src/common/component.rs @@ -1,6 +1,5 @@ // Take a look at the license at the top of the repository in the LICENSE file. -use crate::utils::into_iter_mut; use crate::{ComponentInner, ComponentsInner}; /// Interacting with components. @@ -79,7 +78,7 @@ impl Components { /// use sysinfo::Components; /// /// let mut components = Components::new(); - /// components.refresh_list(); + /// components.refresh(false); /// for component in &components { /// println!("{component:?}"); /// } @@ -90,9 +89,8 @@ impl Components { } } - /// Creates a new [`Components`][crate::Components] type with the user list - /// loaded. It is a combination of [`Components::new`] and - /// [`Components::refresh_list`]. + /// Creates a new [`Components`][crate::Components] type with the components list + /// loaded. /// /// ```no_run /// use sysinfo::Components; @@ -104,7 +102,7 @@ impl Components { /// ``` pub fn new_with_refreshed_list() -> Self { let mut components = Self::new(); - components.refresh_list(); + components.refresh(true); components } @@ -137,44 +135,27 @@ impl Components { self.inner.list_mut() } - /// Refreshes the listed components' information. - /// - /// ⚠️ If a component is added or removed, this method won't take it into account. Use - /// [`Components::refresh_list`] instead. - /// - /// ⚠️ If you didn't call [`Components::refresh_list`] beforehand, this method will do - /// nothing as the component list will be empty. + /// Refreshes the components list. /// /// ```no_run /// use sysinfo::Components; /// /// let mut components = Components::new_with_refreshed_list(); /// // We wait some time...? - /// components.refresh(); + /// components.refresh(false); /// ``` - pub fn refresh(&mut self) { - #[cfg(all( - feature = "multithread", - not(feature = "unknown-ci"), - not(all(target_os = "macos", feature = "apple-sandbox")), - ))] - use rayon::iter::ParallelIterator; - into_iter_mut(self.list_mut()).for_each(|component| component.refresh()); - } - - /// The component list will be emptied then completely recomputed. - /// - /// ```no_run - /// use sysinfo::Components; - /// - /// let mut components = Components::new(); - /// components.refresh_list(); - /// ``` - /// - /// ⚠️ This function is not doing anything on macOS until - /// [this issue](https://github.com/GuillaumeGomez/sysinfo/issues/1279) is fixed. - pub fn refresh_list(&mut self) { - self.inner.refresh_list() + pub fn refresh(&mut self, remove_not_listed_components: bool) { + self.inner.refresh(); + if remove_not_listed_components { + // Remove interfaces which are gone. + self.inner.components.retain_mut(|c| { + if !c.inner.updated { + return false; + } + c.inner.updated = false; + true + }); + } } } @@ -300,7 +281,7 @@ mod tests { #[test] fn test_components_mac_m1() { let mut components = Components::new(); - components.refresh_list(); - components.refresh_list(); + components.refresh(false); + components.refresh(false); } } diff --git a/src/unix/apple/app_store/component.rs b/src/unix/apple/app_store/component.rs index e05aedc04..e7a999f95 100644 --- a/src/unix/apple/app_store/component.rs +++ b/src/unix/apple/app_store/component.rs @@ -2,7 +2,9 @@ use crate::Component; -pub(crate) struct ComponentInner; +pub(crate) struct ComponentInner { + pub(crate) updated: bool, +} impl ComponentInner { pub(crate) fn temperature(&self) -> f32 { @@ -25,7 +27,7 @@ impl ComponentInner { } pub(crate) struct ComponentsInner { - components: Vec, + pub(crate) components: Vec, } impl ComponentsInner { @@ -51,7 +53,7 @@ impl ComponentsInner { &mut self.components } - pub(crate) fn refresh_list(&mut self) { + pub(crate) fn refresh(&mut self) { // Doesn't do anything. } } diff --git a/src/unix/apple/macos/component/arm.rs b/src/unix/apple/macos/component/arm.rs index 6923e98ce..af5a82c7f 100644 --- a/src/unix/apple/macos/component/arm.rs +++ b/src/unix/apple/macos/component/arm.rs @@ -19,7 +19,7 @@ use crate::sys::utils::CFReleaser; use crate::Component; pub(crate) struct ComponentsInner { - components: Vec, + pub(crate) components: Vec, client: Option>, } @@ -51,9 +51,7 @@ impl ComponentsInner { } #[allow(unreachable_code)] - pub(crate) fn refresh_list(&mut self) { - self.components.clear(); - + pub(crate) fn refresh(&mut self) { unsafe { let matches = match CFReleaser::new(matching( kHIDPage_AppleVendor, @@ -103,12 +101,11 @@ impl ComponentsInner { continue; } - let name = match CFReleaser::new(IOHIDServiceClientCopyProperty( + let Some(name) = CFReleaser::new(IOHIDServiceClientCopyProperty( service as *const _, key_ref.inner(), - )) { - Some(n) => n, - None => continue, + )) else { + continue; }; let name_ptr = @@ -119,6 +116,16 @@ impl ComponentsInner { let name_str = CStr::from_ptr(name_ptr).to_string_lossy().to_string(); + if let Some(c) = self + .components + .iter_mut() + .find(|c| c.inner.label == name_str) + { + c.refresh(); + c.inner.updated = true; + continue; + } + let mut component = ComponentInner::new(name_str, None, None, service as *mut _); component.refresh(); @@ -134,6 +141,7 @@ pub(crate) struct ComponentInner { label: String, max: f32, critical: Option, + pub(crate) updated: bool, } unsafe impl Send for ComponentInner {} @@ -152,6 +160,7 @@ impl ComponentInner { max: max.unwrap_or(0.), critical, temperature: 0., + updated: true, } } diff --git a/src/unix/apple/macos/component/x86.rs b/src/unix/apple/macos/component/x86.rs index 51aca60a8..3621cbe09 100644 --- a/src/unix/apple/macos/component/x86.rs +++ b/src/unix/apple/macos/component/x86.rs @@ -46,7 +46,7 @@ impl ComponentFFI { // Used to get CPU information, not supported on iOS, or inside the default macOS sandbox. pub(crate) struct ComponentsInner { - components: Vec, + pub(crate) components: Vec, connection: Option, } @@ -77,20 +77,24 @@ impl ComponentsInner { &mut self.components } - pub(crate) fn refresh_list(&mut self) { - if let Some(ref connection) = self.connection { - let connection = connection.inner(); - self.components.clear(); - // getting CPU critical temperature - let critical_temp = - get_temperature(connection, &['T' as i8, 'C' as i8, '0' as i8, 'D' as i8, 0]); - - for (id, v) in COMPONENTS_TEMPERATURE_IDS.iter() { - if let Some(c) = - ComponentInner::new((*id).to_owned(), None, critical_temp, v, connection) - { - self.components.push(Component { inner: c }); - } + pub(crate) fn refresh(&mut self) { + let Some(ref connection) = self.connection else { + sysinfo_debug!("No connection to IoService, skipping components refresh"); + return; + }; + let connection = connection.inner(); + // getting CPU critical temperature + let critical_temp = + get_temperature(connection, &['T' as i8, 'C' as i8, '0' as i8, 'D' as i8, 0]); + + for (id, v) in COMPONENTS_TEMPERATURE_IDS.iter() { + if let Some(c) = self.components.iter_mut().find(|c| c.inner.label == *id) { + c.refresh(); + c.inner.updated = true; + } else if let Some(c) = + ComponentInner::new((*id).to_owned(), None, critical_temp, v, connection) + { + self.components.push(Component { inner: c }); } } } @@ -102,6 +106,7 @@ pub(crate) struct ComponentInner { critical: Option, label: String, ffi_part: ComponentFFI, + pub(crate) updated: bool, } impl ComponentInner { @@ -120,6 +125,7 @@ impl ComponentInner { max: max.unwrap_or(temperature), critical, ffi_part, + updated: true, }) } diff --git a/src/unix/freebsd/component.rs b/src/unix/freebsd/component.rs index ffaae2593..c849a7d70 100644 --- a/src/unix/freebsd/component.rs +++ b/src/unix/freebsd/component.rs @@ -8,6 +8,7 @@ pub(crate) struct ComponentInner { label: String, temperature: f32, max: f32, + pub(crate) updated: bool, } impl ComponentInner { @@ -51,7 +52,7 @@ unsafe fn refresh_component(id: &[u8]) -> Option { pub(crate) struct ComponentsInner { nb_cpus: usize, - components: Vec, + pub(crate) components: Vec, } impl ComponentsInner { @@ -82,22 +83,29 @@ impl ComponentsInner { &mut self.components } - pub(crate) fn refresh_list(&mut self) { - self.components.clear(); - for core in 0..self.nb_cpus { - unsafe { - let id = format!("dev.cpu.{core}.temperature\0").as_bytes().to_vec(); - if let Some(temperature) = refresh_component(&id) { - self.components.push(Component { - inner: ComponentInner { - id, - label: format!("CPU {}", core + 1), - temperature, - max: temperature, - }, - }); + pub(crate) fn refresh(&mut self) { + if self.components.len() != self.nb_cpus { + for core in 0..self.nb_cpus { + unsafe { + let id = format!("dev.cpu.{core}.temperature\0").as_bytes().to_vec(); + if let Some(temperature) = refresh_component(&id) { + self.components.push(Component { + inner: ComponentInner { + id, + label: format!("CPU {}", core + 1), + temperature, + max: temperature, + updated: true, + }, + }); + } } } + } else { + for c in self.components.iter_mut() { + c.refresh(); + c.inner.updated = true; + } } } } diff --git a/src/unix/linux/component.rs b/src/unix/linux/component.rs index 535080cc0..3c21d0ab9 100644 --- a/src/unix/linux/component.rs +++ b/src/unix/linux/component.rs @@ -33,14 +33,14 @@ pub(crate) struct ComponentInner { temperature: Option, /// Maximum value computed by `sysinfo`. max: Option, - /// Max threshold provided by the chip/kernel - /// - Read in:`temp[1-*]_max` - /// - Unit: read as millidegree Celsius converted to Celsius. - threshold_max: Option, - /// Min threshold provided by the chip/kernel. - /// - Read in:`temp[1-*]_min` - /// - Unit: read as millidegree Celsius converted to Celsius. - threshold_min: Option, + // /// Max threshold provided by the chip/kernel + // /// - Read in:`temp[1-*]_max` + // /// - Unit: read as millidegree Celsius converted to Celsius. + // threshold_max: Option, + // /// Min threshold provided by the chip/kernel. + // /// - Read in:`temp[1-*]_min` + // /// - Unit: read as millidegree Celsius converted to Celsius. + // threshold_min: Option, /// Critical threshold provided by the chip/kernel previous user write. /// Read in `temp[1-*]_crit`: /// Typically greater than corresponding temp_max values. @@ -62,8 +62,6 @@ pub(crate) struct ComponentInner { sensor_type: Option, /// Component Label /// - /// For formatting detail see `Component::label` function docstring. - /// /// ## Linux implementation details /// /// read n: `temp[1-*]_label` Suggested temperature channel label. @@ -73,7 +71,6 @@ pub(crate) struct ComponentInner { /// this temperature channel is being used for, and user-space /// doesn't. In all other cases, the label is provided by user-space. label: String, - // TODO: not used now. // Historical minimum temperature // - Read in:`temp[1-*]_lowest // - Unit: millidegree Celsius @@ -93,6 +90,39 @@ pub(crate) struct ComponentInner { input_file: Option, /// `temp[1-*]_highest file` to read if available highest value. highest_file: Option, + pub(crate) updated: bool, +} + +impl ComponentInner { + fn update_from( + &mut self, + Component { + inner: + ComponentInner { + temperature, + max, + input_file, + highest_file, + .. + }, + }: Component, + ) { + if let Some(temp) = temperature { + self.temperature = Some(temp); + } + match (max, self.max) { + (Some(new_max), Some(old_max)) => self.max = Some(new_max.max(old_max)), + (Some(max), None) => self.max = Some(max), + _ => {} + } + if input_file.is_some() && input_file != self.input_file { + self.input_file = input_file; + } + if highest_file.is_some() && highest_file != self.highest_file { + self.highest_file = highest_file; + } + self.updated = true; + } } // Read arbitrary data from sysfs. @@ -155,8 +185,7 @@ enum ThermalSensorType { AMDAMDSI, /// 6: Intel PECI IntelPECI, - /// Not all types are supported by all chips so we keep space for - /// unknown sensors. + /// Not all types are supported by all chips so we keep space for unknown sensors. #[allow(dead_code)] Unknown(u8), } @@ -199,8 +228,8 @@ fn fill_component(component: &mut ComponentInner, item: &str, folder: &Path, fil component.max = get_temperature_from_file(&hwmon_file).or(component.temperature); component.highest_file = Some(hwmon_file); } - "max" => component.threshold_max = get_temperature_from_file(&hwmon_file), - "min" => component.threshold_min = get_temperature_from_file(&hwmon_file), + // "max" => component.threshold_max = get_temperature_from_file(&hwmon_file), + // "min" => component.threshold_min = get_temperature_from_file(&hwmon_file), "crit" => component.threshold_critical = get_temperature_from_file(&hwmon_file), _ => { sysinfo_debug!( @@ -242,21 +271,19 @@ impl ComponentInner { let dir = read_dir(folder).ok()?; let mut matchings: HashMap = HashMap::with_capacity(10); for entry in dir.flatten() { - let Ok(file_type) = entry.file_type() else { - continue; - }; - if file_type.is_dir() { + if !entry.file_type().is_ok_and(|file_type| file_type.is_dir()) { continue; } let entry = entry.path(); let filename = entry.file_name().and_then(|x| x.to_str()).unwrap_or(""); - if !filename.starts_with("temp") { + let Some((id, item)) = filename + .strip_prefix("temp") + .and_then(|f| f.split_once('_')) + .and_then(|(id, item)| Some((id.parse::().ok()?, item))) + else { continue; - } - - let (id, item) = filename.split_once('_')?; - let id = id.get(4..)?.parse::().ok()?; + }; let component = matchings.entry(id).or_insert_with(|| Component { inner: ComponentInner::default(), @@ -268,20 +295,31 @@ impl ComponentInner { component.device_model = device_model; fill_component(component, item, folder, filename); } - let compo = matchings + for (id, mut new_comp) in matchings .into_iter() - .map(|(id, mut c)| { + // Remove components without `tempN_input` file termal. `Component` doesn't support this + // kind of sensors yet + .filter(|(_, c)| c.inner.input_file.is_some()) + { + if new_comp.inner.label.is_empty() { // sysinfo expose a generic interface with a `label`. // Problem: a lot of sensors don't have a label or a device model! ¯\_(ツ)_/¯ // So let's pretend we have a unique label! // See the table in `Component::label` documentation for the table detail. - c.inner.label = c.inner.format_label("temp", id); - c - }) - // Remove components without `tempN_input` file termal. `Component` doesn't support this kind of sensors yet - .filter(|c| c.inner.input_file.is_some()); + new_comp.inner.label = new_comp.inner.format_label("temp", id); + } + + if let Some(comp) = components + .iter_mut() + .find(|comp| comp.inner.label == new_comp.inner.label) + { + comp.inner.update_from(new_comp); + } else { + new_comp.inner.updated = true; + components.push(new_comp); + } + } - components.extend(compo); Some(()) } @@ -342,7 +380,7 @@ impl ComponentInner { } pub(crate) struct ComponentsInner { - components: Vec, + pub(crate) components: Vec, } impl ComponentsInner { @@ -368,8 +406,7 @@ impl ComponentsInner { &mut self.components } - pub(crate) fn refresh_list(&mut self) { - self.components.clear(); + pub(crate) fn refresh(&mut self) { if let Ok(dir) = read_dir(Path::new("/sys/class/hwmon/")) { for entry in dir.flatten() { let Ok(file_type) = entry.file_type() else { diff --git a/src/unknown/component.rs b/src/unknown/component.rs index e05aedc04..e7a999f95 100644 --- a/src/unknown/component.rs +++ b/src/unknown/component.rs @@ -2,7 +2,9 @@ use crate::Component; -pub(crate) struct ComponentInner; +pub(crate) struct ComponentInner { + pub(crate) updated: bool, +} impl ComponentInner { pub(crate) fn temperature(&self) -> f32 { @@ -25,7 +27,7 @@ impl ComponentInner { } pub(crate) struct ComponentsInner { - components: Vec, + pub(crate) components: Vec, } impl ComponentsInner { @@ -51,7 +53,7 @@ impl ComponentsInner { &mut self.components } - pub(crate) fn refresh_list(&mut self) { + pub(crate) fn refresh(&mut self) { // Doesn't do anything. } } diff --git a/src/utils.rs b/src/utils.rs index 67e859627..9d070dbd7 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -21,17 +21,17 @@ cfg_if! { val.into_par_iter() } - /// Converts the value into a parallel mutable iterator if the `multithread` feature is - /// enabled. Uses the `rayon::iter::IntoParallelRefMutIterator` trait. - #[cfg(feature = "component")] - pub(crate) fn into_iter_mut<'a, T>( - val: &'a mut T, - ) -> >::Iter - where - T: rayon::iter::IntoParallelRefMutIterator<'a> + ?Sized, - { - val.par_iter_mut() - } + // /// Converts the value into a parallel mutable iterator if the `multithread` feature is + // /// enabled. Uses the `rayon::iter::IntoParallelRefMutIterator` trait. + // #[cfg(feature = "component")] + // pub(crate) fn into_iter_mut<'a, T>( + // val: &'a mut T, + // ) -> >::Iter + // where + // T: rayon::iter::IntoParallelRefMutIterator<'a> + ?Sized, + // { + // val.par_iter_mut() + // } } else { /// Converts the value into a sequential iterator if the `multithread` feature is disabled. /// Uses the `std::iter::IntoIterator` trait. @@ -48,14 +48,14 @@ cfg_if! { // case, the `&mut` is already part of `T` and specifying it will result in the argument // being `&mut &mut T`. - /// Converts the value into a sequential mutable iterator if the `multithread` feature is - /// disabled. Uses the `std::iter::IntoIterator` trait. - #[cfg(feature = "component")] - pub(crate) fn into_iter_mut(val: T) -> T::IntoIter - where - T: IntoIterator, - { - val.into_iter() - } + // /// Converts the value into a sequential mutable iterator if the `multithread` feature is + // /// disabled. Uses the `std::iter::IntoIterator` trait. + // #[cfg(feature = "component")] + // pub(crate) fn into_iter_mut(val: T) -> T::IntoIter + // where + // T: IntoIterator, + // { + // val.into_iter() + // } } } diff --git a/src/windows/component.rs b/src/windows/component.rs index 38d43faed..9acd22232 100644 --- a/src/windows/component.rs +++ b/src/windows/component.rs @@ -26,6 +26,7 @@ pub(crate) struct ComponentInner { critical: Option, label: String, connection: Option, + pub(crate) updated: bool, } impl ComponentInner { @@ -44,6 +45,7 @@ impl ComponentInner { max: temperature, critical, connection: Some(c), + updated: true, }) } @@ -87,7 +89,7 @@ impl ComponentInner { } pub(crate) struct ComponentsInner { - components: Vec, + pub(crate) components: Vec, } impl ComponentsInner { @@ -113,11 +115,19 @@ impl ComponentsInner { &mut self.components } - pub(crate) fn refresh_list(&mut self) { - self.components = match ComponentInner::new() { - Some(c) => vec![Component { inner: c }], - None => Vec::new(), - }; + pub(crate) fn refresh(&mut self) { + if self.components.is_empty() { + self.components = match ComponentInner::new() { + Some(c) => vec![Component { inner: c }], + None => Vec::new(), + }; + } else { + // There should always be only one here but just in case... + for c in self.components.iter_mut() { + c.refresh(); + c.inner.updated = true; + } + } } } diff --git a/tests/components.rs b/tests/components.rs index 63ea0fec1..47d8e7125 100644 --- a/tests/components.rs +++ b/tests/components.rs @@ -13,6 +13,6 @@ fn test_components() { return; } - c.refresh_list(); + c.refresh(false); assert!(!c.is_empty()); }