1use std::collections::HashMap;
7use std::sync::Mutex;
8
9pub trait WorkerPool: Send + Sync {
14 fn acquire(&self, resource_tag: Option<&str>) -> bool;
16
17 fn release(&self, resource_tag: Option<&str>);
19
20 fn add_resource(&self, _tag: &str) {}
22
23 fn remove_resource(&self, _tag: &str) {}
25}
26
27pub struct FixedPool {
29 max: usize,
30 active: Mutex<usize>,
31}
32
33impl FixedPool {
34 pub fn new(max_concurrent: usize) -> Self {
35 Self {
36 max: max_concurrent,
37 active: Mutex::new(0),
38 }
39 }
40}
41
42impl WorkerPool for FixedPool {
43 fn acquire(&self, _resource_tag: Option<&str>) -> bool {
44 let mut active = self.active.lock().unwrap();
45 if *active < self.max {
46 *active += 1;
47 true
48 } else {
49 false
50 }
51 }
52
53 fn release(&self, _resource_tag: Option<&str>) {
54 let mut active = self.active.lock().unwrap();
55 *active = active.saturating_sub(1);
56 }
57}
58
59pub struct UnlimitedPool;
61
62impl WorkerPool for UnlimitedPool {
63 fn acquire(&self, _resource_tag: Option<&str>) -> bool {
64 true
65 }
66
67 fn release(&self, _resource_tag: Option<&str>) {}
68}
69
70pub struct DynamicPool {
75 global_max: usize,
76 global_active: Mutex<usize>,
77 tag_slots: Mutex<HashMap<String, (usize, usize)>>, }
79
80impl DynamicPool {
81 pub fn new(global_max: usize) -> Self {
82 Self {
83 global_max,
84 global_active: Mutex::new(0),
85 tag_slots: Mutex::new(HashMap::new()),
86 }
87 }
88
89 pub fn set_tag_limit(&self, tag: &str, max: usize) {
91 let mut slots = self.tag_slots.lock().unwrap();
92 let entry = slots.entry(tag.to_string()).or_insert((0, 0));
93 entry.0 = max;
94 }
95}
96
97impl WorkerPool for DynamicPool {
98 fn acquire(&self, resource_tag: Option<&str>) -> bool {
99 let mut global = self.global_active.lock().unwrap();
100 if *global >= self.global_max {
101 return false;
102 }
103
104 if let Some(tag) = resource_tag {
105 let mut slots = self.tag_slots.lock().unwrap();
106 if let Some((max, active)) = slots.get_mut(tag) {
107 if *active >= *max {
108 return false;
109 }
110 *active += 1;
111 }
112 }
114
115 *global += 1;
116 true
117 }
118
119 fn release(&self, resource_tag: Option<&str>) {
120 let mut global = self.global_active.lock().unwrap();
121 *global = global.saturating_sub(1);
122
123 if let Some(tag) = resource_tag {
124 let mut slots = self.tag_slots.lock().unwrap();
125 if let Some((_, active)) = slots.get_mut(tag) {
126 *active = active.saturating_sub(1);
127 }
128 }
129 }
130
131 fn add_resource(&self, tag: &str) {
132 let mut slots = self.tag_slots.lock().unwrap();
133 let entry = slots.entry(tag.to_string()).or_insert((0, 0));
134 entry.0 += 1;
135 }
136
137 fn remove_resource(&self, tag: &str) {
138 let mut slots = self.tag_slots.lock().unwrap();
139 if let Some((max, _)) = slots.get_mut(tag) {
140 *max = max.saturating_sub(1);
141 }
142 }
143}
144
145#[cfg(test)]
146mod tests {
147 use super::*;
148
149 #[test]
150 fn test_fixed_pool() {
151 let pool = FixedPool::new(2);
152 assert!(pool.acquire(None));
153 assert!(pool.acquire(None));
154 assert!(!pool.acquire(None)); pool.release(None);
156 assert!(pool.acquire(None)); }
158
159 #[test]
160 fn test_unlimited_pool() {
161 let pool = UnlimitedPool;
162 for _ in 0..1000 {
163 assert!(pool.acquire(None));
164 }
165 }
166
167 #[test]
168 fn test_dynamic_pool_global() {
169 let pool = DynamicPool::new(2);
170 assert!(pool.acquire(None));
171 assert!(pool.acquire(None));
172 assert!(!pool.acquire(None));
173 }
174
175 #[test]
176 fn test_dynamic_pool_tags() {
177 let pool = DynamicPool::new(10);
178 pool.set_tag_limit("gpu", 1);
179
180 assert!(pool.acquire(Some("gpu")));
181 assert!(!pool.acquire(Some("gpu"))); assert!(pool.acquire(Some("cpu"))); pool.release(Some("gpu"));
184 assert!(pool.acquire(Some("gpu"))); }
186
187 #[test]
188 fn test_dynamic_pool_add_remove() {
189 let pool = DynamicPool::new(10);
190 pool.set_tag_limit("builder", 0);
191 assert!(!pool.acquire(Some("builder"))); pool.add_resource("builder"); assert!(pool.acquire(Some("builder")));
195 assert!(!pool.acquire(Some("builder")));
196
197 pool.add_resource("builder"); assert!(pool.acquire(Some("builder")));
199 }
200}