Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
equation314 committed Jul 10, 2024
0 parents commit b295873
Show file tree
Hide file tree
Showing 4 changed files with 228 additions and 0 deletions.
55 changes: 55 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: CI

on: [push, pull_request]

jobs:
ci:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
rust-toolchain: [nightly]
targets: [x86_64-unknown-linux-gnu, x86_64-unknown-none, riscv64gc-unknown-none-elf, aarch64-unknown-none-softfloat]
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly
with:
toolchain: ${{ matrix.rust-toolchain }}
components: rust-src, clippy, rustfmt
targets: ${{ matrix.targets }}
- name: Check rust version
run: rustc --version --verbose
- name: Check code format
run: cargo fmt --all -- --check
- name: Clippy
run: cargo clippy --target ${{ matrix.targets }} --all-features -- -A clippy::new_without_default
- name: Build
run: cargo build --target ${{ matrix.targets }} --all-features
- name: Unit test
if: ${{ matrix.targets == 'x86_64-unknown-linux-gnu' }}
run: cargo test --target ${{ matrix.targets }} -- --nocapture

doc:
runs-on: ubuntu-latest
strategy:
fail-fast: false
permissions:
contents: write
env:
default-branch: ${{ format('refs/heads/{0}', github.event.repository.default_branch) }}
RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly
- name: Build docs
continue-on-error: ${{ github.ref != env.default-branch && github.event_name != 'pull_request' }}
run: |
cargo doc --no-deps --all-features
printf '<meta http-equiv="refresh" content="0;url=%s/index.html">' $(cargo tree | head -1 | cut -d' ' -f1) > target/doc/index.html
- name: Deploy to Github Pages
if: ${{ github.ref == env.default-branch }}
uses: JamesIves/github-pages-deploy-action@v4
with:
single-commit: true
branch: gh-pages
folder: target/doc
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/target
/.vscode
.DS_Store
Cargo.lock
12 changes: 12 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "lazy_init"
version = "0.1.0"
edition = "2021"
authors = ["Yuekai Jia <equation618@gmail.com>"]
description = "A wrapper for lazy initialized values without concurrency safety but more efficient"
license = "GPL-3.0-or-later OR Apache-2.0 OR MulanPSL-2.0"
homepage = "https://github.com/arceos-org/arceos"
repository = "https://github.com/arceos-org/lazy_init"
documentation = "https://arceos-org.github.io/lazy_init"

[dependencies]
157 changes: 157 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
//! A wrapper for lazy initialized values.
//!
//! Unlike [`lazy_static`][1], this crate does not provide concurrency safety.
//! The value **MUST** be used after only **ONE** initialization. However, it
//! can be more efficient, as there is no need to check whether other threads
//! are also performing initialization at the same time.
//!
//! # Examples
//!
//! ```
//! use lazy_init::LazyInit;
//!
//! static VALUE: LazyInit<u32> = LazyInit::new();
//! assert!(!VALUE.is_init());
//! // println!("{}", *VALUE); // panic: use uninitialized value
//! assert_eq!(VALUE.try_get(), None);
//!
//! VALUE.init_by(233);
//! // VALUE.init_by(666); // panic: already initialized
//! assert!(VALUE.is_init());
//! assert_eq!(*VALUE, 233);
//! assert_eq!(VALUE.try_get(), Some(&233));
//! ```
//!
//! [1]: https://docs.rs/lazy_static/latest/lazy_static/
#![no_std]

use core::cell::UnsafeCell;
use core::fmt;
use core::mem::MaybeUninit;
use core::ops::{Deref, DerefMut};
use core::sync::atomic::{AtomicBool, Ordering};

/// A wrapper of a lazy initialized value.
///
/// It implements [`Deref`] and [`DerefMut`]. The caller must use the dereference
/// operation after initialization, otherwise it will panic.
pub struct LazyInit<T> {
inited: AtomicBool,
data: UnsafeCell<MaybeUninit<T>>,
}

unsafe impl<T: Send + Sync> Sync for LazyInit<T> {}
unsafe impl<T: Send> Send for LazyInit<T> {}

impl<T> LazyInit<T> {
/// Creates a new uninitialized value.
pub const fn new() -> Self {
Self {
inited: AtomicBool::new(false),
data: UnsafeCell::new(MaybeUninit::uninit()),
}
}

/// Initializes the value.
///
/// # Panics
///
/// Panics if the value is already initialized.
pub fn init_by(&self, data: T) {
assert!(!self.is_init());
unsafe { (*self.data.get()).as_mut_ptr().write(data) };
self.inited.store(true, Ordering::Release);
}

/// Checks whether the value is initialized.
pub fn is_init(&self) -> bool {
self.inited.load(Ordering::Acquire)
}

/// Gets a reference to the value.
///
/// Returns [`None`] if the value is not initialized.
pub fn try_get(&self) -> Option<&T> {
if self.is_init() {
unsafe { Some(&*(*self.data.get()).as_ptr()) }
} else {
None
}
}

fn check_init(&self) {
if !self.is_init() {
panic!(
"Use uninitialized value: {:?}",
core::any::type_name::<Self>()
)
}
}

#[inline]
fn get(&self) -> &T {
self.check_init();
unsafe { self.get_unchecked() }
}

#[inline]
fn get_mut(&mut self) -> &mut T {
self.check_init();
unsafe { self.get_mut_unchecked() }
}

/// Gets the reference to the value without checking if it is initialized.
///
/// # Safety
///
/// Must be called after initialization.
#[inline]
pub unsafe fn get_unchecked(&self) -> &T {
&*(*self.data.get()).as_ptr()
}

/// Get a mutable reference to the value without checking if it is initialized.
///
/// # Safety
///
/// Must be called after initialization.
#[inline]
pub unsafe fn get_mut_unchecked(&mut self) -> &mut T {
&mut *(*self.data.get()).as_mut_ptr()
}
}

impl<T: fmt::Debug> fmt::Debug for LazyInit<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.try_get() {
Some(s) => write!(f, "LazyInit {{ data: ")
.and_then(|()| s.fmt(f))
.and_then(|()| write!(f, "}}")),
None => write!(f, "LazyInit {{ <uninitialized> }}"),
}
}
}

impl<T> Deref for LazyInit<T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
self.get()
}
}

impl<T> DerefMut for LazyInit<T> {
#[inline]
fn deref_mut(&mut self) -> &mut T {
self.get_mut()
}
}

impl<T> Drop for LazyInit<T> {
fn drop(&mut self) {
if self.is_init() {
unsafe { core::ptr::drop_in_place((*self.data.get()).as_mut_ptr()) };
}
}
}

0 comments on commit b295873

Please sign in to comment.