Skip to main content

zenith_foundation/
frame.rs

1//! Frame 池管理模块
2//!
3//! 实现极致优化的 FramePool 帧池,核心设计原则:
4//! - **单线程持有**:每个 Worker 独占一个 FramePool,零锁竞争
5//! - **Free-List 栈式分配器**:Vec<u32> 存储空闲帧索引,LIFO 栈式分配 O(1)
6//! - **连续内存存储**:Vec<FrameInfo> 连续存储帧信息,缓存友好
7//! - **零堆分配**:热路径无任何堆分配操作
8//! - **零锁优先**:无任何 Mutex/RwLock/原子操作
9//! - **编译期约束**:通过类型系统保证所有权唯一
10
11use crate::error::{CoreError, CoreResult};
12use crate::token::FrameToken;
13
14/// Frame ID
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
16pub struct FrameId(u32);
17
18impl FrameId {
19    /// 创建新的 Frame ID
20    #[inline]
21    pub fn new(id: u32) -> Self {
22        FrameId(id)
23    }
24
25    /// 获取原始 ID 值
26    #[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/// Frame 状态
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40#[repr(u8)]
41pub enum FrameState {
42    /// 空闲状态
43    Free = 0,
44    /// 已分配给应用
45    Allocated = 1,
46    /// 在内核 RX Ring 中
47    InRxRing = 2,
48    /// 在用户态处理中
49    Processing = 3,
50    /// 在 TX Ring 中等待发送
51    InTxRing = 4,
52    /// 在完成队列中等待回收
53    InCompletionRing = 5,
54    /// 在隔离区(异常状态)
55    Quarantine = 6,
56}
57
58/// Frame 信息(紧凑布局,面向缓存)
59#[derive(Debug, Clone)]
60#[repr(C)]
61pub struct FrameInfo {
62    /// Frame ID (紧凑存储)
63    id: u32,
64    /// 物理地址
65    physical_addr: u64,
66    /// 虚拟地址
67    virtual_addr: u64,
68    /// 大小
69    size: u32,
70    /// 当前状态
71    state: FrameState,
72    /// 所属域ID
73    domain_id: u32,
74    /// 代际号
75    generation: u64,
76}
77
78impl FrameInfo {
79    /// 获取 Frame ID
80    #[inline]
81    pub fn id(&self) -> FrameId {
82        FrameId(self.id)
83    }
84
85    /// 获取物理地址
86    #[inline]
87    pub fn physical_addr(&self) -> u64 {
88        self.physical_addr
89    }
90
91    /// 获取虚拟地址
92    #[inline]
93    pub fn virtual_addr(&self) -> u64 {
94        self.virtual_addr
95    }
96
97    /// 获取大小
98    #[inline]
99    pub fn size(&self) -> u32 {
100        self.size
101    }
102
103    /// 获取状态
104    #[inline]
105    pub fn state(&self) -> FrameState {
106        self.state
107    }
108
109    /// 获取域ID
110    #[inline]
111    pub fn domain_id(&self) -> u32 {
112        self.domain_id
113    }
114
115    /// 获取代际号
116    #[inline]
117    pub fn generation(&self) -> u64 {
118        self.generation
119    }
120}
121
122/// Frame 池(极致优化版)
123///
124/// ## 核心设计
125/// - **Free-List 栈式分配器**:使用 `Vec<u32>` 作为 LIFO 栈,O(1) 分配/回收
126/// - **连续内存存储**:`Vec<FrameInfo>` 连续存储,缓存行友好
127/// - **单线程持有**:无锁设计,每个 Worker 独占一个 FramePool
128/// - **预分配固定容量**:初始化时一次性分配所有帧,运行期禁止扩容
129///
130/// ## 性能特征
131/// - 分配:O(1),仅一次 Vec push/pop
132/// - 回收:O(1),仅一次 Vec push
133/// - 守恒校验:O(n),但仅在调试/检查时调用
134/// - 热路径零堆分配、零锁、零系统调用
135#[derive(Debug)]
136pub struct FramePool {
137    /// 池名称
138    name: String,
139    /// 总容量
140    capacity: u32,
141    /// 帧大小
142    frame_size: u32,
143    /// Free-List:空闲帧索引栈(LIFO)
144    free_stack: Vec<u32>,
145    /// 帧信息连续存储
146    frames: Vec<FrameInfo>,
147    /// 已分配计数
148    allocated_count: u32,
149    /// 隔离区计数
150    quarantined_count: u32,
151    /// 当前代际号
152    generation: u64,
153    /// 当前 Epoch 编号
154    epoch: u64,
155}
156
157impl FramePool {
158    /// 创建新的 Frame 池(fail-closed 版本)
159    ///
160    /// 初始化时一次性分配所有数据结构,运行期禁止扩容。
161    ///
162    /// # Arguments
163    /// * `name` - 池名称
164    /// * `capacity` - 总容量(帧数)
165    /// * `frame_size` - 每个 Frame 的大小(字节)
166    ///
167    /// # Returns
168    /// * `Ok(FramePool)` - 新的 FramePool 实例
169    ///
170    /// # Errors
171    /// * `frame_size == 0` 或 `capacity == 0` → `CoreError::InvalidConfig`
172    /// * `capacity * frame_size` 地址空间溢出 u64 → `CoreError::ArithmeticOverflow`
173    pub fn try_new(name: impl Into<String>, capacity: u32, frame_size: u32) -> CoreResult<Self> {
174        // 参数校验(fail-closed:非法参数直接返回错误,禁止 panic)
175        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        // 地址空间溢出检查(fail-closed):capacity * frame_size 必须可容纳于 u64,
188        // 禁止静默吞溢出(原 unwrap_or_default 会把物理/虚拟地址置 0)
189        (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        // 预分配帧信息(连续内存)
198        let mut frames: Vec<FrameInfo> = Vec::with_capacity(capacity as usize);
199        // 预分配 Free-List 栈
200        // LIFO:最后推入的最先弹出,利用缓存局部性
201        let mut free_stack: Vec<u32> = Vec::with_capacity(capacity as usize);
202        for i in 0..capacity {
203            // 逐帧地址偏移同样使用 checked 算术;总量已预检,此处不会溢出
204            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    /// 创建新的 Frame 池(兼容版本,禁止 panic)
233    ///
234    /// 参数非法(`frame_size == 0` / `capacity == 0` / 地址空间溢出)时不 panic,
235    /// 回退到最小合法配置(capacity=1, frame_size=1),回退前以 `tracing::error!`
236    /// 记录完整原因(池名、参数与底层错误),避免静默降级难以排查。
237    ///
238    /// **推荐使用 [`FramePool::try_new`]**:显式返回错误、可在调用方精确处置;
239    /// 本构造函数仅为不便于传播错误的兼容调用方保留。
240    ///
241    /// # Arguments
242    /// * `name` - 池名称
243    /// * `capacity` - 总容量(帧数)
244    /// * `frame_size` - 每个 Frame 的大小(字节)
245    ///
246    /// # Returns
247    /// 新的 FramePool 实例
248    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            // 回退到最小合法配置(fail-closed:1*1 必然合法,无需 unwrap);
253            // 先记录完整失败原因,杜绝「参数非法被静默吞掉」
254            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    /// 获取池名称
287    #[inline]
288    pub fn name(&self) -> &str {
289        &self.name
290    }
291
292    /// 获取总容量
293    #[inline]
294    pub fn capacity(&self) -> u32 {
295        self.capacity
296    }
297
298    /// 获取帧大小
299    #[inline]
300    pub fn frame_size(&self) -> u32 {
301        self.frame_size
302    }
303
304    /// 获取已分配数量
305    #[inline]
306    pub fn allocated_count(&self) -> u32 {
307        self.allocated_count
308    }
309
310    /// 获取空闲数量
311    #[inline]
312    pub fn free_count(&self) -> u32 {
313        self.free_stack.len() as u32
314    }
315
316    /// 获取隔离区数量
317    #[inline]
318    pub fn quarantined_count(&self) -> u32 {
319        self.quarantined_count
320    }
321
322    /// 分配一个 Frame(O(1),零锁零堆分配)
323    ///
324    /// 从 Free-List 栈顶弹出一个空闲帧,标记为已分配。
325    ///
326    /// # Arguments
327    /// * `domain_id` - 域ID
328    ///
329    /// # Returns
330    /// * `Ok(FrameToken)` - 成功分配的所有权令牌
331    /// * `Err(CoreError::QuotaExceeded)` - 资源不足
332    #[inline]
333    pub fn allocate(&mut self, domain_id: u32) -> CoreResult<FrameToken> {
334        // 先查看栈顶(LIFO,利用缓存局部性),完成边界检查后再弹出,
335        // 避免内部状态损坏时弹出后丢失帧索引
336        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        // 边界检查(fail-closed):free_stack 中的索引必须落在帧数组内,禁止越界 panic
344        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        // 运行时状态检查(CORE-006):free_stack 中的帧必须为 Free,否则 fail-closed。
349        // 不使用 debug_assert(release 构建不生效),确保状态不变量在任意构建下强制执行。
350        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        // 更新帧状态;代际号取自池级单调计数器,
358        // 每次成功释放/隔离都会推进,保证旧令牌在帧重新分配后必然校验失败(防 ABA)
359        frame.state = FrameState::Allocated;
360        frame.domain_id = domain_id;
361        frame.generation = self.generation;
362
363        // 边界检查通过,弹出栈顶
364        let _ = self.free_stack.pop();
365
366        // 更新计数(checked 算术)
367        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    /// 归还 Frame(O(1),零锁零堆分配)
381    ///
382    /// 验证令牌所有权后,将帧放回 Free-List 栈。
383    ///
384    /// **注意**:即使验证失败(返回 `Err`),`token` 也会被消费(drop),
385    /// 对应的帧将永久泄漏。如果需要在失败后保留令牌以便重试或另行处理,
386    /// 请使用 [`FramePool::release_recoverable`]。
387    ///
388    /// # Arguments
389    /// * `token` - 所有权令牌(被消费,即使在错误路径上也会被 drop)
390    ///
391    /// # Returns
392    /// * `Ok(())` - 成功归还
393    /// * `Err(CoreError::OwnershipViolation)` - 所有权违规(token 已被消费)
394    #[inline]
395    pub fn release(&mut self, token: FrameToken) -> CoreResult<()> {
396        // 失败路径 token 被 drop(不可恢复,与既有语义一致),仅传播错误
397        self.do_release(token).map_err(|(_t, e)| e)
398    }
399
400    /// 归还 Frame(可恢复版本,O(1))
401    ///
402    /// 与 [`release`](Self::release) 语义一致,但在验证失败时将 `token`
403    /// 随错误一起返回(`Err((token, error))`),调用方可据此重试或另行处理,
404    /// 避免验证失败时帧被永久泄漏。
405    ///
406    /// # Arguments
407    /// * `token` - 所有权令牌(成功时被消费,失败时随错误返回)
408    ///
409    /// # Returns
410    /// * `Ok(())` - 成功归还(token 被消费)
411    /// * `Err((FrameToken, CoreError))` - 验证失败,token 随错误返回供调用方处置
412    #[inline]
413    pub fn release_recoverable(&mut self, token: FrameToken) -> Result<(), (FrameToken, CoreError)> {
414        self.do_release(token)
415    }
416
417    /// 统一的核心归还逻辑(CORE-031)
418    ///
419    /// 被 [`release`](Self::release) 与 [`release_recoverable`](Self::release_recoverable)
420    /// 共同委托,消除两处重复的归还/校验/记账逻辑。
421    ///
422    /// 成功时 `token` 被消费;失败时 `token` 随错误返回(`Err((token, error))`),
423    /// 由调用方决定丢弃或保留。所有校验均在修改状态前完成(fail-closed)。
424    ///
425    /// # 代际推进说明(CORE-009)
426    /// `self.generation` 使用 `saturating_add`。代际为 u64,池内每次释放/隔离/回收
427    /// 推进一次,要在实际生命周期内达到 2^64 次推进不可达,因此饱和语义不会隐藏
428    /// 真实溢出;此处保持既有行为,不改变公开子签名。
429    fn do_release(&mut self, token: FrameToken) -> Result<(), (FrameToken, CoreError)> {
430        let frame_idx = token.frame_id().value();
431
432        // 边界检查(fail-closed):越界索引直接返回错误,禁止 panic
433        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        // 代际与域所有权校验(§3.3.4 铁律):
439        // 旧令牌在帧被释放并重新分配后,代际必然不匹配,校验失败
440        if let Err(e) = token.verify_ownership(frame.domain_id, frame.generation) {
441            return Err((token, e));
442        }
443
444        // 验证帧状态
445        if frame.state != FrameState::Allocated {
446            return Err((
447                token,
448                CoreError::ownership_violation("allocated", "not_allocated"),
449            ));
450        }
451
452        // 预检算术:checked_sub 失败属于内部不变量违例,
453        // 在修改状态前返回错误,保证失败路径状态不被破坏
454        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        // 所有校验通过,开始修改状态
465        frame.state = FrameState::Free;
466
467        // 推进代际(防 ABA):使所有指向本帧的旧令牌立即失效
468        self.generation = self.generation.saturating_add(1);
469        frame.generation = self.generation;
470
471        // 放回 Free-List 栈顶
472        self.free_stack.push(frame_idx);
473
474        // 更新计数
475        self.allocated_count = new_count;
476
477        // 令牌被消费
478        let _ = token;
479        Ok(())
480    }
481
482    /// 将 Frame 移入隔离区
483    ///
484    /// 隔离区帧不会被自动回收,需要手动调用 `recover_from_quarantine`。
485    ///
486    /// # Arguments
487    /// * `token` - 所有权令牌(被消费)
488    /// * `reason` - 隔离原因
489    ///
490    /// # Returns
491    /// * `Ok(())` - 成功隔离
492    pub fn quarantine(&mut self, token: FrameToken, reason: impl Into<String>) -> CoreResult<()> {
493        let frame_idx = token.frame_id().value();
494
495        // 边界检查(fail-closed):越界索引直接返回错误,禁止 panic
496        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        // 代际与域所有权校验(§3.3.4 铁律)
502        token.verify_ownership(frame.domain_id, frame.generation)?;
503
504        // 仅允许从 Allocated 状态隔离(fail-closed:先校验后变更,避免部分状态污染)
505        if frame.state != FrameState::Allocated {
506            return Err(CoreError::ownership_violation("allocated", "not_allocated"));
507        }
508
509        // 更新状态
510        frame.state = FrameState::Quarantine;
511
512        // 推进代际(防 ABA):使所有指向本帧的旧令牌立即失效
513        self.generation = self.generation.saturating_add(1);
514        frame.generation = self.generation;
515
516        // 更新计数
517        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        // 隔离原因不再静默丢弃:以 debug 级结构化日志记录,便于事后审计定位
527        tracing::debug!(
528            pool = %self.name,
529            frame_id = frame_idx,
530            reason = %reason.into(),
531            "frame quarantined"
532        );
533        // 令牌被消费(token 在函数结束时自动释放所有权)
534        let _ = token;
535        Ok(())
536    }
537
538    /// 从隔离区回收 Frame
539    ///
540    /// **注意**:本方法仅凭 `frame_id` 回收,**不**验证代际(CORE-008)。
541    /// 仅适用于调用方可确认帧未被回收重新分配的内部安全场景。
542    /// 数据面路径(持有 frame_id + expected_generation)应使用
543    /// [`recover_from_quarantine_by_id`](Self::recover_from_quarantine_by_id),
544    /// 以代际验证防止陈旧 FrameId 被复用。
545    ///
546    /// # Arguments
547    /// * `frame_id` - Frame ID
548    ///
549    /// # Returns
550    /// * `Ok(())` - 成功回收
551    /// * `Err(CoreError::ResourceNotFound)` - Frame 不在隔离区
552    pub fn recover_from_quarantine(&mut self, frame_id: FrameId) -> CoreResult<()> {
553        let frame_idx = frame_id.value();
554
555        // 边界检查
556        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        // 更新状态
566        frame.state = FrameState::Free;
567
568        // 推进代际(防 ABA):与 release/quarantine 一致的写法,
569        // 「隔离 → 回收 → 重新分配」链路中旧代际句柄必然校验失败,
570        // 关闭回收不推进代际留下的窄 ABA 窗口
571        self.generation = self.generation.saturating_add(1);
572        frame.generation = self.generation;
573
574        // 放回 Free-List 栈
575        self.free_stack.push(frame_idx);
576
577        // 更新计数
578        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    /// 从隔离区回收 Frame(代际验证版本,CORE-008)
587    ///
588    /// 与 [`recover_from_quarantine`](Self::recover_from_quarantine) 语义一致,
589    /// 额外校验期望代际号与当前帧代际一致,防止陈旧 FrameId 被恶意复用到
590    /// 新帧上(防 ABA)。适用于 Worker 数据面等持有 frame_id + expected_generation
591    /// 的调用路径。
592    ///
593    /// # Arguments
594    /// * `frame_id` - Frame ID
595    /// * `expected_generation` - 期望的代际号(必须匹配当前帧的代际)
596    ///
597    /// # Returns
598    /// * `Ok(())` - 成功回收
599    /// * `Err(CoreError::ResourceNotFound)` - Frame 不在隔离区
600    /// * `Err(CoreError::OwnershipViolation)` - 代际不匹配
601    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        // 边界检查
609        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        // 代际验证:防止陈旧 FrameID 被复用到新帧
619        if frame.generation != expected_generation {
620            return Err(CoreError::ownership_violation(
621                "valid generation",
622                "invalid generation",
623            ));
624        }
625
626        // 更新状态
627        frame.state = FrameState::Free;
628
629        // 推进代际(防 ABA):与 release/quarantine 一致的写法
630        self.generation = self.generation.saturating_add(1);
631        frame.generation = self.generation;
632
633        // 放回 Free-List 栈
634        self.free_stack.push(frame_idx);
635
636        // 更新计数
637        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    /// 通过 Frame ID 隔离帧(无 Token 场景)
646    ///
647    /// 当 FrameToken 已被消费(如通过 Ring 传递给内核)时,
648    /// 只能通过 Frame ID 来隔离帧。此方法用于 Worker 数据面循环
649    /// 中的解析错误和准入拒绝场景。
650    ///
651    /// # 安全保证
652    /// 必须传入期望的代际号,用于验证帧未被回收重新分配。
653    /// 这防止了陈旧 FrameID 被恶意复用到新帧上。
654    ///
655    /// # 状态转换
656    /// Allocated/Processing/InRxRing/InTxRing/InCompletionRing → Quarantine
657    ///
658    /// # Arguments
659    /// * `frame_id` - Frame ID
660    /// * `expected_generation` - 期望的代际号(必须匹配当前帧的代际)
661    /// * `reason` - 隔离原因
662    ///
663    /// # Returns
664    /// * `Ok(())` - 成功隔离
665    /// * `Err(CoreError::OwnershipViolation)` - 代际不匹配或状态非法
666    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        // 边界检查
675        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        // 代际验证:防止陈旧 FrameID 被复用到新帧
682        if frame.generation != expected_generation {
683            return Err(CoreError::ownership_violation(
684                "valid generation",
685                "invalid generation",
686            ));
687        }
688
689        // 仅允许从活跃状态转换到隔离状态
690        match frame.state {
691            FrameState::Allocated
692            | FrameState::Processing
693            | FrameState::InRxRing
694            | FrameState::InTxRing
695            | FrameState::InCompletionRing => {
696                // 所有 in-use 状态(Allocated/Processing/InRxRing/InTxRing/InCompletionRing)
697                // 均由 allocated_count 跟踪(分配时经 allocate 计入),隔离时必须一律扣减,
698                // 否则守恒不变量 free + allocated + quarantined == capacity 被破坏。
699                // 不变量:这些状态只能由 allocate 产生,故 allocated_count 恒 ≥ 隔离前各帧
700                // 在 in-use 状态中者;此处 checked_sub 防 underflow 兜底不变量损坏场景。
701                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        // 推进代际(防 ABA):帧被隔离后再恢复/重分配时,旧代际句柄必然失效
717        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        // 隔离原因不再静默丢弃:以 debug 级结构化日志记录,便于事后审计定位
726        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    /// 推进代际号
736    ///
737    /// 代际号用于在热更新期间区分新旧资源,防止跨代际访问。
738    ///
739    /// # 溢出说明(CORE-009)
740    /// 使用 `saturating_add`:代际为 u64,每次分配/释放/隔离/回收推进一次,
741    /// 要在实际生命周期内达到 2^64 次推进不可达,故饱和语义不会隐藏真实溢出,
742    /// 保持既有行为(不改变 `-> u64` 签名引入错误传播)。
743    ///
744    /// # Returns
745    /// 新的代际号
746    #[inline]
747    pub fn advance_generation(&mut self) -> u64 {
748        self.generation = self.generation.saturating_add(1);
749        self.generation
750    }
751
752    /// 获取当前代际号
753    #[inline]
754    pub fn current_generation(&self) -> u64 {
755        self.generation
756    }
757
758    /// 推进 Epoch
759    ///
760    /// Epoch 用于标识配置快照的唯一版本,防止过期操作。
761    ///
762    /// # 溢出说明(CORE-009)
763    /// `saturating_add`:与代际同理,u64 在实际生命周期内不可达溢出。
764    ///
765    /// # Returns
766    /// 新的 Epoch 编号
767    #[inline]
768    pub fn advance_epoch(&mut self) -> u64 {
769        self.epoch = self.epoch.saturating_add(1);
770        self.epoch
771    }
772
773    /// 获取当前 Epoch 编号
774    #[inline]
775    pub fn current_epoch(&self) -> u64 {
776        self.epoch
777    }
778
779    /// 获取 Frame 信息(返回克隆副本)
780    ///
781    /// # Arguments
782    /// * `frame_id` - Frame ID
783    ///
784    /// # Returns
785    /// * `Some(FrameInfo)` - Frame 信息副本
786    /// * `None` - 未找到
787    #[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    /// 批量分配 Frame
793    ///
794    /// 一次性分配多个 Frame,减少函数调用开销。
795    /// 返回的令牌需要逐个归还。
796    ///
797    /// # Arguments
798    /// * `domain_id` - 域ID
799    /// * `count` - 要分配的数量
800    ///
801    /// # Returns
802    /// * `Ok(Vec<FrameToken>)` - 成功分配的令牌列表
803    /// * `Err(CoreError::QuotaExceeded)` - 资源不足(部分分配时已分配帧全部回滚)
804    pub fn allocate_batch(&mut self, domain_id: u32, count: u32) -> CoreResult<Vec<FrameToken>> {
805        // 预检查容量
806        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                    // 回滚:归还已分配的帧,避免 FrameToken drop 时不归还帧的泄漏。
820                    // 使用 release_recoverable:即使某个帧归还失败(不应发生),
821                    // 也尽力归还其余帧,且不 panic(CORE-024)。
822                    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    /// 批量归还 Frame
839    ///
840    /// # Arguments
841    /// * `tokens` - 所有权令牌列表(全部被消费)
842    ///
843    /// # Returns
844    /// * `Ok(())` - 成功归还
845    /// * `Err(CoreError)` - 某帧归还失败(剩余帧仍尽力归还,返回首个错误)
846    pub fn release_batch(&mut self, tokens: Vec<FrameToken>) -> CoreResult<()> {
847        let mut first_err: Option<CoreError> = None;
848        for token in tokens {
849            // 使用 release_recoverable:失败时 token 随错误返回,
850            // 避免 token 被 drop 导致帧永久泄漏
851            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    /// 验证所有权守恒
864    ///
865    /// 恒等式:空闲 + 已分配 + 隔离 = 总容量
866    ///
867    /// # Returns
868    /// * `Ok(())` - 守恒
869    /// * `Err(CoreError::Internal)` - 不守恒
870    #[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    /// 通过 Frame ID 归还 Frame(O(1),用于环形队列回收路径)
892    ///
893    /// 此方法用于 AF_XDP 环形队列完成回收路径,
894    /// 通过帧索引直接归还帧,无需 FrameToken。
895    ///
896    /// # 安全保证
897    /// 必须传入期望的代际号,用于验证帧未被回收重新分配。
898    /// 同时验证帧必须处于 Allocated 状态。
899    ///
900    /// # 安全约束
901    /// - 帧必须处于 Allocated 状态
902    /// - 代际号必须匹配当前帧的代际
903    /// - 调用者必须确保没有其他持有者在使用该帧
904    ///
905    /// # Arguments
906    /// * `frame_id` - Frame ID
907    /// * `expected_generation` - 期望的代际号(必须匹配当前帧的代际)
908    ///
909    /// # Returns
910    /// * `Ok(())` - 成功归还
911    /// * `Err(CoreError::OwnershipViolation)` - 帧不在 Allocated 状态或代际不匹配
912    #[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        // 边界检查(fail-closed):越界索引直接返回错误,禁止 panic
917        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        // 代际验证:防止陈旧 FrameID 被复用到新帧
923        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        // 推进代际(防 ABA):与 release/quarantine 一致,
937        // 使所有指向本帧的旧代际句柄在帧重新分配后必然校验失败
938        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    /// 获取底层帧信息切片(用于批量操作)
952    ///
953    /// # Returns
954    /// 帧信息的切片引用
955    #[inline]
956    pub fn frames(&self) -> &[FrameInfo] {
957        &self.frames
958    }
959
960    /// 获取 Free-List 栈的剩余容量
961    ///
962    /// # Returns
963    /// 栈中剩余的空闲帧数
964    #[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        // 分配
997        let token = pool.allocate(0).unwrap();
998        assert_eq!(pool.allocated_count(), 1);
999        assert_eq!(pool.free_count(), TEST_CAPACITY - 1);
1000
1001        // 验证所有权
1002        assert!(token.verify_ownership(0, 0).is_ok());
1003
1004        // 归还
1005        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        // 第三次分配应该失败
1018        let result = pool.allocate(0);
1019        assert!(result.is_err());
1020
1021        // 归还后应该可以再分配
1022        pool.release(token1).unwrap();
1023        let token3 = pool.allocate(0).unwrap();
1024        // ABA 修复:release 推进了代际,重新分配的令牌代际必然递增,
1025        // 旧代际 (0) 校验必须失败,新代际校验必须成功
1026        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        // 隔离
1042        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        // 回收
1048        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        // 初始状态守恒
1085        assert!(pool.verify_conservation().is_ok());
1086
1087        // 分配后守恒
1088        let token = pool.allocate(0).unwrap();
1089        assert!(pool.verify_conservation().is_ok());
1090
1091        // 归还后守恒
1092        pool.release(token).unwrap();
1093        assert!(pool.verify_conservation().is_ok());
1094
1095        // 隔离后守恒
1096        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        // 从隔离区回收后守恒(使用正确的 frame_id)
1102        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        // 批量分配10个
1121        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        // 批量归还
1127        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        // 尝试批量分配超过容量
1137        let result = pool.allocate_batch(0, 10);
1138        assert!(result.is_err());
1139
1140        // 确保没有部分分配
1141        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        // 尝试归还一个空闲帧的 token(直接构造无效token)
1150        // 这在实际使用中不可能发生,因为 FrameToken 只能通过 allocate 获取
1151        // 但我们测试一下状态检查
1152        let token = pool.allocate(0).unwrap();
1153        let frame_idx = token.frame_id().value();
1154
1155        // 先正常归还
1156        pool.release(token).unwrap();
1157
1158        // 现在该帧是 Free 状态
1159        // 如果我们能构造一个 FrameToken(实际不能,因为 pub(crate) 构造函数)
1160        // 这里仅验证 get_frame_info 返回正确状态
1161        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        // 分配全部
1170        let t0 = pool.allocate(0).unwrap(); // 弹出栈顶(idx=2)
1171        let t1 = pool.allocate(0).unwrap(); // 弹出栈顶(idx=1)
1172        let t2 = pool.allocate(0).unwrap(); // 弹出栈顶(idx=0)
1173
1174        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        // 归还第一个分配的帧
1179        pool.release(t0).unwrap(); // 压入栈顶(idx=2)
1180        let t3 = pool.allocate(0).unwrap(); // 弹出栈顶(idx=2)
1181        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        // 域0分配
1193        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        // 域1分配
1199        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        // 分配一个帧
1213        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        // 使用 release_by_id 归还
1219        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        // 再次释放应该失败(帧已空闲)
1224        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        // 尝试释放空闲帧(应该失败)
1233        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        // 越界索引必须 fail-closed 返回错误,禁止 panic
1242        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        // 池状态未被破坏
1249        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        // 分配唯一的帧并记录代际
1257        let token = pool.allocate(0).unwrap();
1258        let frame_id = token.frame_id();
1259        let gen_v1 = token.generation();
1260        let _ = token;
1261
1262        // 通过 release_by_id 归还:代际必须推进
1263        pool.release_by_id(frame_id, gen_v1).unwrap();
1264
1265        // 旧代际句柄立即失效(帧已空闲,且代际已推进)
1266        assert!(pool.release_by_id(frame_id, gen_v1).is_err());
1267
1268        // 帧重新分配后获得新代际,旧句柄 (frame_id, gen_v1) 依然失效(防 ABA)
1269        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        // 新代际句柄正常工作
1275        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        // 分配后归还,令牌被消费;帧重新分配后旧代际必然失配
1284        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        // 重新分配后帧代际已推进,与首次分配不同
1290        assert_ne!(token2.generation(), gen_v1);
1291        pool.release(token2).unwrap();
1292        assert!(pool.verify_conservation().is_ok());
1293    }
1294
1295    // ===== quarantine_by_id 状态转换测试 =====
1296
1297    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    // ===== CORE-007:Processing 状态帧隔离后守恒等式仍成立 =====
1340
1341    #[test]
1342    fn test_quarantine_by_id_processing_conservation() {
1343        let mut pool = create_test_pool();
1344        // 分配多个帧,其中一帧进入 Processing 状态
1345        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        // 隔离前守恒
1353        assert!(pool.verify_conservation().is_ok());
1354
1355        pool.quarantine_by_id(frame_id, token_gen, "processing-conflict").unwrap();
1356
1357        // 隔离后守恒等式 free + allocated + quarantined == capacity 仍成立
1358        assert_eq!(pool.quarantined_count(), 1);
1359        assert_eq!(pool.allocated_count(), 0);
1360        assert!(pool.verify_conservation().is_ok());
1361    }
1362
1363    // ===== CORE-008:recover_from_quarantine_by_id 代际验证 =====
1364
1365    #[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(); // 隔离推进代际
1371
1372        let correct_gen = pool.get_frame_info(frame_id).unwrap().generation();
1373
1374        // 错误代际:回收必须失败,帧保持隔离,守恒不被破坏
1375        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        // 正确代际:回收成功
1382        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    // ===== quarantine_by_id 状态冲突测试 =====
1437
1438    #[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    // ===== FrameState 变体值/排序测试 =====
1464
1465    #[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    // ===== 边界条件测试 =====
1499
1500    #[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    // ===== get_frame_info 越界测试 =====
1546
1547    #[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    // ===== FrameId From<u32> trait 测试 =====
1562
1563    #[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    // ===== FrameInfo 字段测试 =====
1571
1572    #[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    // ===== 零堆分配验证(热路径测试) =====
1586
1587    #[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    // ===== recover_from_quarantine 代际推进(防 ABA 回归测试) =====
1601
1602    #[test]
1603    fn test_recover_from_quarantine_advances_generation() {
1604        let mut pool = FramePool::new("aba_recover", 1, TEST_FRAME_SIZE);
1605
1606        // 分配并隔离,记录隔离后的帧代际
1607        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        // 回收必须推进代际,与 release/quarantine 行为一致
1613        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        // 隔离 → 读出隔离态代际(回收前)→ 回收 → 重新分配
1623        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        // 代际经历 隔离→回收 两次推进,三个采样点互不相同
1634        assert_ne!(gen_v1, gen_quarantined);
1635        assert_ne!(gen_quarantined, gen_v2);
1636        assert_ne!(gen_v1, gen_v2);
1637
1638        // 旧代际句柄(首次分配代际与隔离态代际)必须全部失效
1639        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        // 新代际句柄正常工作
1644        pool.release_by_id(frame_id, gen_v2).unwrap();
1645        assert!(pool.verify_conservation().is_ok());
1646    }
1647
1648    // ===== FramePool::new 非法参数回退(行为保持 + 不 panic) =====
1649
1650    #[test]
1651    fn test_new_invalid_params_fallback_no_panic() {
1652        // capacity == 0 / frame_size == 0:回退 1x1,不 panic,且功能可用
1653        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        // try_new 显式路径保持报错
1664        assert!(FramePool::try_new("t", 0, 1).is_err());
1665        assert!(FramePool::try_new("t", 1, 0).is_err());
1666    }
1667}