Skip to content

Commit

Permalink
refactor: add a safe OnceLock data structure
Browse files Browse the repository at this point in the history
Taken from rust, very minor modifications on my side.
  • Loading branch information
n1tram1 committed Oct 2, 2024
1 parent a9e37a3 commit 6c9fd3b
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 39 deletions.
18 changes: 8 additions & 10 deletions hal_aarch64/src/irq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@ use hal_core::{Error, TimerCallbackFn};
use crate::devices::gicv2::GicV2;

use hal_core::mm::PageAlloc;
use hal_core::once_lock::OnceLock;
use hal_core::IrqOps;

use core::cell::OnceCell;

use cortex_a::registers::*;
use tock_registers::interfaces::{ReadWriteable, Writeable};

Expand Down Expand Up @@ -100,7 +99,8 @@ enum InterruptType {
SerrorLowerElAarch32,
}

static mut IRQS: core::cell::OnceCell<&Aarch64Irqs> = core::cell::OnceCell::new();
/// Need a once lock because this is unset until Aarch64Irqs::init has been called by the hal.
static IRQS: OnceLock<&Aarch64Irqs> = OnceLock::new();

#[no_mangle]
unsafe extern "C" fn aarch64_common_trap(offset: u64) {
Expand Down Expand Up @@ -133,17 +133,17 @@ unsafe extern "C" fn aarch64_common_trap(offset: u64) {

#[derive(Debug)]
pub struct Aarch64Irqs {
irq_chip: OnceCell<GicV2>,
irq_chip: OnceLock<GicV2>,
timer_callback: AtomicPtr<TimerCallbackFn>,
}

/// Safety: I know what I'm doing :D
/// Safety: Not safe because GicV2 is not Sync
unsafe impl Sync for Aarch64Irqs {}

impl Aarch64Irqs {
pub const fn new() -> Self {
Self {
irq_chip: OnceCell::new(),
irq_chip: OnceLock::new(),
timer_callback: AtomicPtr::new(ptr::null_mut()),
}
}
Expand Down Expand Up @@ -185,10 +185,8 @@ impl Aarch64Irqs {
impl IrqOps for Aarch64Irqs {
fn init(&'static self) {
cortex_a::registers::VBAR_EL1.set(el1_vector_table as usize as u64);
unsafe {
IRQS.set(self)
.expect("looks like init has already been called")
};
IRQS.set(self)
.expect("looks like init has already been called");
}

fn init_irq_chip(&self, _allocator: &impl PageAlloc) -> Result<(), Error> {
Expand Down
25 changes: 0 additions & 25 deletions hal_core/src/fake_once_lock.rs

This file was deleted.

6 changes: 3 additions & 3 deletions hal_core/src/hal.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
use super::fake_once_lock::FakeOnceLock;
use super::mm::{self, Mmu, PageAlloc, PageMap};
use super::once_lock::OnceLock;
use super::AddressRange;
use super::Error;
use super::ReentrantSpinlock;
use super::{IrqOps, TimerCallbackFn};

pub struct Hal<P: PageMap + 'static, I: IrqOps> {
kpt: FakeOnceLock<ReentrantSpinlock<&'static mut P>>,
kpt: OnceLock<ReentrantSpinlock<&'static mut P>>,
irq_ops: I,
}

impl<P: PageMap + Mmu + 'static, I: IrqOps> Hal<P, I> {
pub const fn new(irq_ops: I) -> Hal<P, I> {
Self {
kpt: FakeOnceLock::new(),
kpt: OnceLock::new(),
irq_ops,
}
}
Expand Down
3 changes: 2 additions & 1 deletion hal_core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
#![no_std]
#![feature(const_mut_refs)]
#![feature(never_type)]

use core::convert::Into;
use core::ops::Range;

mod hal;
pub use hal::Hal;

mod fake_once_lock;
pub mod once_lock;

mod reentrant_spinlock;
pub use reentrant_spinlock::ReentrantSpinlock;
Expand Down
115 changes: 115 additions & 0 deletions hal_core/src/once_lock.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// Taken from https://doc.rust-lang.org/nightly/src/std/sync/once_lock.rs.html#121-128
// Not mine!
use spin::Once;

use core::cell::UnsafeCell;
use core::fmt;
use core::mem::MaybeUninit;

pub struct OnceLock<T> {
once: Once,
value: UnsafeCell<MaybeUninit<T>>,
}

impl<T> OnceLock<T> {
pub const fn new() -> OnceLock<T> {
OnceLock {
once: Once::new(),
value: UnsafeCell::new(MaybeUninit::uninit()),
}
}

pub fn get(&self) -> Option<&T> {
if self.is_initialized() {
Some(unsafe { self.get_unchecked() })
} else {
None
}
}

pub fn set(&self, value: T) -> Result<(), T> {
match self.try_insert(value) {
Ok(_) => Ok(()),
Err((_, value)) => Err(value),
}
}

fn try_insert(&self, value: T) -> Result<&T, (&T, T)> {
let mut value = Some(value);
let res = self.get_or_init(|| value.take().unwrap());
match value {
None => Ok(res),
Some(value) => Err((res, value)),
}
}

pub fn get_or_init<F>(&self, f: F) -> &T
where
F: FnOnce() -> T,
{
match self.get_or_try_init(|| Ok::<T, !>(f())) {
Ok(val) => val,
Err(_) => unreachable!(), // never type above
}
}

pub fn get_or_try_init<F, E>(&self, f: F) -> Result<&T, E>
where
F: FnOnce() -> Result<T, E>,
{
// Fast path check
// NOTE: We need to perform an acquire on the state in this method
// in order to correctly synchronize `LazyLock::force`. This is
// currently done by calling `self.get()`, which in turn calls
// `self.is_initialized()`, which in turn performs the acquire.
if let Some(value) = self.get() {
return Ok(value);
}
self.initialize(f)?;

debug_assert!(self.is_initialized());

// SAFETY: The inner value has been initialized
Ok(unsafe { self.get_unchecked() })
}

fn is_initialized(&self) -> bool {
self.once.is_completed()
}

fn initialize<F, E>(&self, f: F) -> Result<(), E>
where
F: FnOnce() -> Result<T, E>,
{
let mut res: Result<(), E> = Ok(());
let slot = &self.value;

self.once.call_once(|| match f() {
Ok(value) => {
unsafe { (*slot.get()).write(value) };
}
Err(e) => {
res = Err(e);
}
});
res
}

unsafe fn get_unchecked(&self) -> &T {
debug_assert!(self.is_initialized());
(*self.value.get()).assume_init_ref()
}
}

impl<T: fmt::Debug> fmt::Debug for OnceLock<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut d = f.debug_tuple("OnceLock");
match self.get() {
Some(v) => d.field(v),
None => d.field(&format_args!("<uninit>")),
};
d.finish()
}
}

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

0 comments on commit 6c9fd3b

Please sign in to comment.