Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 159 additions & 0 deletions kernel/src/libs/lazy_init.rs
Original file line number Diff line number Diff line change
@@ -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();
}
}
}
}
3 changes: 2 additions & 1 deletion kernel/src/libs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;