use std::ops::{Deref, DerefMut};
use std::sync::{LockResult, Mutex, PoisonError};
mod sealed {
pub(crate) trait Sealed {}
}
pub(crate) trait Rank: sealed::Sealed {
#[cfg_attr(not(debug_assertions), allow(dead_code))]
const VALUE: u16;
}
pub(crate) mod rank {
use super::{Rank, sealed::Sealed};
macro_rules! ranks {
($($(#[$m:meta])* $name:ident = $value:literal;)*) => {$(
$(#[$m])*
pub(crate) enum $name {}
impl Sealed for $name {}
impl Rank for $name {
const VALUE: u16 = $value;
}
)*};
}
ranks! {
Rotation = 100;
LastFetched = 200;
Tokens = 250;
UsageStore = 300;
UsageStatus = 350;
Config = 400;
State = 500;
Activity = 600;
NextRefresh = 1100;
RefetchQueue = 1200;
PendingSwitch = 1500;
PendingAutoStart = 1600;
PendingSwitchOff = 1700;
}
}
#[cfg(debug_assertions)]
thread_local! {
static HELD: std::cell::RefCell<Vec<u16>> = const { std::cell::RefCell::new(Vec::new()) };
}
pub(crate) struct RankGuard {
#[cfg(debug_assertions)]
rank: u16,
}
impl RankGuard {
#[inline]
pub(crate) fn enter<R: Rank>() -> Self {
#[cfg(debug_assertions)]
{
let rank = R::VALUE;
HELD.with(|h| {
let mut h = h.borrow_mut();
debug_assert!(
h.last().is_none_or(|&top| rank > top),
"lock-order violation: acquiring rank {rank} while holding {:?} \
(would invert the global lock order and risk deadlock)",
h.as_slice(),
);
h.push(rank);
});
Self { rank }
}
#[cfg(not(debug_assertions))]
{
Self {}
}
}
}
impl Drop for RankGuard {
#[inline]
fn drop(&mut self) {
#[cfg(debug_assertions)]
HELD.with(|h| {
let mut h = h.borrow_mut();
if let Some(pos) = h.iter().rposition(|&r| r == self.rank) {
h.remove(pos);
}
});
}
}
pub(crate) struct RankedMutex<T, R: Rank> {
inner: Mutex<T>,
_rank: std::marker::PhantomData<R>,
}
impl<T, R: Rank> RankedMutex<T, R> {
pub(crate) fn new(value: T) -> Self {
Self {
inner: Mutex::new(value),
_rank: std::marker::PhantomData,
}
}
pub(crate) fn lock(&self) -> LockResult<RankedGuard<'_, T>> {
let rank = RankGuard::enter::<R>();
match self.inner.lock() {
Ok(guard) => Ok(RankedGuard { guard, _rank: rank }),
Err(poison) => Err(PoisonError::new(RankedGuard {
guard: poison.into_inner(),
_rank: rank,
})),
}
}
}
pub(crate) struct RankedGuard<'a, T> {
guard: std::sync::MutexGuard<'a, T>,
_rank: RankGuard,
}
impl<T> Deref for RankedGuard<'_, T> {
type Target = T;
fn deref(&self) -> &T {
&self.guard
}
}
impl<T> DerefMut for RankedGuard<'_, T> {
fn deref_mut(&mut self) -> &mut T {
&mut self.guard
}
}