use std::cell::Cell;
use std::num::NonZeroUsize;
pub trait AccessLock {
fn write_locks(&self) -> usize;
fn read_locks(&self) -> usize;
fn acquire_write(&self);
fn release_write(&self);
fn acquire_read(&self);
fn release_read(&self);
fn acquire_write_guarded(&self) -> WriteGuard
where Self: Sized {
self.acquire_write();
WriteGuard(self)
}
fn acquire_read_guarded(&self) -> ReadGuard
where Self: Sized {
self.acquire_read();
ReadGuard(self)
}
}
pub struct WriteGuard<'a>(&'a dyn AccessLock);
impl<'a> Drop for WriteGuard<'a> {
fn drop(&mut self) {
self.0.release_write();
}
}
pub struct ReadGuard<'a>(&'a dyn AccessLock);
impl<'a> Drop for ReadGuard<'a> {
fn drop(&mut self) {
self.0.release_read();
}
}
#[derive(Debug, Eq, PartialEq)]
pub struct UnitAccessLock {
lock: Cell<AccessLockState>
}
impl UnitAccessLock {
pub fn new() -> Self {
Self {
lock: Cell::new(AccessLockState::Idle)
}
}
}
impl Default for UnitAccessLock {
fn default() -> Self {
Self::new()
}
}
impl AccessLock for UnitAccessLock {
fn write_locks(&self) -> usize {
match self.lock.get() {
AccessLockState::Write => 1,
_ => 0
}
}
fn read_locks(&self) -> usize {
match self.lock.get() {
AccessLockState::Read { clients } => clients.get(),
_ => 0
}
}
fn acquire_write(&self) {
let state = self.lock.get();
let next = match state {
AccessLockState::Idle => AccessLockState::Write,
AccessLockState::Write => AccessLockState::Write,
AccessLockState::Read { clients } =>
panic!("tried to acquire a write lease to a resource that is \
currently being read from by {} clients", clients),
};
let old = self.lock.replace(next);
if old != state {
panic!("inconsistency between read and write of cell!")
}
}
fn release_write(&self) {
let state = self.lock.get();
let next = match state {
AccessLockState::Write => AccessLockState::Idle,
_ => panic!("tried to relinquish a write lease to a resource that \
is currently not being written to")
};
let old = self.lock.replace(next);
if old != state {
panic!("inconsistency between read and write of cell!")
}
}
fn acquire_read(&self) {
let state = self.lock.get();
let next = match state {
AccessLockState::Idle => AccessLockState::Read { clients: NonZeroUsize::new(1).unwrap() },
AccessLockState::Read { clients } =>
AccessLockState::Read {
clients: NonZeroUsize::new(clients.get() + 1).unwrap()
},
AccessLockState::Write =>
panic!("tried to acquire a read lease to a resource that is \
currently being written to"),
};
let old = self.lock.replace(next);
if old != state {
panic!("inconsistency between read and write of cell!")
}
}
fn release_read(&self) {
let state = self.lock.get();
let next = match state {
AccessLockState::Read { clients }
if clients.get() == 1 => AccessLockState::Idle,
AccessLockState::Read { clients } =>
AccessLockState::Read {
clients: NonZeroUsize::new(clients.get() - 1).unwrap()
},
_ =>
panic!("tried to relinquish a read lease to a resource that is \
currently not being read from"),
};
let old = self.lock.replace(next);
if old != state {
panic!("inconsistency between read and write of cell!")
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
enum AccessLockState {
Idle,
Write,
Read {
clients: NonZeroUsize,
}
}