Skip to content

Instantly share code, notes, and snippets.

@Visic
Created November 3, 2020 17:57
Show Gist options
  • Save Visic/3c951421b1e15c809f207dfa34bff30e to your computer and use it in GitHub Desktop.
Save Visic/3c951421b1e15c809f207dfa34bff30e to your computer and use it in GitHub Desktop.
//This is a modified implementation of the RwLock from the "Spin" crate
// I have adjusted it to make calling "read" on the RwLock greedy
// by deferring the spin lock until you actually try to deref the readlock
// but adding a reader to the lock counter right away
//
// This approach makes it so the read lock can be taken at any point, rather than
// waiting for the writer to finish up, while still protecting that data access itself
use std::cell::UnsafeCell;
use std::ops::Deref;
use std::sync::atomic::{spin_loop_hint as cpu_relax, AtomicUsize, Ordering};
use std::fmt::Debug;
const WRITER: usize = 1;
const READER: usize = 2;
pub trait SupportedType = Send + ?Sized + 'static;
unsafe impl<T: SupportedType> Send for RwLockReadGuard<'static, T> {}
unsafe impl<T: SupportedType + Sync> Sync for RwLockReadGuard<'static, T> {}
unsafe impl<T: SupportedType> Send for RwLock<T> {}
pub struct RwLock<T: SupportedType> {
lock: AtomicUsize,
data: UnsafeCell<T>,
}
pub struct RwLockReadGuard<'a, T: 'a + SupportedType> {
lock: &'a AtomicUsize,
data: *const T,
}
impl<'a, T: SupportedType + Debug> Debug for RwLockReadGuard<'a, T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Debug::fmt(&**self, f)
}
}
impl<T: SupportedType> RwLock<T> {
#[inline]
pub fn new(user_data: T) -> RwLock<T> {
RwLock {
lock: AtomicUsize::new(0),
data: UnsafeCell::new(user_data),
}
}
#[inline]
pub fn read(&self) -> RwLockReadGuard<T> {
self.lock.fetch_add(READER, Ordering::Acquire);
RwLockReadGuard {
lock: &self.lock,
data: self.data.get(),
}
}
#[inline]
pub fn try_update(&mut self, value: T) -> Option<T> {
if self.lock.compare_exchange(0, WRITER, Ordering::Acquire, Ordering::Relaxed).is_ok() {
unsafe{ *self.data.get() = value };
self.lock.fetch_and(!WRITER, Ordering::Release);
None
} else {
Some(value)
}
}
}
impl<'rwlock, T: SupportedType> Deref for RwLockReadGuard<'rwlock, T> {
type Target = T;
fn deref(&self) -> &T {
while self.lock.load(Ordering::Acquire) & WRITER > 0 { cpu_relax(); }
unsafe { &*self.data }
}
}
impl<'rwlock, T: SupportedType> Drop for RwLockReadGuard<'rwlock, T> {
fn drop(&mut self) {
self.lock.fetch_sub(READER, Ordering::Release);
}
}
impl<T: SupportedType> Drop for RwLock<T> {
fn drop(&mut self) {
//lock so no readers will be able to access the data when we are gone (Note:: This will cause new reads to deadlock)
while self.lock.compare_exchange(0, WRITER, Ordering::Acquire, Ordering::Relaxed).is_err() { cpu_relax(); }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment