1#![allow(dead_code)]
2use std::collections::VecDeque;
9use std::sync::atomic::{AtomicU64, Ordering};
10use std::time::{Duration, Instant};
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
14pub struct FenceId(
15 pub u64,
17);
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub enum FenceStatus {
22 Available,
24 Pending,
26 Signaled,
28 Error,
30}
31
32impl std::fmt::Display for FenceStatus {
33 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34 match self {
35 Self::Available => write!(f, "Available"),
36 Self::Pending => write!(f, "Pending"),
37 Self::Signaled => write!(f, "Signaled"),
38 Self::Error => write!(f, "Error"),
39 }
40 }
41}
42
43#[derive(Debug, Clone)]
45pub struct PooledFence {
46 pub id: FenceId,
48 pub status: FenceStatus,
50 pub submit_time: Option<Instant>,
52 pub signal_time: Option<Instant>,
54 pub recycle_count: u64,
56}
57
58impl PooledFence {
59 #[must_use]
61 pub fn new(id: FenceId) -> Self {
62 Self {
63 id,
64 status: FenceStatus::Available,
65 submit_time: None,
66 signal_time: None,
67 recycle_count: 0,
68 }
69 }
70
71 #[must_use]
73 pub fn latency(&self) -> Option<Duration> {
74 match (self.submit_time, self.signal_time) {
75 (Some(submit), Some(signal)) => Some(signal.duration_since(submit)),
76 _ => None,
77 }
78 }
79
80 #[must_use]
82 pub fn is_available(&self) -> bool {
83 self.status == FenceStatus::Available
84 }
85
86 #[must_use]
88 pub fn is_pending(&self) -> bool {
89 self.status == FenceStatus::Pending
90 }
91}
92
93#[derive(Debug, Clone)]
95pub struct FencePoolConfig {
96 pub initial_size: usize,
98 pub max_size: usize,
100 pub wait_timeout: Duration,
102 pub auto_grow: bool,
104 pub grow_batch_size: usize,
106}
107
108impl Default for FencePoolConfig {
109 fn default() -> Self {
110 Self {
111 initial_size: 16,
112 max_size: 256,
113 wait_timeout: Duration::from_secs(5),
114 auto_grow: true,
115 grow_batch_size: 8,
116 }
117 }
118}
119
120#[derive(Debug, Clone, Default)]
122pub struct FencePoolStats {
123 pub total_fences: usize,
125 pub available_count: usize,
127 pub pending_count: usize,
129 pub signaled_count: usize,
131 pub total_allocations: u64,
133 pub total_recycles: u64,
135 pub grow_events: u64,
137 pub avg_latency_us: f64,
139}
140
141impl FencePoolStats {
142 #[allow(clippy::cast_precision_loss)]
144 #[must_use]
145 pub fn utilization(&self) -> f64 {
146 if self.total_fences == 0 {
147 return 0.0;
148 }
149 self.pending_count as f64 / self.total_fences as f64
150 }
151}
152
153pub struct FencePool {
157 fences: Vec<PooledFence>,
159 available: VecDeque<usize>,
161 config: FencePoolConfig,
163 next_id: AtomicU64,
165 total_allocations: u64,
167 total_recycles: u64,
169 grow_events: u64,
171}
172
173impl FencePool {
174 #[must_use]
176 pub fn new() -> Self {
177 Self::with_config(FencePoolConfig::default())
178 }
179
180 #[must_use]
182 pub fn with_config(config: FencePoolConfig) -> Self {
183 let mut pool = Self {
184 fences: Vec::with_capacity(config.initial_size),
185 available: VecDeque::with_capacity(config.initial_size),
186 next_id: AtomicU64::new(0),
187 total_allocations: 0,
188 total_recycles: 0,
189 grow_events: 0,
190 config,
191 };
192 let initial = pool.config.initial_size;
193 pool.grow(initial);
194 pool
195 }
196
197 fn grow(&mut self, count: usize) {
199 let max = self.config.max_size;
200 let current = self.fences.len();
201 let actual_count = count.min(max.saturating_sub(current));
202 for _ in 0..actual_count {
203 let id = FenceId(self.next_id.fetch_add(1, Ordering::Relaxed));
204 let fence = PooledFence::new(id);
205 let index = self.fences.len();
206 self.fences.push(fence);
207 self.available.push_back(index);
208 }
209 if actual_count > 0 {
210 self.grow_events += 1;
211 }
212 }
213
214 pub fn acquire(&mut self) -> Option<FenceId> {
217 if self.available.is_empty() && self.config.auto_grow {
218 let batch = self.config.grow_batch_size;
219 self.grow(batch);
220 }
221 let index = self.available.pop_front()?;
222 let fence = &mut self.fences[index];
223 fence.status = FenceStatus::Pending;
224 fence.submit_time = Some(Instant::now());
225 fence.signal_time = None;
226 self.total_allocations += 1;
227 Some(fence.id)
228 }
229
230 pub fn signal(&mut self, id: FenceId) -> bool {
232 if let Some(fence) = self.fences.iter_mut().find(|f| f.id == id) {
233 fence.status = FenceStatus::Signaled;
234 fence.signal_time = Some(Instant::now());
235 true
236 } else {
237 false
238 }
239 }
240
241 pub fn release(&mut self, id: FenceId) -> bool {
243 if let Some((index, fence)) = self.fences.iter_mut().enumerate().find(|(_, f)| f.id == id) {
244 fence.status = FenceStatus::Available;
245 fence.submit_time = None;
246 fence.signal_time = None;
247 fence.recycle_count += 1;
248 self.available.push_back(index);
249 self.total_recycles += 1;
250 true
251 } else {
252 false
253 }
254 }
255
256 pub fn status(&self, id: FenceId) -> Option<FenceStatus> {
258 self.fences.iter().find(|f| f.id == id).map(|f| f.status)
259 }
260
261 pub fn pending_fences(&self) -> Vec<FenceId> {
263 self.fences
264 .iter()
265 .filter(|f| f.status == FenceStatus::Pending)
266 .map(|f| f.id)
267 .collect()
268 }
269
270 pub fn flush_all(&mut self) {
272 let pending_ids: Vec<FenceId> = self.pending_fences();
273 for id in &pending_ids {
274 self.signal(*id);
275 }
276 let signaled_ids: Vec<FenceId> = self
277 .fences
278 .iter()
279 .filter(|f| f.status == FenceStatus::Signaled)
280 .map(|f| f.id)
281 .collect();
282 for id in signaled_ids {
283 self.release(id);
284 }
285 }
286
287 pub fn total_count(&self) -> usize {
289 self.fences.len()
290 }
291
292 pub fn available_count(&self) -> usize {
294 self.available.len()
295 }
296
297 #[allow(clippy::cast_precision_loss)]
299 pub fn stats(&self) -> FencePoolStats {
300 let pending_count = self
301 .fences
302 .iter()
303 .filter(|f| f.status == FenceStatus::Pending)
304 .count();
305 let signaled_count = self
306 .fences
307 .iter()
308 .filter(|f| f.status == FenceStatus::Signaled)
309 .count();
310
311 let latencies: Vec<f64> = self
312 .fences
313 .iter()
314 .filter_map(PooledFence::latency)
315 .map(|d| d.as_micros() as f64)
316 .collect();
317 let avg_latency_us = if latencies.is_empty() {
318 0.0
319 } else {
320 latencies.iter().sum::<f64>() / latencies.len() as f64
321 };
322
323 FencePoolStats {
324 total_fences: self.fences.len(),
325 available_count: self.available.len(),
326 pending_count,
327 signaled_count,
328 total_allocations: self.total_allocations,
329 total_recycles: self.total_recycles,
330 grow_events: self.grow_events,
331 avg_latency_us,
332 }
333 }
334
335 pub fn reset(&mut self) {
337 self.available.clear();
338 for (i, fence) in self.fences.iter_mut().enumerate() {
339 fence.status = FenceStatus::Available;
340 fence.submit_time = None;
341 fence.signal_time = None;
342 self.available.push_back(i);
343 }
344 }
345
346 pub fn wait_timeout(&self) -> Duration {
348 self.config.wait_timeout
349 }
350
351 pub fn has_available(&self) -> bool {
353 !self.available.is_empty()
354 }
355}
356
357impl Default for FencePool {
358 fn default() -> Self {
359 Self::new()
360 }
361}
362
363#[cfg(test)]
364mod tests {
365 use super::*;
366
367 #[test]
368 fn test_create_default_pool() {
369 let pool = FencePool::new();
370 assert_eq!(pool.total_count(), 16);
371 assert_eq!(pool.available_count(), 16);
372 }
373
374 #[test]
375 fn test_create_with_config() {
376 let config = FencePoolConfig {
377 initial_size: 4,
378 max_size: 32,
379 auto_grow: true,
380 grow_batch_size: 4,
381 ..Default::default()
382 };
383 let pool = FencePool::with_config(config);
384 assert_eq!(pool.total_count(), 4);
385 assert_eq!(pool.available_count(), 4);
386 }
387
388 #[test]
389 fn test_acquire_fence() {
390 let mut pool = FencePool::new();
391 let id = pool.acquire();
392 assert!(id.is_some());
393 assert_eq!(pool.available_count(), 15);
394 }
395
396 #[test]
397 fn test_signal_fence() {
398 let mut pool = FencePool::new();
399 let id = pool.acquire().expect("fence acquire should succeed");
400 assert!(pool.signal(id));
401 assert_eq!(pool.status(id), Some(FenceStatus::Signaled));
402 }
403
404 #[test]
405 fn test_release_fence() {
406 let mut pool = FencePool::new();
407 let initial_available = pool.available_count();
408 let id = pool.acquire().expect("fence acquire should succeed");
409 assert_eq!(pool.available_count(), initial_available - 1);
410 pool.signal(id);
411 pool.release(id);
412 assert_eq!(pool.available_count(), initial_available);
413 assert_eq!(pool.status(id), Some(FenceStatus::Available));
414 }
415
416 #[test]
417 fn test_pending_fences() {
418 let mut pool = FencePool::new();
419 let id1 = pool.acquire().expect("fence acquire should succeed");
420 let id2 = pool.acquire().expect("fence acquire should succeed");
421 let pending = pool.pending_fences();
422 assert_eq!(pending.len(), 2);
423 assert!(pending.contains(&id1));
424 assert!(pending.contains(&id2));
425 }
426
427 #[test]
428 fn test_flush_all() {
429 let mut pool = FencePool::new();
430 let _id1 = pool.acquire().expect("fence acquire should succeed");
431 let _id2 = pool.acquire().expect("fence acquire should succeed");
432 assert_eq!(pool.pending_fences().len(), 2);
433 pool.flush_all();
434 assert_eq!(pool.pending_fences().len(), 0);
435 assert_eq!(pool.available_count(), 16);
436 }
437
438 #[test]
439 fn test_auto_grow() {
440 let config = FencePoolConfig {
441 initial_size: 2,
442 max_size: 10,
443 auto_grow: true,
444 grow_batch_size: 3,
445 ..Default::default()
446 };
447 let mut pool = FencePool::with_config(config);
448 let _id1 = pool.acquire().expect("fence acquire should succeed");
449 let _id2 = pool.acquire().expect("fence acquire should succeed");
450 let id3 = pool.acquire();
452 assert!(id3.is_some());
453 assert!(pool.total_count() > 2);
454 }
455
456 #[test]
457 fn test_max_size_limit() {
458 let config = FencePoolConfig {
459 initial_size: 2,
460 max_size: 3,
461 auto_grow: true,
462 grow_batch_size: 10,
463 ..Default::default()
464 };
465 let mut pool = FencePool::with_config(config);
466 let _id1 = pool.acquire().expect("fence acquire should succeed");
467 let _id2 = pool.acquire().expect("fence acquire should succeed");
468 let _id3 = pool.acquire();
469 assert!(pool.total_count() <= 3);
471 }
472
473 #[test]
474 fn test_no_auto_grow() {
475 let config = FencePoolConfig {
476 initial_size: 1,
477 max_size: 10,
478 auto_grow: false,
479 ..Default::default()
480 };
481 let mut pool = FencePool::with_config(config);
482 let _id = pool.acquire().expect("fence acquire should succeed");
483 let id2 = pool.acquire();
484 assert!(id2.is_none());
485 }
486
487 #[test]
488 fn test_stats() {
489 let mut pool = FencePool::new();
490 let id1 = pool.acquire().expect("fence acquire should succeed");
491 let _id2 = pool.acquire().expect("fence acquire should succeed");
492 pool.signal(id1);
493 let stats = pool.stats();
494 assert_eq!(stats.total_fences, 16);
495 assert_eq!(stats.pending_count, 1);
496 assert_eq!(stats.signaled_count, 1);
497 assert_eq!(stats.total_allocations, 2);
498 }
499
500 #[test]
501 fn test_stats_utilization() {
502 let stats = FencePoolStats {
503 total_fences: 10,
504 pending_count: 5,
505 ..Default::default()
506 };
507 let util = stats.utilization();
508 assert!((util - 0.5).abs() < f64::EPSILON);
509 }
510
511 #[test]
512 fn test_stats_utilization_empty() {
513 let stats = FencePoolStats::default();
514 assert!((stats.utilization() - 0.0).abs() < f64::EPSILON);
515 }
516
517 #[test]
518 fn test_reset_pool() {
519 let mut pool = FencePool::new();
520 let _id1 = pool.acquire().expect("fence acquire should succeed");
521 let _id2 = pool.acquire().expect("fence acquire should succeed");
522 pool.reset();
523 assert_eq!(pool.available_count(), pool.total_count());
524 }
525
526 #[test]
527 fn test_fence_display() {
528 assert_eq!(format!("{}", FenceStatus::Available), "Available");
529 assert_eq!(format!("{}", FenceStatus::Pending), "Pending");
530 assert_eq!(format!("{}", FenceStatus::Signaled), "Signaled");
531 assert_eq!(format!("{}", FenceStatus::Error), "Error");
532 }
533
534 #[test]
535 fn test_pooled_fence_latency() {
536 let mut fence = PooledFence::new(FenceId(0));
537 assert!(fence.latency().is_none());
538 fence.submit_time = Some(Instant::now());
539 assert!(fence.latency().is_none());
540 fence.signal_time = Some(Instant::now());
541 assert!(fence.latency().is_some());
542 }
543
544 #[test]
545 fn test_wait_timeout() {
546 let pool = FencePool::new();
547 assert_eq!(pool.wait_timeout(), Duration::from_secs(5));
548 }
549
550 #[test]
551 fn test_has_available() {
552 let config = FencePoolConfig {
553 initial_size: 1,
554 max_size: 1,
555 auto_grow: false,
556 ..Default::default()
557 };
558 let mut pool = FencePool::with_config(config);
559 assert!(pool.has_available());
560 let _id = pool.acquire().expect("fence acquire should succeed");
561 assert!(!pool.has_available());
562 }
563}