use crate::{
kty::{AtomicInt, c_int, c_uint},
lmem,
syscall::{futex_wait, futex_wake},
util::{error},
result::{Result},
};
use core::{mem};
pub static DUMMY: Lock = Lock::new();
const UNLOCKED: c_int = 0;
const LOCKED: c_int = 1;
const WAITING: c_int = 2;
pub enum LockStatus {
Unlocked,
Locked,
Waiting,
}
#[repr(C)]
#[derive(Eq)]
pub struct Lock {
val: AtomicInt,
}
impl PartialEq for Lock {
fn eq(&self, other: &Lock) -> bool {
addr(self) == addr(other)
}
}
impl<'a> Lock {
pub const fn new() -> Lock {
Lock { val: AtomicInt::new(UNLOCKED) }
}
fn guard(&'a self) -> LockGuard<'a> {
LockGuard { lock: self }
}
pub unsafe fn unlock(&self) {
self.guard();
}
pub unsafe fn as_atomic(&self) -> &AtomicInt {
&self.val
}
pub fn status(&self) -> LockStatus {
match self.val.load_unordered() {
UNLOCKED => LockStatus::Unlocked,
LOCKED => LockStatus::Locked,
_ => LockStatus::Waiting,
}
}
pub fn try_lock(&'a self) -> Result<LockGuard<'a>> {
if self.val.compare_exchange(UNLOCKED, LOCKED) == UNLOCKED {
Ok(self.guard())
} else {
Err(error::ResourceBusy)
}
}
pub fn lock(&'a self) -> LockGuard<'a> {
let mut status = self.val.compare_exchange(UNLOCKED, LOCKED);
if status == UNLOCKED {
return self.guard();
}
loop {
if status == WAITING ||
self.val.compare_exchange(LOCKED, WAITING) != UNLOCKED {
futex_wait(&self.val, WAITING, None);
}
status = self.val.compare_exchange(UNLOCKED, WAITING);
if status == UNLOCKED {
return self.guard();
}
}
}
pub fn try_lock_for(&'a self, mut time: Time) -> Result<LockGuard<'a>> {
let mut status = self.val.compare_exchange(UNLOCKED, LOCKED);
if status == UNLOCKED {
return Ok(self.guard());
}
let now = clock::MONO_RAW.get_time()?;
let then = now + time;
loop {
if status == WAITING ||
self.val.compare_exchange(LOCKED, WAITING) != UNLOCKED {
let spec = time_to_timespec(time);
match rv!(futex_wait(&self.val, WAITING, Some(&spec))) {
Err(error::TimedOut) => break,
_ => { },
}
}
status = self.val.compare_exchange(UNLOCKED, WAITING);
if status == UNLOCKED {
return Ok(self.guard());
}
let now = clock::MONO_RAW.get_time()?;
if now < then {
time = then - now;
} else {
break;
}
}
Err(error::TimedOut)
}
}
pub struct LockGuard<'a> {
lock: &'a Lock,
}
impl<'a> LockGuard<'a> {
pub fn as_lock(&self) -> &'a Lock {
self.lock
}
pub fn unlock(self) -> &'a Lock {
self.lock
}
}
impl<'a> Drop for LockGuard<'a> {
fn drop(&mut self) {
if self.lock.val.sub(1) != LOCKED {
self.lock.val.store(UNLOCKED);
futex_wake(&self.lock.val, 1);
}
}
}