1#![allow(unsafe_code)]
2
3#[cfg(any(target_os = "linux", target_os = "macos"))]
4use std::io;
5#[cfg(any(target_os = "linux"))]
6use {std::fs::File, std::io::Read};
7
8#[cfg(target_os = "linux")]
15fn get_cgroup_memory_limit() -> io::Result<u64> {
16 File::open("/sys/fs/cgroup/memory/memory.limit_in_bytes")
17 .and_then(read_u64_from)
18}
19
20#[cfg(target_os = "linux")]
21fn read_u64_from(mut file: File) -> io::Result<u64> {
22 let mut s = String::new();
23 file.read_to_string(&mut s).and_then(|_| {
24 s.trim()
25 .parse()
26 .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
27 })
28}
29
30#[cfg(any(target_os = "linux", target_os = "macos"))]
34fn get_rlimit_as() -> io::Result<libc::rlimit> {
35 let mut limit = std::mem::MaybeUninit::<libc::rlimit>::uninit();
36
37 let ret = unsafe { libc::getrlimit(libc::RLIMIT_AS, limit.as_mut_ptr()) };
38
39 if ret == 0 {
40 Ok(unsafe { limit.assume_init() })
41 } else {
42 Err(io::Error::last_os_error())
43 }
44}
45
46#[cfg(any(target_os = "linux", target_os = "macos"))]
47pub fn get_available_memory() -> io::Result<u64> {
48 use std::convert::TryFrom;
49
50 let pages = unsafe { libc::sysconf(libc::_SC_PHYS_PAGES) };
51 if pages == -1 {
52 return Err(io::Error::last_os_error());
53 }
54
55 let page_size = unsafe { libc::sysconf(libc::_SC_PAGE_SIZE) };
56 if page_size == -1 {
57 return Err(io::Error::last_os_error());
58 }
59
60 Ok(u64::try_from(pages).unwrap() * u64::try_from(page_size).unwrap())
61}
62
63pub fn get_memory_limit() -> u64 {
64 static MAX_USIZE: u64 = usize::max_value() as u64;
66
67 let mut max: u64 = 0;
68
69 #[cfg(target_os = "linux")]
70 {
71 if let Ok(mem) = get_cgroup_memory_limit() {
72 max = mem;
73 }
74
75 if max > 0x7FFF_FFFF_0000_0000 {
81 return 0;
82 }
83 }
84
85 #[cfg(any(target_os = "linux", target_os = "macos"))]
86 {
87 if let Ok(rlim) = get_rlimit_as() {
88 let rlim_cur = Into::<u64>::into(rlim.rlim_cur);
89 if rlim_cur < max || max == 0 {
90 max = rlim_cur;
91 }
92 }
93
94 if let Ok(available) = get_available_memory() {
95 if available < max || max == 0 {
96 max = available;
97 }
98 }
99 }
100
101 if max > MAX_USIZE {
102 max = MAX_USIZE;
106 }
107
108 #[cfg(miri)]
109 {
110 max /= 40;
117 }
118
119 max
120}