Skip to main content

tuwunel_core/utils/sys/
limits.rs

1#[cfg(unix)]
2use nix::sys::resource::{Resource, getrlimit};
3use nix::unistd::{SysconfVar, sysconf};
4
5use crate::{Result, apply, debug, utils::math::ExpectInto};
6
7#[cfg(unix)]
8/// This is needed for opening lots of file descriptors, which tends to
9/// happen more often when using RocksDB and making lots of federation
10/// connections at startup. The soft limit is usually 1024, and the hard
11/// limit is usually 512000; I've personally seen it hit >2000.
12///
13/// * <https://www.freedesktop.org/software/systemd/man/systemd.exec.html#id-1.12.2.1.17.6>
14/// * <https://github.com/systemd/systemd/commit/0abf94923b4a95a7d89bc526efc84e7ca2b71741>
15pub fn maximize_fd_limit() -> Result {
16	use nix::sys::resource::setrlimit;
17
18	let (soft_limit, hard_limit) = max_file_descriptors()?;
19	if soft_limit < hard_limit {
20		let new_limit = hard_limit.try_into()?;
21		setrlimit(Resource::RLIMIT_NOFILE, new_limit, new_limit)?;
22		assert_eq!((hard_limit, hard_limit), max_file_descriptors()?, "getrlimit != setrlimit");
23		debug!(to = hard_limit, from = soft_limit, "Raised RLIMIT_NOFILE");
24	}
25
26	Ok(())
27}
28
29#[cfg(not(unix))]
30pub fn maximize_fd_limit() -> Result { Ok(()) }
31
32#[cfg(all(unix, not(target_os = "macos")))]
33/// Some distributions ship with very low defaults for thread counts; similar to
34/// low default file descriptor limits. But unlike fd's, thread limit is rarely
35/// reached, though on large systems (32+ cores) shipping with defaults of
36/// ~1024 as have been observed are problematic.
37pub fn maximize_thread_limit() -> Result {
38	use nix::sys::resource::setrlimit;
39
40	let (soft_limit, hard_limit) = max_threads()?;
41	if soft_limit < hard_limit {
42		let new_limit = hard_limit.try_into()?;
43		setrlimit(Resource::RLIMIT_NPROC, new_limit, new_limit)?;
44		assert_eq!((hard_limit, hard_limit), max_threads()?, "getrlimit != setrlimit");
45		debug!(to = hard_limit, from = soft_limit, "Raised RLIMIT_NPROC");
46	}
47
48	Ok(())
49}
50
51#[cfg(any(not(unix), target_os = "macos"))]
52pub fn maximize_thread_limit() -> Result { Ok(()) }
53
54#[cfg(unix)]
55#[inline]
56pub fn max_file_descriptors() -> Result<(usize, usize)> {
57	getrlimit(Resource::RLIMIT_NOFILE)
58		.map(apply!(2, ExpectInto::expect_into))
59		.map_err(Into::into)
60}
61
62#[cfg(not(unix))]
63#[inline]
64pub fn max_file_descriptors() -> Result<(usize, usize)> { Ok((usize::MAX, usize::MAX)) }
65
66#[cfg(unix)]
67#[inline]
68pub fn max_stack_size() -> Result<(usize, usize)> {
69	getrlimit(Resource::RLIMIT_STACK)
70		.map(apply!(2, ExpectInto::expect_into))
71		.map_err(Into::into)
72}
73
74#[cfg(not(unix))]
75#[inline]
76pub fn max_stack_size() -> Result<(usize, usize)> { Ok((usize::MAX, usize::MAX)) }
77
78#[cfg(all(unix, not(target_os = "macos")))]
79#[inline]
80pub fn max_memory_locked() -> Result<(usize, usize)> {
81	getrlimit(Resource::RLIMIT_MEMLOCK)
82		.map(apply!(2, ExpectInto::expect_into))
83		.map_err(Into::into)
84}
85
86#[cfg(any(not(unix), target_os = "macos"))]
87#[inline]
88pub fn max_memory_locked() -> Result<(usize, usize)> { Ok((usize::MIN, usize::MIN)) }
89
90#[cfg(all(unix, not(target_os = "macos")))]
91#[inline]
92pub fn max_threads() -> Result<(usize, usize)> {
93	getrlimit(Resource::RLIMIT_NPROC)
94		.map(apply!(2, ExpectInto::expect_into))
95		.map_err(Into::into)
96}
97
98#[cfg(any(not(unix), target_os = "macos"))]
99#[inline]
100pub fn max_threads() -> Result<(usize, usize)> { Ok((usize::MAX, usize::MAX)) }
101
102/// Get the system's page size in bytes.
103#[inline]
104pub fn page_size() -> Result<usize> {
105	sysconf(SysconfVar::PAGE_SIZE)?
106		.unwrap_or(-1)
107		.try_into()
108		.map_err(Into::into)
109}