use std::cell::UnsafeCell;
use std::sync::atomic::AtomicU8;
use std::sync::atomic::Ordering::{Acquire, Relaxed, Release};
const UNLOCKED: u8 = 0;
const LOCKED_WRITE: u8 = u8::MAX;
pub struct Spinlock<T> {
data: UnsafeCell<T>,
locked: std::sync::atomic::AtomicU8,
}
unsafe impl<T: Send> Send for Spinlock<T> {}
unsafe impl<T: Send> Sync for Spinlock<T> {}
impl<T> Spinlock<T> {
pub fn new(data: T) -> Self {
Spinlock {
data: UnsafeCell::new(data),
locked: AtomicU8::new(UNLOCKED),
}
}
fn spin_lock_write(&self) {
while self
.locked
.compare_exchange_weak(UNLOCKED, LOCKED_WRITE, Acquire, Relaxed)
.is_err()
{
std::hint::spin_loop();
}
}
fn spin_unlock_write(&self) {
self.locked.store(UNLOCKED, Release);
}
fn spin_lock_read(&self) {
while self
.locked
.fetch_update(Acquire, Relaxed, |v| {
if v < (LOCKED_WRITE - 1) {
Some(v + 1)
} else {
None
}
})
.is_err()
{
std::hint::spin_loop();
}
}
fn spin_unlock_read(&self) {
self.locked.fetch_sub(1, Release);
}
pub fn with_mut<F, R>(&self, f: F) -> R
where
F: FnOnce(&mut T) -> R,
{
self.spin_lock_write();
let result = unsafe { f(&mut *self.data.get()) };
self.spin_unlock_write();
result
}
pub fn with<F, R>(&self, f: F) -> R
where
F: FnOnce(&T) -> R,
{
self.spin_lock_read();
let result = unsafe { f(&*self.data.get()) };
self.spin_unlock_read();
result
}
}