Created
November 3, 2020 17:57
-
-
Save Visic/3c951421b1e15c809f207dfa34bff30e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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