#[cfg(feature = "debug")]
use log::debug;
use std::{
convert::TryFrom,
fmt,
ops::{Deref, DerefMut},
result,
sync::atomic::{AtomicU32, Ordering::SeqCst},
};
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_and_swap(old, old + 1, SeqCst) == old {
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!("if latch is flipped-off, lock can't be flipped-on !");
}
let new = old | Self::LATCH_FLAG;
if self.latchlock.compare_and_swap(old, new, SeqCst) == old {
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_and_swap(old, new, SeqCst) == old {
if cfg!(feature = "debug") {
self.write_locks.fetch_add(1, SeqCst);
}
let door =
unsafe { ((self as *const Self) as *mut Self).as_mut().unwrap() };
break WriteGuard { door };
}
panic!("latch is acquired, ZERO readers, but unable to lock !")
}
if cfg!(feature = "debug") {
self.conflicts.fetch_add(1, SeqCst);
}
}
}
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!("can't have active readers, when lock is held");
}
if self.door.latchlock.compare_and_swap(old, 0, SeqCst) != old {
panic!("cant' have readers/writers to modify when locked")
}
}
}
#[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;