new: lazy_init (#230)

This commit is contained in:
login 2023-04-06 19:01:30 +08:00 committed by GitHub
parent e0dfd4d5d7
commit 766127209e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 161 additions and 1 deletions

View File

@ -0,0 +1,159 @@
// Copyright (C) DragonOS Community longjin
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
// Or you can visit https://www.gnu.org/licenses/gpl-2.0.html
#![allow(dead_code)]
use core::cell::UnsafeCell;
use core::fmt::Debug;
use core::mem::MaybeUninit;
use core::ops::{Deref, DerefMut};
use core::sync::atomic::{AtomicBool, Ordering};
use super::spinlock::SpinLock;
/// A wrapper around a value that is initialized lazily.
/// The value is initialized the first time it is accessed.
/// This is useful for initializing values that are expensive to initialize.
/// The value is initialized using the provided closure.
/// The closure is only called once, and the result is cached.
/// The value is not initialized if it is never accessed.
pub struct Lazy<T: Sync> {
/// The lock that is used to ensure that only one thread calls the init function at the same time.
init_lock: SpinLock<()>,
/// The value that is initialized lazily.
value: UnsafeCell<MaybeUninit<T>>,
/// Whether the value has been initialized.
initialized: AtomicBool,
}
impl<T: Sync> Lazy<T> {
/// Creates a new `Lazy` value that will be initialized with the
/// result of the closure `init`.
pub const fn new() -> Lazy<T> {
Lazy {
value: UnsafeCell::new(MaybeUninit::uninit()),
initialized: AtomicBool::new(false),
init_lock: SpinLock::new(()),
}
}
/// Returns true if the value has been initialized.
#[inline(always)]
pub fn initialized(&self) -> bool {
let initialized = self.initialized.load(Ordering::Acquire);
if initialized {
return true;
}
return false;
}
/// Ensures that this lazy value is initialized. If the value has not
/// yet been initialized, this will raise a panic.
#[inline(always)]
pub fn ensure(&self) {
if self.initialized() {
return;
}
panic!("Lazy value was not initialized");
}
pub fn init(&self, value: T) {
assert!(!self.initialized());
// We need this lock to ensure that only one thread calls the init function at the same time.
let _init_guard = self.init_lock.lock();
// Check again, in case another thread initialized it while we were waiting for the lock.
let initialized = self.initialized();
if initialized {
return;
}
unsafe {
(*self.value.get()).as_mut_ptr().write(value);
}
self.initialized.store(true, Ordering::Release);
}
/// Forces the evaluation of this lazy value and returns a reference to
/// the result. This is equivalent to the `Deref` impl, but is explicit.
/// This will initialize the value if it has not yet been initialized.
pub fn get(&self) -> &T {
self.ensure();
return unsafe { self.get_unchecked() };
}
/// Returns a reference to the value if it has been initialized.
/// Otherwise, returns `None`.
pub fn try_get(&self) -> Option<&T> {
if self.initialized() {
return Some(unsafe { self.get_unchecked() });
}
return None;
}
/// Forces the evaluation of this lazy value and returns a mutable
/// reference to the result. This is equivalent to the `DerefMut` impl,
/// but is explicit. This will initialize the value if it has not yet
/// been initialized.
pub fn get_mut(&mut self) -> &mut T {
self.ensure();
return unsafe { self.get_mut_unchecked() };
}
#[inline(always)]
pub unsafe fn get_unchecked(&self) -> &T {
return &*(*self.value.get()).as_ptr();
}
#[inline(always)]
pub unsafe fn get_mut_unchecked(&mut self) -> &mut T {
return &mut *(*self.value.get()).as_mut_ptr();
}
}
impl<T: Sync> Deref for Lazy<T> {
type Target = T;
#[inline(always)]
fn deref(&self) -> &T {
return self.get();
}
}
impl<T: Sync> DerefMut for Lazy<T> {
#[inline(always)]
fn deref_mut(&mut self) -> &mut T {
return self.get_mut();
}
}
impl<T: Sync + Debug> Debug for Lazy<T> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
if let Some(value) = self.try_get() {
return write!(f, "Lazy({:?})", value);
} else {
return write!(f, "Lazy(uninitialized)");
}
}
}
impl<T: Sync> Drop for Lazy<T> {
fn drop(&mut self) {
if self.initialized() {
unsafe {
(*self.value.get()).as_mut_ptr().drop_in_place();
}
}
}
}

View File

@ -13,5 +13,6 @@ pub mod spinlock;
pub mod vec_cursor;
#[macro_use]
pub mod volatile;
pub mod wait_queue;
pub mod keyboard_parser;
pub mod lazy_init;
pub mod wait_queue;