1use std::{
2 ops::Deref,
3 sync::atomic::{AtomicI32, Ordering},
4};
5use thiserror::Error;
6
7static ACQUIRED_FD: AtomicI32 = AtomicI32::new(0);
8#[derive(Debug)]
9pub struct FDGuard(i32);
10
11impl FDGuard {
12 pub fn acquired(&self) -> i32 {
13 self.0
14 }
15}
16
17impl Deref for FDGuard {
18 type Target = i32;
19
20 fn deref(&self) -> &Self::Target {
21 &self.0
22 }
23}
24
25impl Drop for FDGuard {
26 fn drop(&mut self) {
27 ACQUIRED_FD.fetch_sub(self.0, Ordering::SeqCst); }
29}
30
31#[derive(Copy, Clone, Debug, PartialEq, Error)]
32#[error("Exceeded upper bound, acquired: {acquired}, limit: {limit}")]
33pub struct Error {
34 pub acquired: i32,
35 pub limit: i32,
36}
37
38pub fn acquire_guard(value: i32) -> Result<FDGuard, Error> {
39 loop {
40 let acquired = ACQUIRED_FD.load(Ordering::SeqCst); let limit = limit();
42 if acquired + value > limit {
43 return Err(Error { acquired, limit });
44 }
45 match ACQUIRED_FD.compare_exchange(acquired, acquired + value, Ordering::SeqCst, Ordering::SeqCst) {
47 Ok(_) => return Ok(FDGuard(value)),
48 Err(_) => continue, }
50 }
51}
52
53#[cfg(not(target_arch = "wasm32"))]
54pub fn try_set_fd_limit(limit: u64) -> std::io::Result<u64> {
55 cfg_if::cfg_if! {
56 if #[cfg(target_os = "windows")] {
57 rlimit::setmaxstdio(limit as u32).map(|v| v as u64)
58 } else if #[cfg(unix)] {
59 rlimit::increase_nofile_limit(limit)
60 }
61 }
62}
63
64pub fn limit() -> i32 {
65 cfg_if::cfg_if! {
66 if #[cfg(test)] {
67 100
68 }
69 else if #[cfg(target_os = "windows")] {
70 rlimit::getmaxstdio() as i32
71 }
72 else if #[cfg(unix)] {
73 rlimit::getrlimit(rlimit::Resource::NOFILE).unwrap().0 as i32
74 }
75 else {
76 512
77 }
78 }
79}
80
81pub fn remainder() -> i32 {
82 limit() - ACQUIRED_FD.load(Ordering::Relaxed)
83}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
88
89 #[test]
90 fn test_acquire_and_release_guards() {
91 let guard = acquire_guard(30).unwrap();
92 assert_eq!(guard.acquired(), 30);
93 assert_eq!(ACQUIRED_FD.load(Ordering::Relaxed), 30);
94
95 let err = acquire_guard(80).unwrap_err();
96 assert_eq!(err, Error { acquired: 30, limit: 100 });
97 assert_eq!(ACQUIRED_FD.load(Ordering::Relaxed), 30);
98
99 drop(guard);
100 assert_eq!(ACQUIRED_FD.load(Ordering::Relaxed), 0);
101
102 let guard = acquire_guard(100).unwrap();
103 assert_eq!(guard.acquired(), 100);
104 assert_eq!(ACQUIRED_FD.load(Ordering::Relaxed), 100);
105 drop(guard);
106 assert_eq!(ACQUIRED_FD.load(Ordering::Relaxed), 0);
107
108 let err = acquire_guard(101).unwrap_err();
109 assert_eq!(err, Error { acquired: 0, limit: 100 });
110 }
111}