Skip to content

Instantly share code, notes, and snippets.

@Visic
Created November 14, 2019 17:11
Show Gist options
  • Save Visic/5c672c750e1a6e16590d9cd7dd11d1c8 to your computer and use it in GitHub Desktop.
Save Visic/5c672c750e1a6e16590d9cd7dd11d1c8 to your computer and use it in GitHub Desktop.
Example implementation of a greedy read lock
use core::cell::UnsafeCell;
use core::marker::PhantomData;
use core::ops::{Deref, DerefMut};
use core::ptr::NonNull;
use core::sync::atomic::{spin_loop_hint as cpu_relax, AtomicUsize, Ordering};
pub struct RwLock<T: ?Sized> {
lock: AtomicUsize,
data: UnsafeCell<T>,
}
const WRITER: usize = 1;
const READER: usize = 2;
#[derive(Debug)]
pub struct RwLockReadGuard<'a, T: 'a + ?Sized> {
lock: &'a AtomicUsize,
data: NonNull<T>,
}
#[derive(Debug)]
pub struct RwLockWriteGuard<'a, T: 'a + ?Sized> {
lock: &'a AtomicUsize,
data: NonNull<T>,
_invariant: PhantomData<&'a mut T>,
}
unsafe impl<T: ?Sized + Send> Send for RwLock<T> {}
unsafe impl<T: ?Sized + Send + Sync> Sync for RwLock<T> {}
impl<T> RwLock<T> {
#[inline]
pub const fn new(user_data: T) -> RwLock<T> {
RwLock {
lock: AtomicUsize::new(0),
data: UnsafeCell::new(user_data),
}
}
}
impl<T: ?Sized> RwLock<T> {
#[inline]
pub fn read(&self) -> RwLockReadGuard<T> {
self.lock.fetch_add(READER, Ordering::Acquire);
RwLockReadGuard {
lock: &self.lock,
data: unsafe { NonNull::new_unchecked(self.data.get()) },
}
}
#[inline]
pub fn try_write(&self) -> Option<RwLockWriteGuard<T>> {
if self.lock.compare_exchange(0, WRITER, Ordering::Acquire, Ordering::Relaxed).is_ok() {
Some(RwLockWriteGuard {
lock: &self.lock,
data: unsafe { NonNull::new_unchecked(self.data.get()) },
_invariant: PhantomData,
})
} else {
None
}
}
}
impl<'rwlock, T: ?Sized> 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.as_ref() }
}
}
impl<'rwlock, T: ?Sized> Deref for RwLockWriteGuard<'rwlock, T> {
type Target = T;
fn deref(&self) -> &T {
unsafe { self.data.as_ref() }
}
}
impl<'rwlock, T: ?Sized> DerefMut for RwLockWriteGuard<'rwlock, T> {
fn deref_mut(&mut self) -> &mut T {
unsafe { self.data.as_mut() }
}
}
impl<'rwlock, T: ?Sized> Drop for RwLockReadGuard<'rwlock, T> {
fn drop(&mut self) {
self.lock.fetch_sub(READER, Ordering::Release);
}
}
impl<'rwlock, T: ?Sized> Drop for RwLockWriteGuard<'rwlock, T> {
fn drop(&mut self) {
self.lock.fetch_and(!WRITER, Ordering::Release);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment