oxihuman_core/
semaphore_pool.rs1#![allow(dead_code)]
4
5use std::collections::HashMap;
8
9#[allow(dead_code)]
11#[derive(Debug, Clone)]
12pub struct Semaphore {
13 pub name: String,
14 pub capacity: u32,
15 pub acquired: u32,
16}
17
18#[allow(dead_code)]
19impl Semaphore {
20 pub fn new(name: &str, capacity: u32) -> Self {
21 Self {
22 name: name.to_string(),
23 capacity,
24 acquired: 0,
25 }
26 }
27
28 pub fn available(&self) -> u32 {
29 self.capacity.saturating_sub(self.acquired)
30 }
31
32 pub fn try_acquire(&mut self, count: u32) -> bool {
33 if self.available() >= count {
34 self.acquired += count;
35 true
36 } else {
37 false
38 }
39 }
40
41 pub fn release(&mut self, count: u32) {
42 self.acquired = self.acquired.saturating_sub(count);
43 }
44
45 pub fn is_full(&self) -> bool {
46 self.acquired >= self.capacity
47 }
48}
49
50#[allow(dead_code)]
52pub struct SemaphorePool {
53 semaphores: HashMap<String, Semaphore>,
54 total_acquired: u64,
55 total_released: u64,
56}
57
58#[allow(dead_code)]
59impl SemaphorePool {
60 pub fn new() -> Self {
61 Self {
62 semaphores: HashMap::new(),
63 total_acquired: 0,
64 total_released: 0,
65 }
66 }
67
68 pub fn add(&mut self, name: &str, capacity: u32) {
69 self.semaphores
70 .insert(name.to_string(), Semaphore::new(name, capacity));
71 }
72
73 pub fn remove(&mut self, name: &str) -> bool {
74 self.semaphores.remove(name).is_some()
75 }
76
77 pub fn acquire(&mut self, name: &str, count: u32) -> bool {
78 if let Some(s) = self.semaphores.get_mut(name) {
79 if s.try_acquire(count) {
80 self.total_acquired += u64::from(count);
81 return true;
82 }
83 }
84 false
85 }
86
87 pub fn release(&mut self, name: &str, count: u32) {
88 if let Some(s) = self.semaphores.get_mut(name) {
89 s.release(count);
90 self.total_released += u64::from(count);
91 }
92 }
93
94 pub fn available(&self, name: &str) -> u32 {
95 self.semaphores
96 .get(name)
97 .map(|s| s.available())
98 .unwrap_or(0)
99 }
100
101 pub fn capacity(&self, name: &str) -> u32 {
102 self.semaphores.get(name).map(|s| s.capacity).unwrap_or(0)
103 }
104
105 pub fn count(&self) -> usize {
106 self.semaphores.len()
107 }
108
109 pub fn total_acquired(&self) -> u64 {
110 self.total_acquired
111 }
112
113 pub fn total_released(&self) -> u64 {
114 self.total_released
115 }
116
117 pub fn names(&self) -> Vec<&str> {
118 let mut v: Vec<&str> = self.semaphores.keys().map(|s| s.as_str()).collect();
119 v.sort_unstable();
120 v
121 }
122
123 pub fn is_full(&self, name: &str) -> bool {
124 self.semaphores.get(name).is_some_and(|s| s.is_full())
125 }
126
127 pub fn clear(&mut self) {
128 self.semaphores.clear();
129 }
130}
131
132impl Default for SemaphorePool {
133 fn default() -> Self {
134 Self::new()
135 }
136}
137
138pub fn new_semaphore_pool() -> SemaphorePool {
139 SemaphorePool::new()
140}
141
142#[cfg(test)]
143mod tests {
144 use super::*;
145
146 #[test]
147 fn acquire_and_release() {
148 let mut p = new_semaphore_pool();
149 p.add("net", 4);
150 assert!(p.acquire("net", 2));
151 assert_eq!(p.available("net"), 2);
152 p.release("net", 2);
153 assert_eq!(p.available("net"), 4);
154 }
155
156 #[test]
157 fn over_capacity_fails() {
158 let mut p = new_semaphore_pool();
159 p.add("db", 2);
160 assert!(p.acquire("db", 2));
161 assert!(!p.acquire("db", 1));
162 }
163
164 #[test]
165 fn unknown_semaphore() {
166 let mut p = new_semaphore_pool();
167 assert!(!p.acquire("missing", 1));
168 assert_eq!(p.available("missing"), 0);
169 }
170
171 #[test]
172 fn is_full() {
173 let mut p = new_semaphore_pool();
174 p.add("s", 1);
175 p.acquire("s", 1);
176 assert!(p.is_full("s"));
177 }
178
179 #[test]
180 fn total_acquired_tracked() {
181 let mut p = new_semaphore_pool();
182 p.add("s", 10);
183 p.acquire("s", 3);
184 p.acquire("s", 2);
185 assert_eq!(p.total_acquired(), 5);
186 }
187
188 #[test]
189 fn remove_semaphore() {
190 let mut p = new_semaphore_pool();
191 p.add("x", 5);
192 assert!(p.remove("x"));
193 assert_eq!(p.count(), 0);
194 }
195
196 #[test]
197 fn names_sorted() {
198 let mut p = new_semaphore_pool();
199 p.add("b", 1);
200 p.add("a", 1);
201 assert_eq!(p.names(), vec!["a", "b"]);
202 }
203
204 #[test]
205 fn capacity_query() {
206 let mut p = new_semaphore_pool();
207 p.add("q", 8);
208 assert_eq!(p.capacity("q"), 8);
209 }
210
211 #[test]
212 fn clear_empties_pool() {
213 let mut p = new_semaphore_pool();
214 p.add("a", 1);
215 p.add("b", 2);
216 p.clear();
217 assert_eq!(p.count(), 0);
218 }
219
220 #[test]
221 fn release_no_underflow() {
222 let mut p = new_semaphore_pool();
223 p.add("s", 5);
224 p.release("s", 100); assert_eq!(p.available("s"), 5);
226 }
227}