116 lines
3.1 KiB
Rust

use super::LockStrategy;
use crate::proc::{
process::{Process, ProcessState},
scheduler::{sched, sleep, wakeup},
};
use core::{
cell::UnsafeCell,
ptr::{addr_of, null_mut},
sync::atomic::{AtomicBool, Ordering},
};
pub struct Lock {
locked: AtomicBool,
lock_strategy: UnsafeCell<LockStrategy>,
}
impl Lock {
pub const fn new() -> Lock {
Lock {
locked: AtomicBool::new(false),
lock_strategy: UnsafeCell::new(LockStrategy::Spin),
}
}
pub fn lock_strategy(&self) -> LockStrategy {
unsafe { *self.lock_strategy.get() }
}
pub unsafe fn lock_unguarded(&self, lock_strategy: LockStrategy) {
// Lock it first, then store the lock strategy.
match lock_strategy {
LockStrategy::Spin => {
crate::arch::trap::push_intr_off();
while self.locked.swap(true, Ordering::Acquire) {
core::hint::spin_loop();
}
}
LockStrategy::Sleep => {
while self.locked.swap(true, Ordering::Acquire) {
// Put the process to sleep until the mutex gets released.
sleep(addr_of!(*self).cast_mut().cast());
}
}
};
*self.lock_strategy.get() = lock_strategy;
}
pub fn lock(&self, lock_strategy: LockStrategy) -> LockGuard<'_> {
unsafe {
self.lock_unguarded(lock_strategy);
}
LockGuard { lock: self }
}
pub fn lock_spinning(&self) -> LockGuard<'_> {
self.lock(LockStrategy::Spin)
}
pub fn lock_sleeping(&self) -> LockGuard<'_> {
self.lock(LockStrategy::Sleep)
}
pub unsafe fn unlock(&self) {
let lock_strategy = self.lock_strategy();
self.locked.store(false, Ordering::Release);
match lock_strategy {
LockStrategy::Spin => {
crate::arch::trap::pop_intr_off();
}
LockStrategy::Sleep => {
wakeup(addr_of!(*self).cast_mut().cast());
}
}
}
}
impl Default for Lock {
fn default() -> Lock {
Lock::new()
}
}
impl Clone for Lock {
fn clone(&self) -> Self {
Lock {
locked: AtomicBool::new(self.locked.load(Ordering::SeqCst)),
lock_strategy: UnsafeCell::new(self.lock_strategy()),
}
}
}
unsafe impl Sync for Lock {}
pub struct LockGuard<'l> {
pub lock: &'l Lock,
}
impl<'l> LockGuard<'l> {
/// Sleep until `wakeup(chan)` is called somewhere
/// else, yielding access to the lock until then.
pub unsafe fn sleep(&self, chan: *mut core::ffi::c_void) {
let proc = Process::current().unwrap();
let _guard = proc.lock.lock();
let strategy = self.lock.lock_strategy();
self.lock.unlock();
// Put the process to sleep.
proc.chan = chan;
proc.state = ProcessState::Sleeping;
sched();
// Tidy up and reacquire the lock.
proc.chan = null_mut();
self.lock.lock_unguarded(strategy);
}
}
impl<'l> Drop for LockGuard<'l> {
fn drop(&mut self) {
unsafe { self.lock.unlock() }
}
}