use crate::prelude_lib::*;
use std::cell::UnsafeCell;
use std::thread::ThreadId;
fn thread_id() -> ThreadId {
::std::thread::current().id()
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum LockState {
Open,
Write(ThreadId),
Read(u64),
Poison,
}
pub struct Locked {
pub obj: UnsafeCell<Box<dyn Any>>,
pub state: LockState,
pub name: Name,
}
impl fmt::Debug for Locked {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Locked({})::{:?}", self.name, self.state)
}
}
impl Locked {
pub fn new(obj: Box<dyn Any>, name: Name) -> Box<Self> {
Box::new(Locked {
obj: UnsafeCell::new(obj),
state: LockState::Open,
name,
})
}
pub fn is_poisoned(&self) -> bool {
self.state == LockState::Poison
}
pub fn can(&self, access: Access) -> bool {
match (self.state, access) {
(LockState::Open, _) => true,
(LockState::Read(_), Access::Read) => true,
(LockState::Read(_), Access::Write) => false,
(LockState::Write(orig), _) if orig == thread_id() => {
panic!("thread deadlock")
},
(LockState::Write(_), _) => false,
(LockState::Poison, _) => false,
}
}
pub fn acquire(&mut self, access: Access) {
self.state = match (self.state, access) {
(LockState::Write(_), Access::Read) => {
panic!("kernel multi-locked object via 'WR'")
},
(LockState::Write(_), Access::Write) => {
panic!("kernel multi-locked object via 'WW'")
},
(LockState::Read(_), Access::Write) => {
panic!("kernel multi-locked object via 'RW'")
},
(LockState::Read(n), Access::Read) => LockState::Read(n + 1), (LockState::Open, Access::Read) => LockState::Read(0),
(LockState::Open, Access::Write) => LockState::Write(thread_id()),
(LockState::Poison, _) => {
panic!("acquired poisoned lock object");
},
}
}
pub fn release(&mut self, access: Access) {
self.state = match (self.state, access) {
(LockState::Poison, _) => self.state,
(LockState::Open, access) => {
panic!("tried to release({:?}) a lock that is already open", access)
}
(LockState::Write(_), Access::Write) => LockState::Open,
(LockState::Read(0), Access::Read) => LockState::Open,
(LockState::Read(n), Access::Read) => LockState::Read(n - 1),
(state, access) => {
panic!("Mismatched release({:?}) to {:?}", access, state)
},
}
}
#[allow(clippy::borrowed_box)]
pub unsafe fn contents(&mut self) -> *mut dyn Any {
let obj: *mut Box<dyn Any> = self.obj.get();
let obj: &mut Box<dyn Any> = &mut *obj;
obj.deref_mut()
}
pub unsafe fn read(&mut self) -> GuardRef {
self.acquire(Access::Read);
GuardRef { lock: self }
}
pub unsafe fn write(&mut self) -> GuardMut {
self.acquire(Access::Write);
GuardMut { lock: self }
}
pub fn into_inner(mut self) -> Box<dyn Any> {
unsafe {
self.acquire(Access::Write);
let stuff = self.contents();
::std::mem::forget(self);
Box::from_raw(stuff)
}
}
}
impl Drop for Locked {
fn drop(&mut self) {
if let LockState::Write(_) = self.state {
if std::thread::panicking() {
self.state = LockState::Poison;
} else {
panic!("Locked object dropped without release(): {:?}", self);
}
}
}
}
pub struct GuardRef {
lock: *const Locked,
}
pub struct GuardMut {
lock: *mut Locked,
}
impl Deref for GuardRef {
type Target = dyn Any;
#[allow(clippy::borrowed_box)]
fn deref(&self) -> &dyn Any {
unsafe {
let lock: &Locked = &*self.lock;
let obj: *mut Box<dyn Any> = lock.obj.get();
let obj: &Box<dyn Any> = &*obj;
obj.deref()
}
}
}
impl Deref for GuardMut {
type Target = dyn Any;
#[allow(clippy::borrowed_box)]
fn deref(&self) -> &dyn Any {
unsafe {
let lock: &Locked = &*self.lock;
let obj: *mut Box<dyn Any> = lock.obj.get();
let obj: &Box<dyn Any> = &*obj;
obj.deref()
}
}
}
impl DerefMut for GuardMut {
fn deref_mut(&mut self) -> &mut dyn Any {
unsafe {
let lock: &mut Locked = &mut *self.lock;
&mut *lock.contents()
}
}
}
impl Drop for GuardRef {
fn drop(&mut self) {
unsafe {
let lock: &mut Locked = &mut *(self.lock as *mut Locked);
lock.release(Access::Read);
}
}
}
impl Drop for GuardMut {
fn drop(&mut self) {
unsafe {
let lock: &mut Locked = &mut *self.lock;
lock.release(Access::Write);
}
}
}