ax-lazyinit 技术文档
路径:
components/ax-lazyinit类型:库 crate 分层:组件层 / 一次性初始化基础件 版本:0.4.2文档依据:Cargo.toml、README.md、src/lib.rs
ax-lazyinit 提供一个面向静态对象的一次性初始化容器 LazyInit<T>。它以 AtomicU8 + UnsafeCell<MaybeUninit<T>> 实现无分配、可并发争用的“只初始化一次”语义。它属于运行时叶子基础件:不是资源生命周期框架、不是依赖注入容器,也不是带阻塞唤醒机制的完整 OnceCell 替代品。
1. 架构设计分析
1.1 设计定位
ArceOS/StarryOS/Axvisor 的很多全局对象都不 能在编译期直接构造:
- 平台设备要等 MMIO 基址探测完才能创建。
- 运行时队列、地址空间、网络状态要等初始化流程走到相应阶段才能建立。
- 又因为很多路径处于
no_std和早期启动阶段,不能依赖重型同步原语。
ax-lazyinit 正是在这个背景下提供一个极小的“一次建好,之后只读/少量可变访问”的容器。
1.2 核心类型与状态机
LazyInit<T>:主体类型,内部持有初始化状态和未初始化存储。UNINIT/INITIALIZING/INITED:三态状态机。
其状态流转很简单:
1.3 并发初始化主线
call_once() 的执行路径是整个 crate 的核心:
- 先走一次
is_inited()快路径。 - 用
compare_exchange_weak尝试把状态从UNINIT改成INITIALIZING。 - 抢到初始化权的线程执行闭包、写入
MaybeUninit<T>,最后store(INITED, Release)。 - 其他线程若看到
INITIALIZING,就通过spin_loop()忙等到初始化完成。
这说明 ax-lazyinit 的并发模型是“原子争抢 + 自旋等待”,不是睡 眠阻塞或任务挂起。
1.4 访问模型
init_once(data):以直接值初始化,若已初始化则 panic。call_once(f):以闭包初始化,成功初始化返回Some(&T),否则返回None。get()/get_mut():显式检查是否已初始化。Deref/DerefMut:若未初始化会 panic。get_unchecked()/get_mut_unchecked():供调用方在已知状态下绕过检查。
此外,Drop 会在对象已初始化时主动销毁内部值,因此它并不是“永不释放”的泄漏型 once cell。
2. 核心功能说明
2.1 主要功能
- 为静态或长期存活对象提供一次性初始化容器。
- 在多核竞争下保证只有一个初始化者成功写入值。
- 在初始化后提供低开销的引用访问。