1use std::collections::HashMap;
17use std::sync::{Arc, Mutex};
18use std::time::{Duration, Instant};
19
20use crate::{CacheStore, PidNode, ProcInfo, TreeStore};
21
22struct Entry<V> {
25 value: V,
26 inserted_at: Instant,
27}
28
29impl<V: Clone> Clone for Entry<V> {
30 fn clone(&self) -> Self {
31 Self {
32 value: self.value.clone(),
33 inserted_at: self.inserted_at,
34 }
35 }
36}
37
38type Inner<V> = Arc<Mutex<HashMap<u32, Entry<V>>>>;
41
42fn get_inner<V: Clone>(inner: &Inner<V>, pid: u32, ttl: Duration) -> Option<V> {
43 let mut map = inner.lock().unwrap();
44 let entry = map.get(&pid)?;
45 if !ttl.is_zero() && entry.inserted_at.elapsed() >= ttl {
46 map.remove(&pid);
47 return None;
48 }
49 Some(entry.value.clone())
50}
51
52fn insert_inner<V: Clone>(inner: &Inner<V>, pid: u32, value: V) {
53 let mut map = inner.lock().unwrap();
54 map.insert(
55 pid,
56 Entry {
57 value,
58 inserted_at: Instant::now(),
59 },
60 );
61}
62
63fn len_inner<V>(inner: &Inner<V>) -> usize {
64 inner.lock().unwrap().len()
65}
66
67pub struct DefaultStore<V> {
73 inner: Inner<V>,
74 ttl: Duration,
75}
76
77pub type DefaultTree = DefaultStore<PidNode>;
79
80pub type DefaultCache = DefaultStore<ProcInfo>;
82
83impl<V: Clone> DefaultStore<V> {
84 pub fn new(_capacity: u64, ttl_secs: u64) -> Self {
87 Self {
88 inner: Arc::new(Mutex::new(HashMap::new())),
89 ttl: Duration::from_secs(ttl_secs),
90 }
91 }
92
93 pub fn len(&self) -> usize {
95 len_inner(&self.inner)
96 }
97
98 pub fn is_empty(&self) -> bool {
100 self.len() == 0
101 }
102
103 pub fn contains_key(&self, pid: u32) -> bool {
105 get_inner(&self.inner, pid, self.ttl).is_some()
106 }
107}
108
109impl<V: Clone> Clone for DefaultStore<V> {
110 fn clone(&self) -> Self {
111 Self {
112 inner: Arc::clone(&self.inner),
113 ttl: self.ttl,
114 }
115 }
116}
117
118impl<V: Clone> Default for DefaultStore<V> {
119 fn default() -> Self {
121 Self::new(100, 0)
122 }
123}
124
125impl TreeStore for DefaultTree {
126 fn get_node(&self, pid: u32) -> Option<PidNode> {
127 get_inner(&self.inner, pid, self.ttl)
128 }
129
130 fn insert_node(&self, pid: u32, node: PidNode) {
131 insert_inner(&self.inner, pid, node);
132 }
133
134 fn all_pids(&self) -> Vec<u32> {
135 self.inner.lock().unwrap().keys().copied().collect()
136 }
137}
138
139impl CacheStore for DefaultCache {
140 fn get_info(&self, pid: u32) -> Option<ProcInfo> {
141 get_inner(&self.inner, pid, self.ttl)
142 }
143
144 fn insert_info(&self, pid: u32, info: ProcInfo) {
145 insert_inner(&self.inner, pid, info);
146 }
147}
148
149#[cfg(test)]
150mod tests {
151 use super::*;
152
153 #[test]
154 fn default_tree_insert_get() {
155 let tree = DefaultTree::new(100, 0);
156 tree.insert_node(
157 1,
158 PidNode {
159 ppid: 0,
160 cmd: "init".into(),
161 },
162 );
163 let node = tree.get_node(1).unwrap();
164 assert_eq!(node.ppid, 0);
165 assert_eq!(node.cmd, "init");
166 }
167
168 #[test]
169 fn default_tree_ttl_expired() {
170 let tree = DefaultTree::new(100, 0); tree.insert_node(
172 1,
173 PidNode {
174 ppid: 0,
175 cmd: "init".into(),
176 },
177 );
178 assert!(tree.get_node(1).is_some());
179
180 let tree = DefaultTree::new(100, 1);
182 tree.insert_node(
183 1,
184 PidNode {
185 ppid: 0,
186 cmd: "init".into(),
187 },
188 );
189 assert!(tree.get_node(1).is_some());
190 std::thread::sleep(Duration::from_millis(1100));
191 assert!(tree.get_node(1).is_none());
192 }
193
194 #[test]
195 fn default_cache_insert_get() {
196 let cache = DefaultCache::new(100, 0);
197 cache.insert_info(
198 42,
199 ProcInfo {
200 cmd: "bash".into(),
201 user: "root".into(),
202 ppid: 1,
203 tgid: 42,
204 start_time_ns: 0,
205 },
206 );
207 let info = cache.get_info(42).unwrap();
208 assert_eq!(info.cmd, "bash");
209 assert_eq!(info.ppid, 1);
210 }
211
212 #[test]
213 fn clone_shares_data() {
214 let tree = DefaultTree::new(100, 0);
215 tree.insert_node(
216 1,
217 PidNode {
218 ppid: 0,
219 cmd: "init".into(),
220 },
221 );
222 let tree2 = tree.clone();
223 assert!(tree2.get_node(1).is_some());
224 tree2.insert_node(
225 2,
226 PidNode {
227 ppid: 1,
228 cmd: "bash".into(),
229 },
230 );
231 assert!(tree.get_node(2).is_some());
232 }
233
234 #[test]
235 fn len_and_contains() {
236 let cache = DefaultCache::new(100, 0);
237 assert_eq!(cache.len(), 0);
238 cache.insert_info(
239 1,
240 ProcInfo {
241 cmd: "a".into(),
242 user: "u".into(),
243 ppid: 0,
244 tgid: 1,
245 start_time_ns: 0,
246 },
247 );
248 assert_eq!(cache.len(), 1);
249 assert!(cache.contains_key(1));
250 assert!(!cache.contains_key(999));
251 }
252
253 #[test]
254 fn default_cache_ttl_expired() {
255 let cache = DefaultCache::new(100, 0);
256 cache.insert_info(
257 1,
258 ProcInfo {
259 cmd: "a".into(),
260 user: "u".into(),
261 ppid: 0,
262 tgid: 1,
263 start_time_ns: 0,
264 },
265 );
266 assert!(cache.get_info(1).is_some());
267
268 let cache = DefaultCache::new(100, 1);
269 cache.insert_info(
270 1,
271 ProcInfo {
272 cmd: "a".into(),
273 user: "u".into(),
274 ppid: 0,
275 tgid: 1,
276 start_time_ns: 0,
277 },
278 );
279 assert!(cache.get_info(1).is_some());
280 std::thread::sleep(Duration::from_millis(1100));
281 assert!(cache.get_info(1).is_none());
282 }
283
284 #[test]
285 fn is_empty_default() {
286 let tree = DefaultTree::new(100, 0);
287 assert!(tree.is_empty());
288 tree.insert_node(
289 1,
290 PidNode {
291 ppid: 0,
292 cmd: "init".into(),
293 },
294 );
295 assert!(!tree.is_empty());
296
297 let cache = DefaultCache::new(100, 0);
298 assert!(cache.is_empty());
299 cache.insert_info(
300 1,
301 ProcInfo {
302 cmd: "a".into(),
303 user: "u".into(),
304 ppid: 0,
305 tgid: 1,
306 start_time_ns: 0,
307 },
308 );
309 assert!(!cache.is_empty());
310 }
311
312 #[test]
313 fn all_pids_returns_keys() {
314 let tree = DefaultTree::new(100, 0);
315 tree.insert_node(
316 1,
317 PidNode {
318 ppid: 0,
319 cmd: "a".into(),
320 },
321 );
322 tree.insert_node(
323 2,
324 PidNode {
325 ppid: 1,
326 cmd: "b".into(),
327 },
328 );
329 tree.insert_node(
330 3,
331 PidNode {
332 ppid: 1,
333 cmd: "c".into(),
334 },
335 );
336 let mut pids = tree.all_pids();
337 pids.sort();
338 assert_eq!(pids, vec![1, 2, 3]);
339 }
340
341 #[test]
342 fn tree_ttl_contains_key_expires() {
343 let tree = DefaultTree::new(100, 1);
344 tree.insert_node(
345 1,
346 PidNode {
347 ppid: 0,
348 cmd: "a".into(),
349 },
350 );
351 assert!(tree.contains_key(1));
352 std::thread::sleep(Duration::from_millis(1100));
353 assert!(!tree.contains_key(1));
354 }
355}