1use crate::error::{CoreError, CoreResult};
12use crate::token::FrameToken;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
16pub struct FrameId(u32);
17
18impl FrameId {
19 #[inline]
21 pub fn new(id: u32) -> Self {
22 FrameId(id)
23 }
24
25 #[inline]
27 pub fn value(&self) -> u32 {
28 self.0
29 }
30}
31
32impl From<u32> for FrameId {
33 fn from(id: u32) -> Self {
34 FrameId(id)
35 }
36}
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40#[repr(u8)]
41pub enum FrameState {
42 Free = 0,
44 Allocated = 1,
46 InRxRing = 2,
48 Processing = 3,
50 InTxRing = 4,
52 InCompletionRing = 5,
54 Quarantine = 6,
56}
57
58#[derive(Debug, Clone)]
60#[repr(C)]
61pub struct FrameInfo {
62 id: u32,
64 physical_addr: u64,
66 virtual_addr: u64,
68 size: u32,
70 state: FrameState,
72 domain_id: u32,
74 generation: u64,
76}
77
78impl FrameInfo {
79 #[inline]
81 pub fn id(&self) -> FrameId {
82 FrameId(self.id)
83 }
84
85 #[inline]
87 pub fn physical_addr(&self) -> u64 {
88 self.physical_addr
89 }
90
91 #[inline]
93 pub fn virtual_addr(&self) -> u64 {
94 self.virtual_addr
95 }
96
97 #[inline]
99 pub fn size(&self) -> u32 {
100 self.size
101 }
102
103 #[inline]
105 pub fn state(&self) -> FrameState {
106 self.state
107 }
108
109 #[inline]
111 pub fn domain_id(&self) -> u32 {
112 self.domain_id
113 }
114
115 #[inline]
117 pub fn generation(&self) -> u64 {
118 self.generation
119 }
120}
121
122#[derive(Debug)]
136pub struct FramePool {
137 name: String,
139 capacity: u32,
141 frame_size: u32,
143 free_stack: Vec<u32>,
145 frames: Vec<FrameInfo>,
147 allocated_count: u32,
149 quarantined_count: u32,
151 generation: u64,
153 epoch: u64,
155}
156
157impl FramePool {
158 pub fn try_new(name: impl Into<String>, capacity: u32, frame_size: u32) -> CoreResult<Self> {
174 if frame_size == 0 {
176 return Err(CoreError::invalid_config(
177 "frame_size",
178 "frame_size must be greater than 0",
179 ));
180 }
181 if capacity == 0 {
182 return Err(CoreError::invalid_config(
183 "capacity",
184 "capacity must be greater than 0",
185 ));
186 }
187 (capacity as u64)
190 .checked_mul(frame_size as u64)
191 .ok_or_else(|| {
192 CoreError::arithmetic_overflow("mul", capacity as u64, frame_size as u64)
193 })?;
194
195 let name = name.into();
196
197 let mut frames: Vec<FrameInfo> = Vec::with_capacity(capacity as usize);
199 let mut free_stack: Vec<u32> = Vec::with_capacity(capacity as usize);
202 for i in 0..capacity {
203 let offset = (i as u64).checked_mul(frame_size as u64).ok_or_else(|| {
205 CoreError::arithmetic_overflow("mul", i as u64, frame_size as u64)
206 })?;
207 frames.push(FrameInfo {
208 id: i,
209 physical_addr: offset,
210 virtual_addr: offset,
211 size: frame_size,
212 state: FrameState::Free,
213 domain_id: 0,
214 generation: 0,
215 });
216 free_stack.push(i);
217 }
218
219 Ok(Self {
220 name,
221 capacity,
222 frame_size,
223 free_stack,
224 frames,
225 allocated_count: 0,
226 quarantined_count: 0,
227 generation: 0,
228 epoch: 0,
229 })
230 }
231
232 pub fn new(name: impl Into<String>, capacity: u32, frame_size: u32) -> Self {
249 let name = name.into();
250 match Self::try_new(name.clone(), capacity, frame_size) {
251 Ok(pool) => pool,
252 Err(e) => {
255 tracing::error!(
256 pool = %name,
257 capacity,
258 frame_size,
259 error = %e,
260 "FramePool::new 参数非法,回退到 1x1 最小配置;\
261 需要显式错误处理的调用方应改用 FramePool::try_new"
262 );
263 Self {
264 name,
265 capacity: 1,
266 frame_size: 1,
267 free_stack: vec![0],
268 frames: vec![FrameInfo {
269 id: 0,
270 physical_addr: 0,
271 virtual_addr: 0,
272 size: 1,
273 state: FrameState::Free,
274 domain_id: 0,
275 generation: 0,
276 }],
277 allocated_count: 0,
278 quarantined_count: 0,
279 generation: 0,
280 epoch: 0,
281 }
282 }
283 }
284 }
285
286 #[inline]
288 pub fn name(&self) -> &str {
289 &self.name
290 }
291
292 #[inline]
294 pub fn capacity(&self) -> u32 {
295 self.capacity
296 }
297
298 #[inline]
300 pub fn frame_size(&self) -> u32 {
301 self.frame_size
302 }
303
304 #[inline]
306 pub fn allocated_count(&self) -> u32 {
307 self.allocated_count
308 }
309
310 #[inline]
312 pub fn free_count(&self) -> u32 {
313 self.free_stack.len() as u32
314 }
315
316 #[inline]
318 pub fn quarantined_count(&self) -> u32 {
319 self.quarantined_count
320 }
321
322 #[inline]
333 pub fn allocate(&mut self, domain_id: u32) -> CoreResult<FrameToken> {
334 let frame_idx = *self
337 .free_stack
338 .last()
339 .ok_or_else(|| {
340 CoreError::quota_exceeded("frame", self.capacity as u64, 1)
341 })?;
342
343 let frame = self
345 .frames
346 .get_mut(frame_idx as usize)
347 .ok_or_else(|| CoreError::resource_not_found(frame_idx as u64, "frame"))?;
348 if frame.state != FrameState::Free {
351 return Err(CoreError::state_conflict(
352 format!("frame {} in state {:?}", frame_idx, frame.state),
353 "allocate",
354 ));
355 }
356
357 frame.state = FrameState::Allocated;
360 frame.domain_id = domain_id;
361 frame.generation = self.generation;
362
363 let _ = self.free_stack.pop();
365
366 self.allocated_count = self
368 .allocated_count
369 .checked_add(1)
370 .ok_or_else(|| CoreError::arithmetic_overflow("add", self.allocated_count as u64, 1))?;
371
372 Ok(FrameToken::new(
373 FrameId(frame_idx),
374 domain_id,
375 self.generation,
376 self.epoch,
377 ))
378 }
379
380 #[inline]
395 pub fn release(&mut self, token: FrameToken) -> CoreResult<()> {
396 self.do_release(token).map_err(|(_t, e)| e)
398 }
399
400 #[inline]
413 pub fn release_recoverable(&mut self, token: FrameToken) -> Result<(), (FrameToken, CoreError)> {
414 self.do_release(token)
415 }
416
417 fn do_release(&mut self, token: FrameToken) -> Result<(), (FrameToken, CoreError)> {
430 let frame_idx = token.frame_id().value();
431
432 let frame = match self.frames.get_mut(frame_idx as usize) {
434 Some(f) => f,
435 None => return Err((token, CoreError::resource_not_found(frame_idx as u64, "frame"))),
436 };
437
438 if let Err(e) = token.verify_ownership(frame.domain_id, frame.generation) {
441 return Err((token, e));
442 }
443
444 if frame.state != FrameState::Allocated {
446 return Err((
447 token,
448 CoreError::ownership_violation("allocated", "not_allocated"),
449 ));
450 }
451
452 let new_count = match self.allocated_count.checked_sub(1) {
455 Some(c) => c,
456 None => {
457 return Err((
458 token,
459 CoreError::arithmetic_overflow("sub", self.allocated_count as u64, 1),
460 ))
461 }
462 };
463
464 frame.state = FrameState::Free;
466
467 self.generation = self.generation.saturating_add(1);
469 frame.generation = self.generation;
470
471 self.free_stack.push(frame_idx);
473
474 self.allocated_count = new_count;
476
477 let _ = token;
479 Ok(())
480 }
481
482 pub fn quarantine(&mut self, token: FrameToken, reason: impl Into<String>) -> CoreResult<()> {
493 let frame_idx = token.frame_id().value();
494
495 let frame = self
497 .frames
498 .get_mut(frame_idx as usize)
499 .ok_or_else(|| CoreError::resource_not_found(frame_idx as u64, "frame"))?;
500
501 token.verify_ownership(frame.domain_id, frame.generation)?;
503
504 if frame.state != FrameState::Allocated {
506 return Err(CoreError::ownership_violation("allocated", "not_allocated"));
507 }
508
509 frame.state = FrameState::Quarantine;
511
512 self.generation = self.generation.saturating_add(1);
514 frame.generation = self.generation;
515
516 self.allocated_count = self
518 .allocated_count
519 .checked_sub(1)
520 .ok_or_else(|| CoreError::arithmetic_overflow("sub", self.allocated_count as u64, 1))?;
521 self.quarantined_count = self
522 .quarantined_count
523 .checked_add(1)
524 .ok_or_else(|| CoreError::arithmetic_overflow("add", self.quarantined_count as u64, 1))?;
525
526 tracing::debug!(
528 pool = %self.name,
529 frame_id = frame_idx,
530 reason = %reason.into(),
531 "frame quarantined"
532 );
533 let _ = token;
535 Ok(())
536 }
537
538 pub fn recover_from_quarantine(&mut self, frame_id: FrameId) -> CoreResult<()> {
553 let frame_idx = frame_id.value();
554
555 if (frame_idx as usize) >= self.frames.len() {
557 return Err(CoreError::resource_not_found(frame_idx as u64, "frame"));
558 }
559
560 let frame = &mut self.frames[frame_idx as usize];
561 if frame.state != FrameState::Quarantine {
562 return Err(CoreError::resource_not_found(frame_idx as u64, "frame"));
563 }
564
565 frame.state = FrameState::Free;
567
568 self.generation = self.generation.saturating_add(1);
572 frame.generation = self.generation;
573
574 self.free_stack.push(frame_idx);
576
577 self.quarantined_count = self
579 .quarantined_count
580 .checked_sub(1)
581 .ok_or_else(|| CoreError::arithmetic_overflow("sub", self.quarantined_count as u64, 1))?;
582
583 Ok(())
584 }
585
586 pub fn recover_from_quarantine_by_id(
602 &mut self,
603 frame_id: FrameId,
604 expected_generation: u64,
605 ) -> CoreResult<()> {
606 let frame_idx = frame_id.value();
607
608 if (frame_idx as usize) >= self.frames.len() {
610 return Err(CoreError::resource_not_found(frame_idx as u64, "frame"));
611 }
612
613 let frame = &mut self.frames[frame_idx as usize];
614 if frame.state != FrameState::Quarantine {
615 return Err(CoreError::resource_not_found(frame_idx as u64, "frame"));
616 }
617
618 if frame.generation != expected_generation {
620 return Err(CoreError::ownership_violation(
621 "valid generation",
622 "invalid generation",
623 ));
624 }
625
626 frame.state = FrameState::Free;
628
629 self.generation = self.generation.saturating_add(1);
631 frame.generation = self.generation;
632
633 self.free_stack.push(frame_idx);
635
636 self.quarantined_count = self
638 .quarantined_count
639 .checked_sub(1)
640 .ok_or_else(|| CoreError::arithmetic_overflow("sub", self.quarantined_count as u64, 1))?;
641
642 Ok(())
643 }
644
645 pub fn quarantine_by_id(
667 &mut self,
668 frame_id: FrameId,
669 expected_generation: u64,
670 reason: impl Into<String>,
671 ) -> CoreResult<()> {
672 let frame_idx = frame_id.value();
673
674 if (frame_idx as usize) >= self.frames.len() {
676 return Err(CoreError::resource_not_found(frame_idx as u64, "frame"));
677 }
678
679 let frame = &mut self.frames[frame_idx as usize];
680
681 if frame.generation != expected_generation {
683 return Err(CoreError::ownership_violation(
684 "valid generation",
685 "invalid generation",
686 ));
687 }
688
689 match frame.state {
691 FrameState::Allocated
692 | FrameState::Processing
693 | FrameState::InRxRing
694 | FrameState::InTxRing
695 | FrameState::InCompletionRing => {
696 self.allocated_count = self
702 .allocated_count
703 .checked_sub(1)
704 .ok_or_else(|| CoreError::arithmetic_overflow("sub", self.allocated_count as u64, 1))?;
705 }
706 _ => {
707 return Err(CoreError::state_conflict(
708 format!("frame {} in state {:?}", frame_idx, frame.state),
709 "quarantine",
710 ));
711 }
712 }
713
714 frame.state = FrameState::Quarantine;
715
716 self.generation = self.generation.saturating_add(1);
718 frame.generation = self.generation;
719
720 self.quarantined_count = self
721 .quarantined_count
722 .checked_add(1)
723 .ok_or_else(|| CoreError::arithmetic_overflow("add", self.quarantined_count as u64, 1))?;
724
725 tracing::debug!(
727 pool = %self.name,
728 frame_id = frame_idx,
729 reason = %reason.into(),
730 "frame quarantined by id"
731 );
732 Ok(())
733 }
734
735 #[inline]
747 pub fn advance_generation(&mut self) -> u64 {
748 self.generation = self.generation.saturating_add(1);
749 self.generation
750 }
751
752 #[inline]
754 pub fn current_generation(&self) -> u64 {
755 self.generation
756 }
757
758 #[inline]
768 pub fn advance_epoch(&mut self) -> u64 {
769 self.epoch = self.epoch.saturating_add(1);
770 self.epoch
771 }
772
773 #[inline]
775 pub fn current_epoch(&self) -> u64 {
776 self.epoch
777 }
778
779 #[inline]
788 pub fn get_frame_info(&self, frame_id: FrameId) -> Option<FrameInfo> {
789 self.frames.get(frame_id.value() as usize).cloned()
790 }
791
792 pub fn allocate_batch(&mut self, domain_id: u32, count: u32) -> CoreResult<Vec<FrameToken>> {
805 if self.free_stack.len() < count as usize {
807 return Err(CoreError::quota_exceeded(
808 "frame",
809 self.free_stack.len() as u64,
810 count as u64,
811 ));
812 }
813
814 let mut tokens = Vec::with_capacity(count as usize);
815 for _ in 0..count {
816 match self.allocate(domain_id) {
817 Ok(t) => tokens.push(t),
818 Err(e) => {
819 for t in tokens {
823 if let Err((_t, re)) = self.release_recoverable(t) {
824 tracing::error!(
825 pool = %self.name,
826 error = %re,
827 "allocate_batch 回滚归还帧失败(尽力回滚,帧可能泄漏)"
828 );
829 }
830 }
831 return Err(e);
832 }
833 }
834 }
835 Ok(tokens)
836 }
837
838 pub fn release_batch(&mut self, tokens: Vec<FrameToken>) -> CoreResult<()> {
847 let mut first_err: Option<CoreError> = None;
848 for token in tokens {
849 if let Err((_token, e)) = self.release_recoverable(token)
852 && first_err.is_none()
853 {
854 first_err = Some(e);
855 }
856 }
857 match first_err {
858 Some(e) => Err(e),
859 None => Ok(()),
860 }
861 }
862
863 #[inline]
871 pub fn verify_conservation(&self) -> CoreResult<()> {
872 let free = self.free_stack.len() as u64;
873 let allocated = self.allocated_count as u64;
874 let quarantined = self.quarantined_count as u64;
875
876 let total = free
877 .checked_add(allocated)
878 .and_then(|v| v.checked_add(quarantined))
879 .ok_or_else(|| CoreError::arithmetic_overflow("add", free, allocated))?;
880
881 if total != self.capacity as u64 {
882 return Err(CoreError::internal(format!(
883 "conservation violation: total={}, capacity={}, free={}, allocated={}, quarantined={}",
884 total, self.capacity, free, allocated, quarantined
885 )));
886 }
887
888 Ok(())
889 }
890
891 #[inline]
913 pub fn release_by_id(&mut self, frame_id: FrameId, expected_generation: u64) -> CoreResult<()> {
914 let frame_idx = frame_id.value();
915
916 let frame = self
918 .frames
919 .get_mut(frame_idx as usize)
920 .ok_or_else(|| CoreError::resource_not_found(frame_idx as u64, "frame"))?;
921
922 if frame.generation != expected_generation {
924 return Err(CoreError::ownership_violation(
925 "valid generation",
926 "invalid generation",
927 ));
928 }
929
930 if frame.state != FrameState::Allocated {
931 return Err(CoreError::ownership_violation("allocated", "not_allocated"));
932 }
933
934 frame.state = FrameState::Free;
935
936 self.generation = self.generation.saturating_add(1);
939 frame.generation = self.generation;
940
941 self.free_stack.push(frame_idx);
942
943 self.allocated_count = self
944 .allocated_count
945 .checked_sub(1)
946 .ok_or_else(|| CoreError::arithmetic_overflow("sub", self.allocated_count as u64, 1))?;
947
948 Ok(())
949 }
950
951 #[inline]
956 pub fn frames(&self) -> &[FrameInfo] {
957 &self.frames
958 }
959
960 #[inline]
965 pub fn remaining_free(&self) -> u32 {
966 self.free_stack.len() as u32
967 }
968}
969
970#[cfg(test)]
971mod tests {
972 use super::*;
973
974 const TEST_CAPACITY: u32 = 100;
975 const TEST_FRAME_SIZE: u32 = 2048;
976
977 fn create_test_pool() -> FramePool {
978 FramePool::new("test_pool", TEST_CAPACITY, TEST_FRAME_SIZE)
979 }
980
981 #[test]
982 fn test_pool_creation() {
983 let pool = create_test_pool();
984 assert_eq!(pool.name(), "test_pool");
985 assert_eq!(pool.capacity(), TEST_CAPACITY);
986 assert_eq!(pool.frame_size(), TEST_FRAME_SIZE);
987 assert_eq!(pool.free_count(), TEST_CAPACITY);
988 assert_eq!(pool.allocated_count(), 0);
989 assert_eq!(pool.quarantined_count(), 0);
990 }
991
992 #[test]
993 fn test_allocate_and_release() {
994 let mut pool = create_test_pool();
995
996 let token = pool.allocate(0).unwrap();
998 assert_eq!(pool.allocated_count(), 1);
999 assert_eq!(pool.free_count(), TEST_CAPACITY - 1);
1000
1001 assert!(token.verify_ownership(0, 0).is_ok());
1003
1004 pool.release(token).unwrap();
1006 assert_eq!(pool.allocated_count(), 0);
1007 assert_eq!(pool.free_count(), TEST_CAPACITY);
1008 }
1009
1010 #[test]
1011 fn test_allocate_exhausted() {
1012 let mut pool = FramePool::new("small_pool", 2, TEST_FRAME_SIZE);
1013
1014 let token1 = pool.allocate(0).unwrap();
1015 let token2 = pool.allocate(0).unwrap();
1016
1017 let result = pool.allocate(0);
1019 assert!(result.is_err());
1020
1021 pool.release(token1).unwrap();
1023 let token3 = pool.allocate(0).unwrap();
1024 assert_ne!(token3.generation(), 0);
1027 assert!(token3.verify_ownership(0, 0).is_err());
1028 assert!(token3.verify_ownership(0, token3.generation()).is_ok());
1029
1030 pool.release(token2).unwrap();
1031 pool.release(token3).unwrap();
1032 }
1033
1034 #[test]
1035 fn test_quarantine_and_recover() {
1036 let mut pool = create_test_pool();
1037
1038 let token = pool.allocate(0).unwrap();
1039 let frame_id = token.frame_id();
1040
1041 pool.quarantine(token, "test reason").unwrap();
1043 assert_eq!(pool.quarantined_count(), 1);
1044 assert_eq!(pool.allocated_count(), 0);
1045 assert_eq!(pool.free_count(), TEST_CAPACITY - 1);
1046
1047 pool.recover_from_quarantine(frame_id).unwrap();
1049 assert_eq!(pool.quarantined_count(), 0);
1050 assert_eq!(pool.free_count(), TEST_CAPACITY);
1051 }
1052
1053 #[test]
1054 fn test_quarantine_nonexistent() {
1055 let mut pool = create_test_pool();
1056 let result = pool.recover_from_quarantine(FrameId::new(999));
1057 assert!(result.is_err());
1058 }
1059
1060 #[test]
1061 fn test_generation_management() {
1062 let mut pool = create_test_pool();
1063 assert_eq!(pool.current_generation(), 0);
1064
1065 let generation = pool.advance_generation();
1066 assert_eq!(generation, 1);
1067 assert_eq!(pool.current_generation(), 1);
1068 }
1069
1070 #[test]
1071 fn test_epoch_management() {
1072 let mut pool = create_test_pool();
1073 assert_eq!(pool.current_epoch(), 0);
1074
1075 let epoch = pool.advance_epoch();
1076 assert_eq!(epoch, 1);
1077 assert_eq!(pool.current_epoch(), 1);
1078 }
1079
1080 #[test]
1081 fn test_verify_conservation() {
1082 let mut pool = create_test_pool();
1083
1084 assert!(pool.verify_conservation().is_ok());
1086
1087 let token = pool.allocate(0).unwrap();
1089 assert!(pool.verify_conservation().is_ok());
1090
1091 pool.release(token).unwrap();
1093 assert!(pool.verify_conservation().is_ok());
1094
1095 let token = pool.allocate(0).unwrap();
1097 let frame_id = token.frame_id();
1098 pool.quarantine(token, "test").unwrap();
1099 assert!(pool.verify_conservation().is_ok());
1100
1101 pool.recover_from_quarantine(frame_id).unwrap();
1103 assert!(pool.verify_conservation().is_ok());
1104 }
1105
1106 #[test]
1107 fn test_get_frame_info() {
1108 let pool = create_test_pool();
1109
1110 let info = pool.get_frame_info(FrameId::new(0)).unwrap();
1111 assert_eq!(info.id(), FrameId::new(0));
1112 assert_eq!(info.size(), TEST_FRAME_SIZE);
1113 assert_eq!(info.state(), FrameState::Free);
1114 }
1115
1116 #[test]
1117 fn test_allocate_batch() {
1118 let mut pool = create_test_pool();
1119
1120 let tokens = pool.allocate_batch(0, 10).unwrap();
1122 assert_eq!(tokens.len(), 10);
1123 assert_eq!(pool.allocated_count(), 10);
1124 assert_eq!(pool.free_count(), TEST_CAPACITY - 10);
1125
1126 pool.release_batch(tokens).unwrap();
1128 assert_eq!(pool.allocated_count(), 0);
1129 assert_eq!(pool.free_count(), TEST_CAPACITY);
1130 }
1131
1132 #[test]
1133 fn test_allocate_batch_exhausted() {
1134 let mut pool = FramePool::new("tiny_pool", 5, TEST_FRAME_SIZE);
1135
1136 let result = pool.allocate_batch(0, 10);
1138 assert!(result.is_err());
1139
1140 assert_eq!(pool.allocated_count(), 0);
1142 assert_eq!(pool.free_count(), 5);
1143 }
1144
1145 #[test]
1146 fn test_release_wrong_state() {
1147 let mut pool = create_test_pool();
1148
1149 let token = pool.allocate(0).unwrap();
1153 let frame_idx = token.frame_id().value();
1154
1155 pool.release(token).unwrap();
1157
1158 let info = pool.get_frame_info(FrameId::new(frame_idx)).unwrap();
1162 assert_eq!(info.state(), FrameState::Free);
1163 }
1164
1165 #[test]
1166 fn test_free_list_lifo_order() {
1167 let mut pool = FramePool::new("lifo_test", 3, TEST_FRAME_SIZE);
1168
1169 let t0 = pool.allocate(0).unwrap(); let t1 = pool.allocate(0).unwrap(); let t2 = pool.allocate(0).unwrap(); assert_eq!(t0.frame_id().value(), 2);
1175 assert_eq!(t1.frame_id().value(), 1);
1176 assert_eq!(t2.frame_id().value(), 0);
1177
1178 pool.release(t0).unwrap(); let t3 = pool.allocate(0).unwrap(); assert_eq!(t3.frame_id().value(), 2);
1182
1183 pool.release(t1).unwrap();
1184 pool.release(t2).unwrap();
1185 pool.release(t3).unwrap();
1186 }
1187
1188 #[test]
1189 fn test_domain_isolation() {
1190 let mut pool = create_test_pool();
1191
1192 let token0 = pool.allocate(0).unwrap();
1194 assert_eq!(token0.domain_id(), 0);
1195 assert!(token0.verify_ownership(0, 0).is_ok());
1196 assert!(token0.verify_ownership(1, 0).is_err());
1197
1198 let token1 = pool.allocate(1).unwrap();
1200 assert_eq!(token1.domain_id(), 1);
1201 assert!(token1.verify_ownership(1, 0).is_ok());
1202 assert!(token1.verify_ownership(0, 0).is_err());
1203
1204 pool.release(token0).unwrap();
1205 pool.release(token1).unwrap();
1206 }
1207
1208 #[test]
1209 fn test_release_by_id() {
1210 let mut pool = create_test_pool();
1211
1212 let token = pool.allocate(42).unwrap();
1214 let frame_id = token.frame_id();
1215 let token_gen = token.generation();
1216 assert_eq!(pool.allocated_count(), 1);
1217
1218 pool.release_by_id(frame_id, token_gen).unwrap();
1220 assert_eq!(pool.allocated_count(), 0);
1221 assert!(pool.verify_conservation().is_ok());
1222
1223 let result = pool.release_by_id(frame_id, token_gen);
1225 assert!(result.is_err());
1226 }
1227
1228 #[test]
1229 fn test_release_by_id_with_wrong_state() {
1230 let mut pool = create_test_pool();
1231
1232 let result = pool.release_by_id(FrameId::new(0), pool.current_generation());
1234 assert!(result.is_err());
1235 }
1236
1237 #[test]
1238 fn test_release_by_id_out_of_bounds() {
1239 let mut pool = create_test_pool();
1240
1241 let result = pool.release_by_id(FrameId::new(TEST_CAPACITY), pool.current_generation());
1243 assert!(result.is_err());
1244
1245 let result = pool.release_by_id(FrameId::new(u32::MAX), pool.current_generation());
1246 assert!(result.is_err());
1247
1248 assert!(pool.verify_conservation().is_ok());
1250 }
1251
1252 #[test]
1253 fn test_release_by_id_aba_protection() {
1254 let mut pool = FramePool::new("aba_test", 1, TEST_FRAME_SIZE);
1255
1256 let token = pool.allocate(0).unwrap();
1258 let frame_id = token.frame_id();
1259 let gen_v1 = token.generation();
1260 let _ = token;
1261
1262 pool.release_by_id(frame_id, gen_v1).unwrap();
1264
1265 assert!(pool.release_by_id(frame_id, gen_v1).is_err());
1267
1268 let token2 = pool.allocate(0).unwrap();
1270 let gen_v2 = token2.generation();
1271 assert_ne!(gen_v1, gen_v2);
1272 assert!(pool.release_by_id(frame_id, gen_v1).is_err());
1273
1274 pool.release_by_id(frame_id, gen_v2).unwrap();
1276 assert!(pool.verify_conservation().is_ok());
1277 }
1278
1279 #[test]
1280 fn test_release_token_aba_protection() {
1281 let mut pool = FramePool::new("aba_token", 1, TEST_FRAME_SIZE);
1282
1283 let token = pool.allocate(0).unwrap();
1285 let gen_v1 = token.generation();
1286 pool.release(token).unwrap();
1287
1288 let token2 = pool.allocate(0).unwrap();
1289 assert_ne!(token2.generation(), gen_v1);
1291 pool.release(token2).unwrap();
1292 assert!(pool.verify_conservation().is_ok());
1293 }
1294
1295 fn set_frame_state(pool: &mut FramePool, frame_id: u32, state: FrameState) {
1298 let current_gen = pool.current_generation();
1299 let frame = &mut pool.frames[frame_id as usize];
1300 frame.state = state;
1301 frame.generation = current_gen;
1302 }
1303
1304 #[test]
1305 fn test_quarantine_by_id_from_allocated() {
1306 let mut pool = create_test_pool();
1307 let token = pool.allocate(0).unwrap();
1308 let frame_id = token.frame_id();
1309 let token_gen = token.generation();
1310 let _ = token;
1311
1312 assert_eq!(pool.allocated_count(), 1);
1313 assert_eq!(pool.quarantined_count(), 0);
1314
1315 pool.quarantine_by_id(frame_id, token_gen, "test").unwrap();
1316
1317 assert_eq!(pool.allocated_count(), 0);
1318 assert_eq!(pool.quarantined_count(), 1);
1319 assert_eq!(pool.get_frame_info(frame_id).unwrap().state(), FrameState::Quarantine);
1320 assert!(pool.verify_conservation().is_ok());
1321 }
1322
1323 #[test]
1324 fn test_quarantine_by_id_from_processing() {
1325 let mut pool = create_test_pool();
1326 let token = pool.allocate(0).unwrap();
1327 let frame_id = token.frame_id();
1328 let idx = frame_id.value();
1329 let token_gen = token.generation();
1330 let _ = token;
1331
1332 set_frame_state(&mut pool, idx, FrameState::Processing);
1333 pool.quarantine_by_id(frame_id, token_gen, "test").unwrap();
1334
1335 assert_eq!(pool.quarantined_count(), 1);
1336 assert_eq!(pool.get_frame_info(frame_id).unwrap().state(), FrameState::Quarantine);
1337 }
1338
1339 #[test]
1342 fn test_quarantine_by_id_processing_conservation() {
1343 let mut pool = create_test_pool();
1344 let token = pool.allocate(0).unwrap();
1346 let frame_id = token.frame_id();
1347 let idx = frame_id.value();
1348 let token_gen = token.generation();
1349 let _ = token;
1350
1351 set_frame_state(&mut pool, idx, FrameState::Processing);
1352 assert!(pool.verify_conservation().is_ok());
1354
1355 pool.quarantine_by_id(frame_id, token_gen, "processing-conflict").unwrap();
1356
1357 assert_eq!(pool.quarantined_count(), 1);
1359 assert_eq!(pool.allocated_count(), 0);
1360 assert!(pool.verify_conservation().is_ok());
1361 }
1362
1363 #[test]
1366 fn test_recover_from_quarantine_by_id_generation_mismatch() {
1367 let mut pool = create_test_pool();
1368 let token = pool.allocate(0).unwrap();
1369 let frame_id = token.frame_id();
1370 pool.quarantine(token, "test").unwrap(); let correct_gen = pool.get_frame_info(frame_id).unwrap().generation();
1373
1374 assert!(pool
1376 .recover_from_quarantine_by_id(frame_id, correct_gen + 1)
1377 .is_err());
1378 assert_eq!(pool.quarantined_count(), 1);
1379 assert!(pool.verify_conservation().is_ok());
1380
1381 pool.recover_from_quarantine_by_id(frame_id, correct_gen).unwrap();
1383 assert_eq!(pool.quarantined_count(), 0);
1384 assert_eq!(pool.free_count(), TEST_CAPACITY);
1385 assert!(pool.verify_conservation().is_ok());
1386 }
1387
1388 #[test]
1389 fn test_quarantine_by_id_from_in_rx_ring() {
1390 let mut pool = create_test_pool();
1391 let token = pool.allocate(0).unwrap();
1392 let frame_id = token.frame_id();
1393 let idx = frame_id.value();
1394 let token_gen = token.generation();
1395 let _ = token;
1396
1397 set_frame_state(&mut pool, idx, FrameState::InRxRing);
1398 pool.quarantine_by_id(frame_id, token_gen, "test").unwrap();
1399
1400 assert_eq!(pool.quarantined_count(), 1);
1401 assert_eq!(pool.get_frame_info(frame_id).unwrap().state(), FrameState::Quarantine);
1402 }
1403
1404 #[test]
1405 fn test_quarantine_by_id_from_in_tx_ring() {
1406 let mut pool = create_test_pool();
1407 let token = pool.allocate(0).unwrap();
1408 let frame_id = token.frame_id();
1409 let idx = frame_id.value();
1410 let token_gen = token.generation();
1411 let _ = token;
1412
1413 set_frame_state(&mut pool, idx, FrameState::InTxRing);
1414 pool.quarantine_by_id(frame_id, token_gen, "test").unwrap();
1415
1416 assert_eq!(pool.quarantined_count(), 1);
1417 assert_eq!(pool.get_frame_info(frame_id).unwrap().state(), FrameState::Quarantine);
1418 }
1419
1420 #[test]
1421 fn test_quarantine_by_id_from_in_completion_ring() {
1422 let mut pool = create_test_pool();
1423 let token = pool.allocate(0).unwrap();
1424 let frame_id = token.frame_id();
1425 let idx = frame_id.value();
1426 let token_gen = token.generation();
1427 let _ = token;
1428
1429 set_frame_state(&mut pool, idx, FrameState::InCompletionRing);
1430 pool.quarantine_by_id(frame_id, token_gen, "test").unwrap();
1431
1432 assert_eq!(pool.quarantined_count(), 1);
1433 assert_eq!(pool.get_frame_info(frame_id).unwrap().state(), FrameState::Quarantine);
1434 }
1435
1436 #[test]
1439 fn test_quarantine_by_id_from_free_fails() {
1440 let mut pool = create_test_pool();
1441 let result = pool.quarantine_by_id(FrameId::new(0), pool.current_generation(), "test");
1442 assert!(result.is_err());
1443 }
1444
1445 #[test]
1446 fn test_quarantine_by_id_from_quarantine_fails() {
1447 let mut pool = create_test_pool();
1448 let token = pool.allocate(0).unwrap();
1449 let frame_id = token.frame_id();
1450 pool.quarantine(token, "first").unwrap();
1451
1452 let result = pool.quarantine_by_id(frame_id, pool.current_generation(), "test");
1453 assert!(result.is_err());
1454 }
1455
1456 #[test]
1457 fn test_quarantine_by_id_out_of_bounds() {
1458 let mut pool = create_test_pool();
1459 let result = pool.quarantine_by_id(FrameId::new(9999), pool.current_generation(), "test");
1460 assert!(result.is_err());
1461 }
1462
1463 #[test]
1466 fn test_frame_state_discriminant_values() {
1467 assert_eq!(FrameState::Free as u8, 0);
1468 assert_eq!(FrameState::Allocated as u8, 1);
1469 assert_eq!(FrameState::InRxRing as u8, 2);
1470 assert_eq!(FrameState::Processing as u8, 3);
1471 assert_eq!(FrameState::InTxRing as u8, 4);
1472 assert_eq!(FrameState::InCompletionRing as u8, 5);
1473 assert_eq!(FrameState::Quarantine as u8, 6);
1474 }
1475
1476 #[test]
1477 fn test_frame_state_equality() {
1478 assert_eq!(FrameState::Free, FrameState::Free);
1479 assert_ne!(FrameState::Free, FrameState::Allocated);
1480 assert_ne!(FrameState::Allocated, FrameState::Processing);
1481 }
1482
1483 #[test]
1484 fn test_frame_state_clone_copy() {
1485 let s = FrameState::Processing;
1486 let s2 = s;
1487 assert_eq!(s, s2);
1488 let s3 = s;
1489 assert_eq!(s, s3);
1490 }
1491
1492 #[test]
1493 fn test_frame_state_debug() {
1494 let s = format!("{:?}", FrameState::Quarantine);
1495 assert_eq!(s, "Quarantine");
1496 }
1497
1498 #[test]
1501 fn test_pool_capacity_one() {
1502 let mut pool = FramePool::new("single", 1, 4096);
1503 assert_eq!(pool.capacity(), 1);
1504 assert_eq!(pool.free_count(), 1);
1505 assert_eq!(pool.allocated_count(), 0);
1506
1507 let token = pool.allocate(0).unwrap();
1508 assert_eq!(pool.free_count(), 0);
1509 assert_eq!(pool.allocated_count(), 1);
1510
1511 let result = pool.allocate(0);
1512 assert!(result.is_err());
1513
1514 pool.release(token).unwrap();
1515 assert_eq!(pool.free_count(), 1);
1516 assert_eq!(pool.allocated_count(), 0);
1517 assert!(pool.verify_conservation().is_ok());
1518 }
1519
1520 #[test]
1521 fn test_capacity_one_quarantine_and_recover() {
1522 let mut pool = FramePool::new("single_q", 1, 4096);
1523 let token = pool.allocate(0).unwrap();
1524 let frame_id = token.frame_id();
1525
1526 pool.quarantine(token, "test").unwrap();
1527 assert_eq!(pool.quarantined_count(), 1);
1528 assert_eq!(pool.free_count(), 0);
1529
1530 pool.recover_from_quarantine(frame_id).unwrap();
1531 assert_eq!(pool.quarantined_count(), 0);
1532 assert_eq!(pool.free_count(), 1);
1533 assert!(pool.verify_conservation().is_ok());
1534 }
1535
1536 #[test]
1537 fn test_frame_id_value_boundaries() {
1538 let id_zero = FrameId::new(0);
1539 assert_eq!(id_zero.value(), 0);
1540
1541 let id_max = FrameId::new(u32::MAX);
1542 assert_eq!(id_max.value(), u32::MAX);
1543 }
1544
1545 #[test]
1548 fn test_get_frame_info_out_of_bounds() {
1549 let pool = create_test_pool();
1550 assert!(pool.get_frame_info(FrameId::new(TEST_CAPACITY)).is_none());
1551 assert!(pool.get_frame_info(FrameId::new(u32::MAX)).is_none());
1552 }
1553
1554 #[test]
1555 fn test_get_frame_info_valid_boundary() {
1556 let pool = create_test_pool();
1557 assert!(pool.get_frame_info(FrameId::new(0)).is_some());
1558 assert!(pool.get_frame_info(FrameId::new(TEST_CAPACITY - 1)).is_some());
1559 }
1560
1561 #[test]
1564 fn test_frame_id_from_u32() {
1565 let id: FrameId = 42u32.into();
1566 assert_eq!(id.value(), 42);
1567 assert_eq!(id, FrameId::new(42));
1568 }
1569
1570 #[test]
1573 fn test_frame_info_fields() {
1574 let pool = create_test_pool();
1575 let info = pool.get_frame_info(FrameId::new(5)).unwrap();
1576 assert_eq!(info.id(), FrameId::new(5));
1577 assert_eq!(info.size(), TEST_FRAME_SIZE);
1578 assert_eq!(info.state(), FrameState::Free);
1579 assert_eq!(info.domain_id(), 0);
1580 assert_eq!(info.generation(), 0);
1581 assert_eq!(info.physical_addr(), 5 * TEST_FRAME_SIZE as u64);
1582 assert_eq!(info.virtual_addr(), 5 * TEST_FRAME_SIZE as u64);
1583 }
1584
1585 #[test]
1588 fn test_allocate_release_no_panic() {
1589 let mut pool = create_test_pool();
1590 for _ in 0..1000 {
1591 let token = pool.allocate(0).unwrap();
1592 assert_eq!(token.domain_id(), 0);
1593 pool.release(token).unwrap();
1594 }
1595 assert_eq!(pool.allocated_count(), 0);
1596 assert_eq!(pool.free_count(), TEST_CAPACITY);
1597 assert!(pool.verify_conservation().is_ok());
1598 }
1599
1600 #[test]
1603 fn test_recover_from_quarantine_advances_generation() {
1604 let mut pool = FramePool::new("aba_recover", 1, TEST_FRAME_SIZE);
1605
1606 let token = pool.allocate(0).unwrap();
1608 let frame_id = token.frame_id();
1609 pool.quarantine(token, "test").unwrap();
1610 let gen_quarantined = pool.get_frame_info(frame_id).unwrap().generation();
1611
1612 pool.recover_from_quarantine(frame_id).unwrap();
1614 let gen_recovered = pool.get_frame_info(frame_id).unwrap().generation();
1615 assert_ne!(gen_quarantined, gen_recovered);
1616 }
1617
1618 #[test]
1619 fn test_recover_from_quarantine_stale_handle_rejected() {
1620 let mut pool = FramePool::new("aba_recover2", 1, TEST_FRAME_SIZE);
1621
1622 let token = pool.allocate(0).unwrap();
1624 let frame_id = token.frame_id();
1625 let gen_v1 = token.generation();
1626 pool.quarantine(token, "test").unwrap();
1627 let gen_quarantined = pool.get_frame_info(frame_id).unwrap().generation();
1628 pool.recover_from_quarantine(frame_id).unwrap();
1629 let token2 = pool.allocate(0).unwrap();
1630 let gen_v2 = token2.generation();
1631 let _ = token2;
1632
1633 assert_ne!(gen_v1, gen_quarantined);
1635 assert_ne!(gen_quarantined, gen_v2);
1636 assert_ne!(gen_v1, gen_v2);
1637
1638 assert!(pool.release_by_id(frame_id, gen_v1).is_err());
1640 assert!(pool.quarantine_by_id(frame_id, gen_v1, "stale").is_err());
1641 assert!(pool.quarantine_by_id(frame_id, gen_quarantined, "stale").is_err());
1642
1643 pool.release_by_id(frame_id, gen_v2).unwrap();
1645 assert!(pool.verify_conservation().is_ok());
1646 }
1647
1648 #[test]
1651 fn test_new_invalid_params_fallback_no_panic() {
1652 let mut pool = FramePool::new("fallback_cap", 0, TEST_FRAME_SIZE);
1654 assert_eq!(pool.capacity(), 1);
1655 assert_eq!(pool.frame_size(), 1);
1656 let token = pool.allocate(0).unwrap();
1657 pool.release(token).unwrap();
1658
1659 let pool = FramePool::new("fallback_size", TEST_CAPACITY, 0);
1660 assert_eq!(pool.capacity(), 1);
1661 assert_eq!(pool.frame_size(), 1);
1662
1663 assert!(FramePool::try_new("t", 0, 1).is_err());
1665 assert!(FramePool::try_new("t", 1, 0).is_err());
1666 }
1667}