#![feature(negative_impls)]
#![feature(unboxed_closures)]
#![feature(stmt_expr_attributes)]
use std::{cell::UnsafeCell, fmt::Display, ops::{Deref, DerefMut}, sync::atomic::{AtomicBool, AtomicU64}};
use std::{fmt::Debug, sync::atomic::Ordering::*};
#[cfg(test)]
mod test;
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum OptimisticLockCouplingErrorType {
Poisoned,
Outdated,
Blocked,
VersionUpdated,
}
pub type OptimisticLockCouplingResult<T> = Result<T, OptimisticLockCouplingErrorType>;
pub struct OptimisticLockCoupling<T: ?Sized> {
version_lock_outdate: AtomicU64,
poisoned: AtomicBool,
data: UnsafeCell<T>,
}
unsafe impl<T: ?Sized + Send> Send for OptimisticLockCoupling<T> {}
unsafe impl<T: ?Sized + Send + Sync> Sync for OptimisticLockCoupling<T> {}
impl<T> OptimisticLockCoupling<T> {
#[inline(always)]
pub fn new(t: T) -> Self {
Self {
version_lock_outdate: AtomicU64::new(0),
poisoned: AtomicBool::new(false),
data: UnsafeCell::new(t),
}
}
#[inline(always)]
pub fn read_txn<F, R>(&self, mut logic: F) -> OptimisticLockCouplingResult<R>
where
F: FnMut(&OptimisticLockCouplingReadGuard<T>) -> OptimisticLockCouplingResult<R>,
{
'txn: loop {
match self.read() {
Ok(guard) => match logic(&guard) {
Ok(r) => match guard.try_sync() {
Ok(_) => {
return Ok(r);
}
Err(e) => match e {
OptimisticLockCouplingErrorType::Poisoned
| OptimisticLockCouplingErrorType::Outdated => {
return Err(e);
}
_ => {
continue 'txn;
}
},
},
Err(e) => match e {
OptimisticLockCouplingErrorType::Poisoned
| OptimisticLockCouplingErrorType::Outdated => {
return Err(e);
}
_ => {
continue 'txn;
}
},
},
Err(e) => match e {
OptimisticLockCouplingErrorType::Poisoned
| OptimisticLockCouplingErrorType::Outdated => {
return Err(e);
}
_ => {
continue 'txn;
}
},
}
}
}
}
impl<T: Sized> From<T> for OptimisticLockCoupling<T> {
#[inline(always)]
fn from(t: T) -> Self {
Self::new(t)
}
}
impl<T: ?Sized + Default> Default for OptimisticLockCoupling<T> {
#[inline(always)]
fn default() -> Self {
Self::new(Default::default())
}
}
impl<T: ?Sized> OptimisticLockCoupling<T> {
#[inline(always)]
pub fn make_outdate(&self) {
self.version_lock_outdate.fetch_or(0b1, Release);
}
#[inline(always)]
pub fn is_poisoned(&self) -> bool {
self.poisoned.load(std::sync::atomic::Ordering::Acquire)
}
#[inline(always)]
fn try_lock(&self) -> OptimisticLockCouplingResult<u64> {
use OptimisticLockCouplingErrorType::*;
if self.is_poisoned() {
return Err(Poisoned);
}
let version = self.version_lock_outdate.load(Acquire);
if is_outdate(version) {
return Err(Outdated);
}
if is_locked(version) {
return Err(Blocked);
}
Ok(version)
}
#[inline(always)]
pub fn read(&self) -> OptimisticLockCouplingResult<OptimisticLockCouplingReadGuard<'_, T>> {
OptimisticLockCouplingReadGuard::new(self)
}
#[inline(always)]
pub fn write(&self) -> OptimisticLockCouplingResult<OptimisticLockCouplingWriteGuard<'_, T>> {
use OptimisticLockCouplingErrorType::*;
let version = self.try_lock()?;
match self
.version_lock_outdate
.compare_exchange(version, version + 0b10, Acquire, Acquire)
{
Ok(_) => Ok(OptimisticLockCouplingWriteGuard::new(self)),
Err(_) => Err(VersionUpdated),
}
}
}
#[inline(always)]
fn is_locked(version: u64) -> bool {
version & 0b10 != 0
}
#[inline(always)]
fn is_outdate(version: u64) -> bool {
version & 0b1 != 0
}
pub struct OptimisticLockCouplingReadGuard<'a, T: ?Sized + 'a> {
lock: &'a OptimisticLockCoupling<T>,
version: u64,
}
impl<'a, T: ?Sized> OptimisticLockCouplingReadGuard<'a, T> {
#[inline(always)]
pub fn new(lock: &'a OptimisticLockCoupling<T>) -> OptimisticLockCouplingResult<Self> {
use crate::OptimisticLockCouplingErrorType::*;
if lock.is_poisoned() {
return Err(Poisoned);
}
let version = lock.try_lock()?;
Ok(Self {
lock: &lock,
version,
})
}
}
impl<T: ?Sized> !Send for OptimisticLockCouplingReadGuard<'_, T> {}
impl<T: ?Sized> OptimisticLockCouplingReadGuard<'_, T> {
#[inline(always)]
pub fn try_sync(self) -> OptimisticLockCouplingResult<()> {
if self.version == self.lock.try_lock()? {
drop(self);
Ok(())
} else {
use crate::OptimisticLockCouplingErrorType::*;
Err(VersionUpdated)
}
}
}
impl<T: ?Sized> Deref for OptimisticLockCouplingReadGuard<'_, T> {
type Target = T;
#[inline(always)]
fn deref(&self) -> &Self::Target {
unsafe { &*self.lock.data.get() }
}
}
impl<T: Debug> Debug for OptimisticLockCouplingReadGuard<'_, T> {
#[inline(always)]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("OptimisticLockCouplingReadGuard")
.field("version", &(self.version >> 2))
.field("data", self.deref())
.finish()
}
}
impl<T: Debug + Display> Display for OptimisticLockCouplingReadGuard<'_, T> {
#[inline(always)]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!(
"OptimisticLockCouplingReadGuard (ver: {}) {}",
self.version >> 2,
self.deref()
))
}
}
pub struct OptimisticLockCouplingWriteGuard<'a, T: ?Sized + 'a> {
lock: &'a OptimisticLockCoupling<T>,
}
unsafe impl<T: ?Sized + Sync> Sync for OptimisticLockCouplingWriteGuard<'_, T> {}
impl<T: ?Sized> Deref for OptimisticLockCouplingWriteGuard<'_, T> {
type Target = T;
#[inline(always)]
fn deref(&self) -> &Self::Target {
unsafe { &*self.lock.data.get() }
}
}
impl<T: ?Sized> DerefMut for OptimisticLockCouplingWriteGuard<'_, T> {
#[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *self.lock.data.get() }
}
}
impl<T: ?Sized> Drop for OptimisticLockCouplingWriteGuard<'_, T> {
#[inline(always)]
fn drop(&mut self) {
if std::thread::panicking() {
self.lock.poisoned.fetch_or(true, Release);
} else {
self.lock.version_lock_outdate.fetch_add(0b10, Release);
}
}
}
impl<T: Debug> Debug for OptimisticLockCouplingWriteGuard<'_, T> {
#[inline(always)]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("OptimisticLockCouplingWriteGuard")
.field(
"version",
&(self.lock.version_lock_outdate.load(Relaxed) >> 2),
)
.field("data", self.deref())
.finish()
}
}
impl<T: Debug + Display> Display for OptimisticLockCouplingWriteGuard<'_, T> {
#[inline(always)]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!(
"OptimisticLockCouplingWriteGuard (ver: {}) {}",
self.lock.version_lock_outdate.load(Relaxed) >> 2,
self.deref()
))
}
}
impl<'a, T: ?Sized> OptimisticLockCouplingWriteGuard<'a, T> {
#[inline(always)]
pub fn new(lock: &'a OptimisticLockCoupling<T>) -> Self {
Self { lock }
}
}