holochain_locksmith 0.0.42-alpha2

helpers for mutex deadlock diagnosis
Documentation
use crate::{
    common::LOCK_TIMEOUT,
    error::{LockType, LocksmithError, LocksmithErrorKind, LocksmithResult},
    guard::{HcMutexGuard, HcRwLockReadGuard, HcRwLockWriteGuard},
};
use parking_lot::{Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard};
use std::time::{Duration, Instant};

#[derive(Debug)]
pub struct HcMutex<T: ?Sized> {
    fair_unlocking: bool,
    inner: Mutex<T>,
}

impl<T> HcMutex<T> {
    pub fn new(v: T) -> Self {
        Self {
            fair_unlocking: false,
            inner: Mutex::new(v),
        }
    }

    pub fn new_with_fair_unlocking(v: T) -> Self {
        Self {
            fair_unlocking: true,
            inner: Mutex::new(v),
        }
    }

    pub fn use_fair_unlocking(mut self) -> Self {
        self.fair_unlocking = true;
        self
    }
}

#[derive(Debug)]
pub struct HcRwLock<T: ?Sized> {
    fair_unlocking: bool,
    inner: RwLock<T>,
}

impl<T> HcRwLock<T> {
    pub fn new(v: T) -> Self {
        Self {
            fair_unlocking: false,
            inner: RwLock::new(v),
        }
    }

    pub fn new_with_fair_unlocking(v: T) -> Self {
        Self {
            fair_unlocking: true,
            inner: RwLock::new(v),
        }
    }

    pub fn use_fair_unlocking(mut self) -> Self {
        self.fair_unlocking = true;
        self
    }
}

macro_rules! mutex_impl {
    ($HcMutex: ident, $Mutex: ident, $HcGuard:ident, $Guard:ident, $lock_type:ident, $lock_fn:ident, $try_lock_fn:ident, $try_lock_for_fn:ident, $try_lock_until_fn:ident, $new_guard_fn:ident) => {
        impl<T: ?Sized> $HcMutex<T> {
            pub fn $lock_fn(&self) -> LocksmithResult<$HcGuard<T>> {
                self.$try_lock_for_fn(*LOCK_TIMEOUT).ok_or_else(|| {
                    LocksmithError::new(LockType::$lock_type, LocksmithErrorKind::LocksmithTimeout)
                })
            }

            pub fn $try_lock_for_fn(&self, duration: Duration) -> Option<$HcGuard<T>> {
                self.inner
                    .$try_lock_for_fn(duration)
                    .map(|g| self.$new_guard_fn(g))
            }

            pub fn $try_lock_until_fn(&self, instant: Instant) -> Option<$HcGuard<T>> {
                self.inner
                    .$try_lock_until_fn(instant)
                    .map(|g| self.$new_guard_fn(g))
            }

            pub fn $try_lock_fn(&self) -> Option<$HcGuard<T>> {
                (*self).inner.$try_lock_fn().map(|g| self.$new_guard_fn(g))
            }

            fn $new_guard_fn<'a>(&self, inner: $Guard<'a, T>) -> $HcGuard<'a, T> {
                if self.fair_unlocking {
                    $HcGuard::new(inner).use_fair_unlocking()
                } else {
                    $HcGuard::new(inner)
                }
            }
        }
    };
}

mutex_impl!(
    HcMutex,
    Mutex,
    HcMutexGuard,
    MutexGuard,
    Lock,
    lock,
    try_lock,
    try_lock_for,
    try_lock_until,
    new_guard
);
mutex_impl!(
    HcRwLock,
    RwLock,
    HcRwLockReadGuard,
    RwLockReadGuard,
    Read,
    read,
    try_read,
    try_read_for,
    try_read_until,
    new_guard_read
);
mutex_impl!(
    HcRwLock,
    RwLock,
    HcRwLockWriteGuard,
    RwLockWriteGuard,
    Write,
    write,
    try_write,
    try_write_for,
    try_write_until,
    new_guard_write
);