use alloc::vec::Vec;
use core::ops::{Deref, DerefMut};
use super::{MigrationGuard, SpinLock, SpinLockGuard};
use crate::{
sched::{CpuId, cpu_topology_len},
thread::TaskError,
};
pub struct LocalLock<T> {
cpus: Vec<SpinLock<T>>,
}
#[must_use]
pub struct LocalLockGuard<'a, T> {
owner: SpinLockGuard<'a, T>,
migration: MigrationGuard,
}
impl<T> LocalLock<T> {
pub fn new(mut init: impl FnMut(CpuId) -> T) -> Result<Self, TaskError> {
let cpus = (0..cpu_topology_len()?)
.map(|cpu| SpinLock::new(init(CpuId::new(cpu as u32))))
.collect();
Ok(Self { cpus })
}
pub fn lock(&self) -> LocalLockGuard<'_, T> {
let migration = MigrationGuard::new().expect("local lock requires task context");
let owner = self.cpus[migration.cpu().as_usize()].lock();
LocalLockGuard { owner, migration }
}
pub fn try_lock(&self) -> Option<LocalLockGuard<'_, T>> {
let migration = MigrationGuard::new().ok()?;
let owner = self.cpus[migration.cpu().as_usize()].try_lock()?;
Some(LocalLockGuard { owner, migration })
}
pub fn lock_irqsave(&self) -> LocalLockGuard<'_, T> {
self.lock()
}
pub fn try_lock_irqsave(&self) -> Option<LocalLockGuard<'_, T>> {
self.try_lock()
}
}
impl<T> LocalLockGuard<'_, T> {
pub fn cpu(&self) -> CpuId {
self.migration.cpu()
}
}
impl<T> Deref for LocalLockGuard<'_, T> {
type Target = T;
fn deref(&self) -> &T {
&self.owner
}
}
impl<T> DerefMut for LocalLockGuard<'_, T> {
fn deref_mut(&mut self) -> &mut T {
&mut self.owner
}
}