83 lines
1.9 KiB
Rust
83 lines
1.9 KiB
Rust
use crate::{
|
|
proc::proc::{sched, Proc, ProcState},
|
|
trap::{pop_intr_off, push_intr_off},
|
|
};
|
|
use core::{
|
|
ffi::c_char,
|
|
ptr::null_mut,
|
|
sync::atomic::{AtomicBool, Ordering},
|
|
};
|
|
|
|
#[repr(C)]
|
|
#[derive(Default)]
|
|
pub struct Spinlock {
|
|
pub locked: AtomicBool,
|
|
}
|
|
impl Spinlock {
|
|
/// Initializes a `Spinlock`.
|
|
pub const fn new() -> Spinlock {
|
|
Spinlock {
|
|
locked: AtomicBool::new(false),
|
|
}
|
|
}
|
|
pub unsafe fn lock_unguarded(&self) {
|
|
push_intr_off();
|
|
|
|
while self.locked.swap(true, Ordering::Acquire) {
|
|
core::hint::spin_loop();
|
|
}
|
|
}
|
|
pub fn lock(&self) -> SpinlockGuard<'_> {
|
|
unsafe {
|
|
self.lock_unguarded();
|
|
}
|
|
SpinlockGuard { lock: self }
|
|
}
|
|
pub unsafe fn unlock(&self) {
|
|
self.locked.store(false, Ordering::Release);
|
|
|
|
pop_intr_off();
|
|
}
|
|
}
|
|
|
|
pub struct SpinlockGuard<'l> {
|
|
pub lock: &'l Spinlock,
|
|
}
|
|
impl<'l> SpinlockGuard<'l> {
|
|
/// Sleep until `wakeup(chan)` is called somewhere else, yielding the lock until then.
|
|
pub unsafe fn sleep(&self, chan: *mut core::ffi::c_void) {
|
|
let proc = Proc::current().unwrap();
|
|
let _guard = proc.lock.lock();
|
|
self.lock.unlock();
|
|
|
|
// Put the process to sleep.
|
|
proc.chan = chan;
|
|
proc.state = ProcState::Sleeping;
|
|
sched();
|
|
|
|
// Tidy up and reacquire the lock.
|
|
proc.chan = null_mut();
|
|
self.lock.lock_unguarded();
|
|
}
|
|
}
|
|
impl<'l> Drop for SpinlockGuard<'l> {
|
|
fn drop(&mut self) {
|
|
unsafe { self.lock.unlock() }
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn initlock(lock: *mut Spinlock, _name: *mut c_char) {
|
|
*lock = Spinlock::new();
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn acquire(lock: *mut Spinlock) {
|
|
(*lock).lock_unguarded();
|
|
}
|
|
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn release(lock: *mut Spinlock) {
|
|
(*lock).unlock();
|
|
}
|