Skip to main content

system_memory/
lib.rs

1//! A small crate that resolves the total system memory of the host. This is useful for many projects that
2//! may behave differently depending on how much memory the host system has and how much is available.
3//!
4//! Be aware of the potential for data races when using this code -- since the amount of available system memory may (
5//! and likely will) change between calls, repeated use of the function even on the same thread cannot be expected to
6//! return the same values, nor will [`available`] necessarily return values consistent with [`used`], since the value
7//! may change between calls.
8
9#![deny(missing_copy_implementations, missing_debug_implementations)]
10#![deny(rustdoc::broken_intra_doc_links)]
11#![deny(clippy::cast_possible_truncation)]
12#![warn(missing_docs)]
13#![no_std]
14// Compiler directive to get docs.rs (which uses the nightly version of the rust compiler) to show
15// info about feature required for various modules and functionality.
16//
17// See: <https://stackoverflow.com/a/70914430>.
18#![cfg_attr(all(doc, CHANNEL_NIGHTLY), feature(doc_cfg))]
19
20#[cfg(windows)]
21pub mod windows;
22
23#[cfg(any(target_os = "macos", target_os = "ios"))]
24pub mod macos;
25
26#[cfg(target_os = "linux")]
27pub mod linux;
28
29/// Snapshot of the host's memory stats.
30#[derive(Debug, Clone, Copy)]
31pub struct Snapshot {
32    /// Total number of bytes of physical memory on the host system.
33    pub total: u64,
34
35    /// Number of bytes of available physical memory on the host system.
36    pub available: u64,
37}
38
39impl Snapshot {
40    /// Try to get a snapshot of the state of the system's memory
41    #[cfg(any(windows, target_os = "linux", target_os = "macos", target_os = "ios"))]
42    #[allow(unreachable_code)]
43    pub fn get() -> Result<Self, Option<errno::Errno>> {
44        #[cfg(windows)] {
45            let mem_status = windows::populate_mem_status()?;
46
47            return Ok(Self {
48                total: mem_status.ullTotalPhys,
49                available: mem_status.ullAvailPhys,
50            });
51        }
52
53        // sysinfo.totalram is a C unsigned long, which is a u32 on some targets.
54        // cast to u64 just to be sure.
55        #[cfg(target_os = "linux")] {
56            let sysinfo = linux::populate_sysinfo()?;
57
58            let total = sysinfo.totalram as _;
59            let available = sysinfo.freeram as _;
60
61            return Ok(Self {
62                total,
63                available
64            });
65        }
66
67        #[cfg(any(target_os = "macos", target_os = "ios"))] {
68            let total_memory = macos::try_get_total_physical_memory()?;
69            let page_size = macos::page_size()?;
70            let vm_stats = macos::vm_statistics()
71                .map_err(|errno| if errno.0 == 0 { None } else { Some(errno) })?;
72
73            return Ok(Self {
74                total: total_memory,
75                // This is how heim calculates it so we will too -- I wish macOS
76                // had better docs for this.
77                available: (vm_stats.active_count + vm_stats.free_count) as u64 * page_size,
78            });
79        }
80
81        unreachable!("This function should have already hit a CFG and returned");
82    }
83
84    /// Get the number of bytes of memory in use at the time of this snapshot.
85    pub fn in_use(&self) -> u64 {
86        self.total - self.available
87    }
88}
89
90
91#[cfg(any(windows, target_os = "linux", target_os = "macos", target_os = "ios"))]
92#[inline]
93fn get_snapshot() -> Snapshot {
94    Snapshot::get().expect("failed to query system for memory stats")
95}
96
97/// Get the total number of bytes of physical memory on this host.
98///
99/// # Panics
100/// This function may panic if any of the underlying platform-specific syscalls fail.
101#[cfg(any(windows, target_os = "linux", target_os = "macos", target_os = "ios"))]
102pub fn total() -> u64 {
103    get_snapshot().total
104}
105
106/// Get the number of bytes of available physical memory on this host.
107///
108/// # Panics
109/// This function may panic if any of the underlying platform-specific syscalls fail.
110#[cfg(any(windows, target_os = "linux", target_os = "macos", target_os = "ios"))]
111pub fn available() -> u64 {
112    get_snapshot().available
113}
114
115/// Get the number of bytes of physical memory currently in use.
116///
117/// # Panics
118/// This function may panic if any of the underlying platform-specific syscalls fail.
119#[cfg(any(windows, target_os = "linux", target_os = "macos", target_os = "ios"))]
120pub fn used() -> u64 {
121    get_snapshot().in_use()
122}
123
124#[cfg(test)]
125mod tests {
126    extern crate std;
127    use std::println;
128
129    #[test]
130    fn get_total_system_memory() {
131        println!(
132            "Total system memory: {:.2} GiB",
133            super::total() as f64 / 1024f64 / 1024f64 / 1024f64
134        );
135        println!(
136            "Available system memory: {:.2} GiB",
137            super::available() as f64 / 1024f64 / 1024f64 / 1024f64
138        );
139
140        // This may not always assert successfully -- there's a race condition here if the amount of available memory
141        // changes after the call to `super::used`.
142        assert_eq!(super::used(), super::total() - super::available());
143    }
144}