use crate::error;
use crate::sync::{AtomicUsize, Ordering, RwLock, RwLockReadGuard, RwLockWriteGuard, UnsafeCell};
#[repr(C, align(64))]
pub struct HybridLatch<T> {
version: AtomicUsize,
lock: RwLock<()>,
data: UnsafeCell<T>,
}
unsafe impl<T: Send> Send for HybridLatch<T> {}
unsafe impl<T: Send + Sync> Sync for HybridLatch<T> {}
impl<T> HybridLatch<T> {
#[inline]
pub fn new(data: T) -> HybridLatch<T> {
HybridLatch {
version: AtomicUsize::new(0), data: UnsafeCell::new(data),
lock: RwLock::new(()),
}
}
#[inline]
pub fn exclusive(&self) -> ExclusiveGuard<'_, T> {
let guard = self.lock.write();
let version = self.version.load(Ordering::Relaxed) + 1;
self.version.store(version, Ordering::Release);
ExclusiveGuard {
latch: self,
guard,
data: self.data.get(),
version,
}
}
#[inline]
pub fn shared(&self) -> SharedGuard<'_, T> {
let guard = self.lock.read();
let version = self.version.load(Ordering::Relaxed);
SharedGuard {
latch: self,
guard,
data: self.data.get(),
version,
}
}
#[inline(never)]
pub fn optimistic_or_spin(&self) -> OptimisticGuard<'_, T> {
let mut version = self.version.load(Ordering::Acquire);
if (version & 1) == 1 {
let mut spinwait = parking_lot_core::SpinWait::new();
loop {
version = self.version.load(Ordering::Acquire);
if (version & 1) == 1 {
spinwait.spin();
continue;
} else {
break;
}
}
}
OptimisticGuard {
latch: self,
data: self.data.get(),
version,
}
}
#[inline]
#[allow(dead_code)]
pub fn optimistic_or_unwind(&self) -> OptimisticOrShared<'_, T> {
let version = self.version.load(Ordering::Acquire);
if (version & 1) == 1 {
let guard = self.lock.read();
let version = self.version.load(Ordering::Relaxed);
OptimisticOrShared::Shared(SharedGuard {
latch: self,
guard,
data: self.data.get(),
version,
})
} else {
OptimisticOrShared::Optimistic(OptimisticGuard {
latch: self,
data: self.data.get(),
version,
})
}
}
#[inline]
#[allow(dead_code)]
pub fn optimistic_or_exclusive(&self) -> OptimisticOrExclusive<'_, T> {
let version = self.version.load(Ordering::Acquire);
if (version & 1) == 1 {
let guard = self.lock.write();
let version = self.version.load(Ordering::Relaxed) + 1;
self.version.store(version, Ordering::Release);
OptimisticOrExclusive::Exclusive(ExclusiveGuard {
latch: self,
guard,
data: self.data.get(),
version,
})
} else {
OptimisticOrExclusive::Optimistic(OptimisticGuard {
latch: self,
data: self.data.get(),
version,
})
}
}
}
impl<T> std::convert::AsMut<T> for HybridLatch<T> {
#[inline]
fn as_mut(&mut self) -> &mut T {
unsafe { &mut *self.data.get() }
}
}
pub trait HybridGuard<T> {
fn inner(&self) -> &T;
fn recheck(&self) -> error::Result<()>;
fn latch(&self) -> &HybridLatch<T>;
}
pub struct OptimisticGuard<'a, T> {
latch: &'a HybridLatch<T>,
data: *const T,
version: usize,
}
unsafe impl<'a, T: Sync> Sync for OptimisticGuard<'a, T> {}
impl<'a, T> OptimisticGuard<'a, T> {
#[inline]
pub fn recheck(&self) -> error::Result<()> {
if self.version != self.latch.version.load(Ordering::Acquire) {
return Err(error::Error::Unwind);
}
Ok(())
}
#[inline]
pub fn to_shared(self) -> error::Result<SharedGuard<'a, T>> {
if let Some(guard) = self.latch.lock.try_read() {
if self.version != self.latch.version.load(Ordering::Relaxed) {
return Err(error::Error::Unwind);
}
Ok(SharedGuard {
latch: self.latch,
guard,
data: self.data,
version: self.version,
})
} else {
Err(error::Error::Unwind)
}
}
#[inline]
pub fn to_exclusive(self) -> error::Result<ExclusiveGuard<'a, T>> {
if let Some(guard) = self.latch.lock.try_write() {
if self.version != self.latch.version.load(Ordering::Relaxed) {
return Err(error::Error::Unwind);
}
let version = self.version + 1;
self.latch.version.store(version, Ordering::Release);
Ok(ExclusiveGuard {
latch: self.latch,
guard,
data: self.data as *mut T,
version,
})
} else {
Err(error::Error::Unwind)
}
}
pub fn latch(&self) -> &'a HybridLatch<T> {
self.latch
}
}
impl<'a, T> std::ops::Deref for OptimisticGuard<'a, T> {
type Target = T;
fn deref(&self) -> &T {
unsafe { &*self.data }
}
}
impl<'a, T> HybridGuard<T> for OptimisticGuard<'a, T> {
fn inner(&self) -> &T {
self
}
fn recheck(&self) -> error::Result<()> {
self.recheck()
}
fn latch(&self) -> &HybridLatch<T> {
self.latch()
}
}
pub struct ExclusiveGuard<'a, T> {
latch: &'a HybridLatch<T>,
#[allow(dead_code)]
guard: RwLockWriteGuard<'a, ()>,
data: *mut T,
version: usize,
}
unsafe impl<'a, T: Sync> Sync for ExclusiveGuard<'a, T> {}
impl<'a, T> ExclusiveGuard<'a, T> {
#[inline]
pub fn recheck(&self) {
assert!(
self.version == self.latch.version.load(Ordering::Relaxed),
"exclusive guard version mismatch - lock invariant violated"
);
}
#[inline]
pub fn unlock(self) -> OptimisticGuard<'a, T> {
let new_version = self.version + 1;
let latch = self.latch;
let data = self.data;
drop(self);
OptimisticGuard {
latch,
data,
version: new_version,
}
}
pub fn latch(&self) -> &'a HybridLatch<T> {
self.latch
}
}
impl<'a, T> Drop for ExclusiveGuard<'a, T> {
#[inline]
fn drop(&mut self) {
let new_version = self.version + 1;
self.latch.version.store(new_version, Ordering::Release);
}
}
impl<'a, T> std::ops::Deref for ExclusiveGuard<'a, T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
unsafe { &*self.data }
}
}
impl<'a, T> std::ops::DerefMut for ExclusiveGuard<'a, T> {
#[inline]
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.data }
}
}
impl<'a, T> std::convert::AsMut<T> for ExclusiveGuard<'a, T> {
#[inline]
fn as_mut(&mut self) -> &mut T {
unsafe { &mut *self.data }
}
}
impl<'a, T> HybridGuard<T> for ExclusiveGuard<'a, T> {
fn inner(&self) -> &T {
self
}
fn recheck(&self) -> error::Result<()> {
self.recheck();
Ok(())
}
fn latch(&self) -> &HybridLatch<T> {
self.latch()
}
}
pub struct SharedGuard<'a, T> {
latch: &'a HybridLatch<T>,
#[allow(dead_code)]
guard: RwLockReadGuard<'a, ()>,
data: *const T,
version: usize,
}
unsafe impl<'a, T: Sync> Sync for SharedGuard<'a, T> {}
impl<'a, T> SharedGuard<'a, T> {
#[inline]
pub fn recheck(&self) {
assert!(
self.version == self.latch.version.load(Ordering::Relaxed),
"shared guard version mismatch - lock invariant violated"
);
}
#[inline]
pub fn unlock(self) -> OptimisticGuard<'a, T> {
OptimisticGuard {
latch: self.latch,
data: self.data,
version: self.version,
}
}
#[inline]
#[allow(dead_code)]
pub fn as_optimistic(&self) -> OptimisticGuard<'_, T> {
OptimisticGuard {
latch: self.latch,
data: self.data,
version: self.version,
}
}
pub fn latch(&self) -> &'a HybridLatch<T> {
self.latch
}
}
impl<'a, T> std::ops::Deref for SharedGuard<'a, T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
unsafe { &*self.data }
}
}
impl<'a, T> HybridGuard<T> for SharedGuard<'a, T> {
fn inner(&self) -> &T {
self
}
fn recheck(&self) -> error::Result<()> {
self.recheck();
Ok(())
}
fn latch(&self) -> &HybridLatch<T> {
self.latch()
}
}
#[allow(dead_code)]
pub enum OptimisticOrShared<'a, T> {
Optimistic(OptimisticGuard<'a, T>),
Shared(SharedGuard<'a, T>),
}
#[allow(dead_code)]
impl<'a, T> OptimisticOrShared<'a, T> {
#[inline]
pub fn recheck(&self) -> error::Result<()> {
match self {
OptimisticOrShared::Optimistic(g) => g.recheck(),
OptimisticOrShared::Shared(g) => {
g.recheck();
Ok(())
}
}
}
}
#[allow(dead_code)]
pub enum OptimisticOrExclusive<'a, T> {
Optimistic(OptimisticGuard<'a, T>),
Exclusive(ExclusiveGuard<'a, T>),
}
#[allow(dead_code)]
impl<'a, T> OptimisticOrExclusive<'a, T> {
#[inline]
pub fn recheck(&self) -> error::Result<()> {
match self {
OptimisticOrExclusive::Optimistic(g) => g.recheck(),
OptimisticOrExclusive::Exclusive(g) => {
g.recheck();
Ok(())
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn version_starts_at_zero() {
let latch = HybridLatch::new(42);
assert_eq!(latch.version.load(Ordering::Relaxed), 0);
}
#[test]
fn exclusive_lock_makes_version_odd() {
let latch = HybridLatch::new(42);
let guard = latch.exclusive();
assert_eq!(latch.version.load(Ordering::Relaxed) & 1, 1);
drop(guard);
}
#[test]
fn exclusive_unlock_makes_version_even() {
let latch = HybridLatch::new(42);
{
let _guard = latch.exclusive();
}
let version = latch.version.load(Ordering::Relaxed);
assert_eq!(version & 1, 0);
assert_eq!(version, 2); }
#[test]
fn multiple_exclusive_locks_increment_version() {
let latch = HybridLatch::new(42);
for i in 0..5 {
{
let _guard = latch.exclusive();
assert_eq!(latch.version.load(Ordering::Relaxed), 2 * i + 1);
}
assert_eq!(latch.version.load(Ordering::Relaxed), 2 * (i + 1));
}
}
#[test]
fn optimistic_recheck_succeeds_without_write() {
let latch = HybridLatch::new(42);
let guard = latch.optimistic_or_spin();
let value = *guard;
assert_eq!(value, 42);
assert!(guard.recheck().is_ok());
}
#[test]
fn optimistic_recheck_fails_after_write() {
let latch = HybridLatch::new(42);
let opt = latch.optimistic_or_spin();
{
let mut exc = latch.exclusive();
*exc = 100;
}
assert!(opt.recheck().is_err());
}
#[test]
fn optimistic_recheck_fails_during_write() {
let latch = HybridLatch::new(42);
let exc = latch.exclusive();
assert_eq!(latch.version.load(Ordering::Relaxed) & 1, 1);
drop(exc);
}
#[test]
fn optimistic_captures_correct_version() {
let latch = HybridLatch::new(42);
for _ in 0..3 {
let _guard = latch.exclusive();
}
let opt = latch.optimistic_or_spin();
assert_eq!(opt.version, 6);
assert!(opt.recheck().is_ok());
}
#[test]
fn to_shared_succeeds_without_contention() {
let latch = HybridLatch::new(42);
let opt = latch.optimistic_or_spin();
let shared = opt.to_shared();
assert!(shared.is_ok());
let shared = shared.unwrap();
assert_eq!(*shared, 42);
}
#[test]
fn to_shared_fails_after_write() {
let latch = HybridLatch::new(42);
let opt = latch.optimistic_or_spin();
{
let mut exc = latch.exclusive();
*exc = 100;
}
assert!(opt.to_shared().is_err());
}
#[test]
fn to_exclusive_succeeds_without_contention() {
let latch = HybridLatch::new(42);
let opt = latch.optimistic_or_spin();
let exc = opt.to_exclusive();
assert!(exc.is_ok());
let mut exc = exc.unwrap();
*exc = 100;
drop(exc);
let opt2 = latch.optimistic_or_spin();
assert_eq!(*opt2, 100);
}
#[test]
fn to_exclusive_fails_after_write() {
let latch = HybridLatch::new(42);
let opt = latch.optimistic_or_spin();
{
let mut exc = latch.exclusive();
*exc = 100;
}
assert!(opt.to_exclusive().is_err());
}
#[test]
fn to_exclusive_increments_version() {
let latch = HybridLatch::new(42);
let opt = latch.optimistic_or_spin();
let initial_version = opt.version;
let exc = opt.to_exclusive().unwrap();
assert_eq!(latch.version.load(Ordering::Relaxed), initial_version + 1);
drop(exc);
assert_eq!(latch.version.load(Ordering::Relaxed), initial_version + 2);
}
#[test]
fn exclusive_unlock_returns_optimistic_with_new_version() {
let latch = HybridLatch::new(42);
let exc = latch.exclusive();
let version_during_lock = exc.version;
let opt = exc.unlock();
assert_eq!(opt.version, version_during_lock + 1);
assert!(opt.recheck().is_ok());
}
#[test]
fn shared_unlock_returns_optimistic_with_same_version() {
let latch = HybridLatch::new(42);
let shared = latch.shared();
let version = shared.version;
let opt = shared.unlock();
assert_eq!(opt.version, version);
assert!(opt.recheck().is_ok());
}
#[test]
fn shared_lock_provides_read_access() {
let latch = HybridLatch::new(42);
let shared = latch.shared();
assert_eq!(*shared, 42);
}
#[test]
fn multiple_shared_locks_allowed() {
let latch = HybridLatch::new(42);
let shared1 = latch.shared();
let shared2 = latch.shared();
assert_eq!(*shared1, 42);
assert_eq!(*shared2, 42);
}
#[test]
fn shared_recheck_always_succeeds() {
let latch = HybridLatch::new(42);
let shared = latch.shared();
shared.recheck();
}
#[test]
fn exclusive_lock_provides_write_access() {
let latch = HybridLatch::new(42);
{
let mut exc = latch.exclusive();
*exc = 100;
}
let opt = latch.optimistic_or_spin();
assert_eq!(*opt, 100);
}
#[test]
fn exclusive_deref_mut_works() {
let latch = HybridLatch::new(vec![1, 2, 3]);
{
let mut exc = latch.exclusive();
exc.push(4);
}
let opt = latch.optimistic_or_spin();
assert_eq!(*opt, vec![1, 2, 3, 4]);
}
#[test]
fn hybrid_guard_trait_works_for_optimistic() {
let latch = HybridLatch::new(42);
let guard = latch.optimistic_or_spin();
fn use_guard<T>(guard: &impl HybridGuard<T>) -> &T {
guard.inner()
}
assert_eq!(*use_guard(&guard), 42);
assert!(guard.recheck().is_ok());
}
#[test]
fn hybrid_guard_trait_works_for_shared() {
let latch = HybridLatch::new(42);
let guard = latch.shared();
fn use_guard<T>(guard: &impl HybridGuard<T>) -> &T {
guard.inner()
}
assert_eq!(*use_guard(&guard), 42);
guard.recheck();
}
#[test]
fn hybrid_guard_trait_works_for_exclusive() {
let latch = HybridLatch::new(42);
let guard = latch.exclusive();
fn use_guard<T>(guard: &impl HybridGuard<T>) -> &T {
guard.inner()
}
assert_eq!(*use_guard(&guard), 42);
guard.recheck();
}
#[test]
fn optimistic_or_unwind_returns_optimistic_when_unlocked() {
let latch = HybridLatch::new(42);
let guard = latch.optimistic_or_unwind();
match guard {
OptimisticOrShared::Optimistic(_) => {}
OptimisticOrShared::Shared(_) => panic!("expected optimistic"),
}
}
#[test]
fn optimistic_or_exclusive_returns_optimistic_when_unlocked() {
let latch = HybridLatch::new(42);
let guard = latch.optimistic_or_exclusive();
match guard {
OptimisticOrExclusive::Optimistic(_) => {}
OptimisticOrExclusive::Exclusive(_) => panic!("expected optimistic"),
}
}
#[test]
fn latch_with_zero_sized_type() {
let latch = HybridLatch::new(());
let opt = latch.optimistic_or_spin();
assert!(opt.recheck().is_ok());
}
#[test]
fn latch_with_complex_type() {
#[derive(Debug, PartialEq)]
struct Complex {
a: i32,
b: String,
c: Vec<u8>,
}
let latch = HybridLatch::new(Complex {
a: 42,
b: "hello".to_string(),
c: vec![1, 2, 3],
});
let opt = latch.optimistic_or_spin();
assert_eq!(opt.a, 42);
assert_eq!(opt.b, "hello");
assert_eq!(opt.c, vec![1, 2, 3]);
assert!(opt.recheck().is_ok());
}
#[test]
fn as_mut_provides_direct_access() {
let mut latch = HybridLatch::new(42);
*latch.as_mut() = 100;
let opt = latch.optimistic_or_spin();
assert_eq!(*opt, 100);
}
}