Repository: GuillaumeGomez/sysinfo
Pull request #1169 (2023-12-17 15:39:00)

Make Process::tasks method available on all platforms and change its return type to `Option`
Follow-up of https://github.com/GuillaumeGomez/sysinfo/pull/1168.


Patch:
diff --git a/src/c_interface.rs b/src/c_interface.rs
index 0e4d48f..0ca8cea 100644
--- a/src/c_interface.rs
+++ b/src/c_interface.rs
@@ -21,7 +21,6 @@ pub type CProcess = *const c_void;
 pub type RString = *const c_char;
 /// Callback used by [`processes`][crate::System#method.processes].
 pub type ProcessLoop = extern "C" fn(pid: PID, process: CProcess, data: *mut c_void) -> bool;
-#[cfg(target_os = "linux")]
 /// Callback used by [`tasks`][crate::Process#method.tasks].
 pub type ProcessPidLoop = extern "C" fn(pid: PID, data: *mut c_void) -> bool;
 /// Equivalent of [`Networks`][crate::Networks] struct.
@@ -102,7 +101,6 @@ pub extern "C" fn sysinfo_refresh_processes(system: CSystem) {
 }
 
 /// Equivalent of [`System::refresh_process()`][crate::System#method.refresh_process].
-#[cfg(target_os = "linux")]
 #[no_mangle]
 pub extern "C" fn sysinfo_refresh_process(system: CSystem, pid: PID) {
     assert!(!system.is_null());
@@ -394,7 +392,6 @@ pub extern "C" fn sysinfo_process_by_pid(system: CSystem, pid: PID) -> CProcess
 /// # ⚠️ WARNING ⚠️
 ///
 /// While having this method processes, you should *never* call any refresh method!
-#[cfg(target_os = "linux")]
 #[no_mangle]
 pub extern "C" fn sysinfo_process_tasks(
     process: CProcess,
@@ -405,12 +402,16 @@ pub extern "C" fn sysinfo_process_tasks(
     if let Some(fn_pointer) = fn_pointer {
         unsafe {
             let process = process as *const Process;
-            for pid in (*process).tasks().iter() {
-                if !fn_pointer(pid.0, data) {
-                    break;
+            if let Some(tasks) = (*process).tasks() {
+                for pid in tasks {
+                    if !fn_pointer(pid.0, data) {
+                        break;
+                    }
                 }
+                tasks.len() as size_t
+            } else {
+                0
             }
-            (*process).tasks().len() as size_t
         }
     } else {
         0
diff --git a/src/common.rs b/src/common.rs
index 9c9e95e..a233c5f 100644
--- a/src/common.rs
+++ b/src/common.rs
@@ -6,7 +6,7 @@ use crate::{
 };
 
 use std::cmp::Ordering;
-use std::collections::HashMap;
+use std::collections::{HashMap, HashSet};
 use std::convert::{From, TryFrom};
 use std::ffi::OsStr;
 use std::fmt;
@@ -802,7 +802,7 @@ impl Process {
     /// let s = System::new_all();
     /// if let Some(process) = s.process(Pid::from(1337)) {
     ///     if process.kill_with(Signal::Kill).is_none() {
-    ///         eprintln!("This signal isn't supported on this platform");
+    ///         println!("This signal isn't supported on this platform");
     ///     }
     /// }
     /// ```
@@ -1102,7 +1102,7 @@ impl Process {
     /// let mut s = System::new_all();
     ///
     /// if let Some(process) = s.process(Pid::from(1337)) {
-    ///     eprintln!("User id for process 1337: {:?}", process.user_id());
+    ///     println!("User id for process 1337: {:?}", process.user_id());
     /// }
     /// ```
     pub fn user_id(&self) -> Option<&Uid> {
@@ -1125,7 +1125,7 @@ impl Process {
     /// let mut s = System::new_all();
     ///
     /// if let Some(process) = s.process(Pid::from(1337)) {
-    ///     eprintln!("User id for process 1337: {:?}", process.effective_user_id());
+    ///     println!("User id for process 1337: {:?}", process.effective_user_id());
     /// }
     /// ```
     pub fn effective_user_id(&self) -> Option<&Uid> {
@@ -1142,7 +1142,7 @@ impl Process {
     /// let mut s = System::new_all();
     ///
     /// if let Some(process) = s.process(Pid::from(1337)) {
-    ///     eprintln!("Group ID for process 1337: {:?}", process.group_id());
+    ///     println!("Group ID for process 1337: {:?}", process.group_id());
     /// }
     /// ```
     pub fn group_id(&self) -> Option<Gid> {
@@ -1163,7 +1163,7 @@ impl Process {
     /// let mut s = System::new_all();
     ///
     /// if let Some(process) = s.process(Pid::from(1337)) {
-    ///     eprintln!("User id for process 1337: {:?}", process.effective_group_id());
+    ///     println!("User id for process 1337: {:?}", process.effective_group_id());
     /// }
     /// ```
     pub fn effective_group_id(&self) -> Option<Gid> {
@@ -1178,9 +1178,9 @@ impl Process {
     /// let mut s = System::new_all();
     ///
     /// if let Some(process) = s.process(Pid::from(1337)) {
-    ///     eprintln!("Waiting for pid 1337");
+    ///     println!("Waiting for pid 1337");
     ///     process.wait();
-    ///     eprintln!("Pid 1337 exited");
+    ///     println!("Pid 1337 exited");
     /// }
     /// ```
     pub fn wait(&self) {
@@ -1198,22 +1198,44 @@ impl Process {
     /// let mut s = System::new_all();
     ///
     /// if let Some(process) = s.process(Pid::from(1337)) {
-    ///     eprintln!("Session ID for process 1337: {:?}", process.session_id());
+    ///     println!("Session ID for process 1337: {:?}", process.session_id());
     /// }
     /// ```
     pub fn session_id(&self) -> Option<Pid> {
         self.inner.session_id()
     }
 
-    /// Tasks run by this process.
+    /// Tasks run by this process. If there are none, returns `None`.
     ///
-    /// ⚠️ This method is only available on Linux.
-    #[cfg(all(
-        any(target_os = "linux", target_os = "android"),
-        not(feature = "unknown-ci")
-    ))]
-    pub fn tasks(&self) -> &std::collections::HashSet<Pid> {
-        &self.inner.tasks
+    /// ⚠️ This method always returns `None` on other plantforms than Linux.
+    ///
+    /// ```no_run
+    /// use sysinfo::{Pid, System};
+    ///
+    /// let mut s = System::new_all();
+    ///
+    /// if let Some(process) = s.process(Pid::from(1337)) {
+    ///     if let Some(tasks) = process.tasks() {
+    ///         println!("Listing tasks for process {:?}", process.pid());
+    ///         for task_pid in tasks {
+    ///             if let Some(task) = s.process(*task_pid) {
+    ///                 println!("Task {:?}: {:?}", task.pid(), task.name());
+    ///             }
+    ///         }
+    ///     }
+    /// }
+    /// ```
+    pub fn tasks(&self) -> Option<&HashSet<Pid>> {
+        cfg_if::cfg_if! {
+            if #[cfg(all(
+                any(target_os = "linux", target_os = "android"),
+                not(feature = "unknown-ci")
+            ))] {
+                self.inner.tasks.as_ref()
+            } else {
+                None
+            }
+        }
     }
 }
 
@@ -1807,7 +1829,7 @@ impl Networks {
     ///
     /// let networks = Networks::new_with_refreshed_list();
     /// for network in &networks {
-    ///     eprintln!("{network:?}");
+    ///     println!("{network:?}");
     /// }
     /// ```
     pub fn new_with_refreshed_list() -> Self {
@@ -1823,7 +1845,7 @@ impl Networks {
     ///
     /// let networks = Networks::new_with_refreshed_list();
     /// for network in networks.list() {
-    ///     eprintln!("{network:?}");
+    ///     println!("{network:?}");
     /// }
     /// ```
     pub fn list(&self) -> &HashMap<String, NetworkData> {
@@ -2266,7 +2288,7 @@ impl Disks {
     /// let mut disks = Disks::new();
     /// disks.refresh_list();
     /// for disk in disks.list() {
-    ///     eprintln!("{disk:?}");
+    ///     println!("{disk:?}");
     /// }
     /// ```
     pub fn new() -> Self {
@@ -2283,7 +2305,7 @@ impl Disks {
     ///
     /// let mut disks = Disks::new_with_refreshed_list();
     /// for disk in disks.list() {
-    ///     eprintln!("{disk:?}");
+    ///     println!("{disk:?}");
     /// }
     /// ```
     pub fn new_with_refreshed_list() -> Self {
@@ -2299,7 +2321,7 @@ impl Disks {
     ///
     /// let disks = Disks::new_with_refreshed_list();
     /// for disk in disks.list() {
-    ///     eprintln!("{disk:?}");
+    ///     println!("{disk:?}");
     /// }
     /// ```
     pub fn list(&self) -> &[Disk] {
@@ -2314,7 +2336,7 @@ impl Disks {
     /// let mut disks = Disks::new_with_refreshed_list();
     /// for disk in disks.list_mut() {
     ///     disk.refresh();
-    ///     eprintln!("{disk:?}");
+    ///     println!("{disk:?}");
     /// }
     /// ```
     pub fn list_mut(&mut self) -> &mut [Disk] {
@@ -2486,7 +2508,7 @@ impl Users {
     /// let mut users = Users::new();
     /// users.refresh_list();
     /// for user in users.list() {
-    ///     eprintln!("{user:?}");
+    ///     println!("{user:?}");
     /// }
     /// ```
     pub fn new() -> Self {
@@ -2501,7 +2523,7 @@ impl Users {
     ///
     /// let mut users = Users::new_with_refreshed_list();
     /// for user in users.list() {
-    ///     eprintln!("{user:?}");
+    ///     println!("{user:?}");
     /// }
     /// ```
     pub fn new_with_refreshed_list() -> Self {
@@ -2517,7 +2539,7 @@ impl Users {
     ///
     /// let users = Users::new_with_refreshed_list();
     /// for user in users.list() {
-    ///     eprintln!("{user:?}");
+    ///     println!("{user:?}");
     /// }
     /// ```
     pub fn list(&self) -> &[User] {
@@ -2573,7 +2595,7 @@ impl Users {
     ///
     /// if let Some(process) = s.process(Pid::from(1337)) {
     ///     if let Some(user_id) = process.user_id() {
-    ///         eprintln!("User for process 1337: {:?}", users.get_user_by_id(user_id));
+    ///         println!("User for process 1337: {:?}", users.get_user_by_id(user_id));
     ///     }
     /// }
     /// ```
@@ -3158,7 +3180,7 @@ pub enum ProcessStatus {
 ///         println!("current pid: {}", pid);
 ///     }
 ///     Err(e) => {
-///         eprintln!("failed to get current pid: {}", e);
+///         println!("failed to get current pid: {}", e);
 ///     }
 /// }
 /// ```
@@ -3228,7 +3250,7 @@ impl fmt::Display for MacAddr {
 ///
 /// let components = Components::new_with_refreshed_list();
 /// for component in &components {
-///     eprintln!("{component:?}");
+///     println!("{component:?}");
 /// }
 /// ```
 pub struct Components {
@@ -3299,7 +3321,7 @@ impl Components {
     /// let mut components = Components::new();
     /// components.refresh_list();
     /// for component in &components {
-    ///     eprintln!("{component:?}");
+    ///     println!("{component:?}");
     /// }
     /// ```
     pub fn new() -> Self {
@@ -3317,7 +3339,7 @@ impl Components {
     ///
     /// let mut components = Components::new_with_refreshed_list();
     /// for component in components.list() {
-    ///     eprintln!("{component:?}");
+    ///     println!("{component:?}");
     /// }
     /// ```
     pub fn new_with_refreshed_list() -> Self {
@@ -3333,7 +3355,7 @@ impl Components {
     ///
     /// let components = Components::new_with_refreshed_list();
     /// for component in components.list() {
-    ///     eprintln!("{component:?}");
+    ///     println!("{component:?}");
     /// }
     /// ```
     pub fn list(&self) -> &[Component] {
@@ -3348,7 +3370,7 @@ impl Components {
     /// let mut components = Components::new_with_refreshed_list();
     /// for component in components.list_mut() {
     ///     component.refresh();
-    ///     eprintln!("{component:?}");
+    ///     println!("{component:?}");
     /// }
     /// ```
     pub fn list_mut(&mut self) -> &mut [Component] {
diff --git a/src/sysinfo.h b/src/sysinfo.h
index e70b828..279714c 100644
--- a/src/sysinfo.h
+++ b/src/sysinfo.h
@@ -28,9 +28,7 @@ void        sysinfo_refresh_memory(CSystem system);
 void        sysinfo_refresh_cpu(CSystem system);
 void        sysinfo_refresh_all(CSystem system);
 void        sysinfo_refresh_processes(CSystem system);
-#ifdef __linux__
 void        sysinfo_refresh_process(CSystem system, PID pid);
-#endif
 
 CDisks      sysinfo_disks_init(void);
 void        sysinfo_disks_destroy(CDisks disks);
@@ -48,10 +46,8 @@ void        sysinfo_cpus_usage(CSystem system, unsigned int *length, float **cpu
 
 size_t      sysinfo_processes(CSystem system, bool (*fn_pointer)(PID, CProcess, void*),
                               void *data);
-#ifdef __linux__
 size_t      sysinfo_process_tasks(CProcess process, bool (*fn_pointer)(PID, void*),
                                   void *data);
-#endif
 CProcess    sysinfo_process_by_pid(CSystem system, PID pid);
 PID         sysinfo_process_pid(CProcess process);
 PID         sysinfo_process_parent_pid(CProcess process);
diff --git a/src/unix/linux/process.rs b/src/unix/linux/process.rs
index 5b9d43f..5f3000d 100644
--- a/src/unix/linux/process.rs
+++ b/src/unix/linux/process.rs
@@ -111,7 +111,7 @@ pub(crate) struct ProcessInner {
     group_id: Option<Gid>,
     effective_group_id: Option<Gid>,
     pub(crate) status: ProcessStatus,
-    pub(crate) tasks: HashSet<Pid>,
+    pub(crate) tasks: Option<HashSet<Pid>>,
     pub(crate) stat_file: Option<FileCounter>,
     old_read_bytes: u64,
     old_written_bytes: u64,
@@ -146,7 +146,7 @@ impl ProcessInner {
             group_id: None,
             effective_group_id: None,
             status: ProcessStatus::Unknown(0),
-            tasks: HashSet::new(),
+            tasks: None,
             stat_file: None,
             old_read_bytes: 0,
             old_written_bytes: 0,
@@ -608,7 +608,7 @@ fn update_time_and_memory(
 struct ProcAndTasks {
     pid: Pid,
     path: PathBuf,
-    tasks: HashSet<Pid>,
+    tasks: Option<HashSet<Pid>>,
 }
 
 fn get_all_pid_entries(
@@ -630,8 +630,8 @@ fn get_all_pid_entries(
     let pid = Pid::from(usize::from_str(&name.to_string_lossy()).ok()?);
 
     let tasks_dir = Path::join(&entry, "task");
-    let mut tasks = HashSet::new();
-    if tasks_dir.is_dir() {
+    let tasks = if tasks_dir.is_dir() {
+        let mut tasks = HashSet::new();
         if let Ok(entries) = fs::read_dir(tasks_dir) {
             for task in entries
                 .into_iter()
@@ -640,7 +640,10 @@ fn get_all_pid_entries(
                 tasks.insert(task);
             }
         }
-    }
+        Some(tasks)
+    } else {
+        None
+    };
     data.push(ProcAndTasks {
         pid,
         path: entry,


Test patch:
diff --git a/tests/process.rs b/tests/process.rs
index ad65733..9f1502b 100644
--- a/tests/process.rs
+++ b/tests/process.rs
@@ -393,10 +393,15 @@ fn test_refresh_tasks() {
     let mut s = System::new();
     s.refresh_processes();
 
-    assert!(s.process(pid).unwrap().tasks().iter().any(|task_pid| s
-        .process(*task_pid)
-        .map(|task| task.name() == task_name)
-        .unwrap_or(false)));
+    assert!(s
+        .process(pid)
+        .unwrap()
+        .tasks()
+        .map(|tasks| tasks.iter().any(|task_pid| s
+            .process(*task_pid)
+            .map(|task| task.name() == task_name)
+            .unwrap_or(false)))
+        .unwrap_or(false));
     assert!(s.processes_by_exact_name(task_name).next().is_some());
 
     // Let's give some time to the system to clean up...
@@ -404,10 +409,15 @@ fn test_refresh_tasks() {
 
     s.refresh_processes();
 
-    assert!(!s.process(pid).unwrap().tasks().iter().any(|task_pid| s
-        .process(*task_pid)
-        .map(|task| task.name() == task_name)
-        .unwrap_or(false)));
+    assert!(!s
+        .process(pid)
+        .unwrap()
+        .tasks()
+        .map(|tasks| tasks.iter().any(|task_pid| s
+            .process(*task_pid)
+            .map(|task| task.name() == task_name)
+            .unwrap_or(false)))
+        .unwrap_or(false));
     assert!(s.processes_by_exact_name(task_name).next().is_none());
 }