#[cfg(feature = "debug")]
use log::debug;
#[cfg(test)]
use std::convert::TryFrom;
use std::{
fmt,
ops::{Deref, DerefMut},
result,
sync::atomic::{AtomicU32, Ordering::SeqCst},
};
#[cfg(test)]
use crate::{Error, Result};
pub struct Spinlock<T> {
latchlock: AtomicU32,
read_locks: AtomicU32,
write_locks: AtomicU32,
conflicts: AtomicU32,
value: T,
}
#[cfg(feature = "debug")]
impl<T> Drop for Spinlock<T> {
fn drop(&mut self) {
debug!("{}", self.to_stats().unwrap());
}
}
impl<T> Spinlock<T> {
const LATCH_FLAG: u32 = 0x40000000;
const LOCK_FLAG: u32 = 0x80000000;
const LATCH_LOCK_FLAG: u32 = 0xC0000000;
const READERS_FLAG: u32 = 0x3FFFFFFF;
pub fn new(value: T) -> Spinlock<T> {
Spinlock {
latchlock: AtomicU32::new(0),
read_locks: AtomicU32::new(0),
write_locks: AtomicU32::new(0),
conflicts: AtomicU32::new(0),
value,
}
}
pub fn read(&self) -> ReadGuard<T> {
loop {
let old = self.latchlock.load(SeqCst);
if (old & Self::LATCH_LOCK_FLAG) == 0 {
if self
.latchlock
.compare_exchange(old, old + 1, SeqCst, SeqCst)
.is_ok()
{
if cfg!(feature = "debug") {
self.read_locks.fetch_add(1, SeqCst);
}
break ReadGuard { door: self };
}
}
if cfg!(feature = "debug") {
self.conflicts.fetch_add(1, SeqCst);
}
}
}
pub fn write(&self) -> WriteGuard<T> {
loop {
let old = self.latchlock.load(SeqCst);
if (old & Self::LATCH_FLAG) == 0 {
if (old & Self::LOCK_FLAG) != 0 {
panic!(concat!(
"if latch is flipped-off, lock can't be flipped-on! ",
"call the programmer"
));
}
let new = old | Self::LATCH_FLAG;
if self
.latchlock
.compare_exchange(old, new, SeqCst, SeqCst)
.is_ok()
{
break;
}
}
if cfg!(feature = "debug") {
self.conflicts.fetch_add(1, SeqCst);
}
}
loop {
let old = self.latchlock.load(SeqCst);
if (old & Self::READERS_FLAG) == 0 {
let new = old | Self::LOCK_FLAG;
if self
.latchlock
.compare_exchange(old, new, SeqCst, SeqCst)
.is_ok()
{
if cfg!(feature = "debug") {
self.write_locks.fetch_add(1, SeqCst);
}
let door = unsafe {
let door = self as *const Self as *mut Self;
door.as_mut().unwrap()
};
break WriteGuard { door };
}
panic!(concat!(
"latch is acquired, ZERO readers, but unable to lock! ",
"call the programmer"
));
}
if cfg!(feature = "debug") {
self.conflicts.fetch_add(1, SeqCst);
}
}
}
#[cfg(test)]
pub fn to_stats(&self) -> Result<Stats> {
let rl = err_at!(FailConvert, usize::try_from(self.read_locks.load(SeqCst)))?;
let wl = err_at!(FailConvert, usize::try_from(self.write_locks.load(SeqCst)))?;
let cn = err_at!(FailConvert, usize::try_from(self.conflicts.load(SeqCst)))?;
Ok(Stats {
latchlock: self.latchlock.load(SeqCst),
read_locks: rl,
write_locks: wl,
conflicts: cn,
})
}
}
pub struct ReadGuard<'a, T> {
door: &'a Spinlock<T>,
}
impl<'a, T> Deref for ReadGuard<'a, T> {
type Target = T;
fn deref(&self) -> &T {
&self.door.value
}
}
impl<'a, T> Drop for ReadGuard<'a, T> {
fn drop(&mut self) {
self.door.latchlock.fetch_sub(1, SeqCst);
}
}
pub struct WriteGuard<'a, T> {
door: &'a mut Spinlock<T>,
}
impl<'a, T> Deref for WriteGuard<'a, T> {
type Target = T;
fn deref(&self) -> &T {
&self.door.value
}
}
impl<'a, T> DerefMut for WriteGuard<'a, T> {
fn deref_mut(&mut self) -> &mut T {
&mut self.door.value
}
}
impl<'a, T> Drop for WriteGuard<'a, T> {
fn drop(&mut self) {
let old = self.door.latchlock.load(SeqCst);
if (old & Spinlock::<T>::READERS_FLAG) > 0 {
panic!(concat!(
"can't have active readers, when lock is held! ",
"call the programmer"
));
}
if self
.door
.latchlock
.compare_exchange(old, 0, SeqCst, SeqCst)
.is_err()
{
panic!(concat!(
"cant' have readers/writers to modify when locked! ",
"call the programmer"
))
}
}
}
#[derive(Default)]
pub struct Stats {
pub latchlock: u32,
pub read_locks: usize,
pub write_locks: usize,
pub conflicts: usize,
}
impl fmt::Display for Stats {
fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
write!(
f,
concat!(
"{{ latchlock = {:X}, read_locks = {}, ",
"write_locks = {}, conflicts = {} }}",
),
self.latchlock, self.read_locks, self.write_locks, self.conflicts,
)
}
}
#[cfg(test)]
#[path = "spinlock_test.rs"]
mod spinlock_test;