use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard};
pub mod level {
pub const CATALOG: usize = 1;
pub const BLOOMS: usize = 2;
pub const TOMBSTONES: usize = 3;
pub const ROLLUP_REGISTRY: usize = 4;
}
#[cfg(debug_assertions)]
std::thread_local! {
static HELD_LEVELS: std::cell::RefCell<Vec<usize>> = const { std::cell::RefCell::new(Vec::new()) };
}
#[cfg(debug_assertions)]
fn push_level(level: usize) {
HELD_LEVELS.with(|h| {
let mut stack = h.borrow_mut();
if let Some(&top) = stack.last() {
assert!(
level > top,
"lock ordering violation — attempted to acquire level {level} \
while holding level {top}. Required order: catalog(1) → \
blooms(2) → tombstones(3) → rollup_registry(4)"
);
}
stack.push(level);
});
}
#[cfg(debug_assertions)]
fn pop_level(level: usize) {
HELD_LEVELS.with(|h| {
let mut stack = h.borrow_mut();
if let Some(&top) = stack.last()
&& top == level
{
stack.pop();
}
});
}
pub struct OrderedRwLock<const LEVEL: usize, T: ?Sized> {
inner: RwLock<T>,
}
impl<const LEVEL: usize, T> OrderedRwLock<LEVEL, T> {
pub fn new(value: T) -> Self {
Self {
inner: RwLock::new(value),
}
}
}
impl<const LEVEL: usize, T: ?Sized> OrderedRwLock<LEVEL, T> {
#[inline]
pub fn read(&self) -> OrderedReadGuard<'_, LEVEL, T> {
#[cfg(debug_assertions)]
push_level(LEVEL);
OrderedReadGuard {
guard: self.inner.read(),
}
}
#[inline]
pub fn write(&self) -> OrderedWriteGuard<'_, LEVEL, T> {
#[cfg(debug_assertions)]
push_level(LEVEL);
OrderedWriteGuard {
guard: self.inner.write(),
}
}
}
pub struct OrderedReadGuard<'a, const LEVEL: usize, T: ?Sized> {
guard: RwLockReadGuard<'a, T>,
}
impl<const LEVEL: usize, T: ?Sized> std::ops::Deref for OrderedReadGuard<'_, LEVEL, T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
&self.guard
}
}
#[cfg(debug_assertions)]
impl<const LEVEL: usize, T: ?Sized> Drop for OrderedReadGuard<'_, LEVEL, T> {
fn drop(&mut self) {
pop_level(LEVEL);
}
}
pub struct OrderedWriteGuard<'a, const LEVEL: usize, T: ?Sized> {
guard: RwLockWriteGuard<'a, T>,
}
impl<const LEVEL: usize, T: ?Sized> std::ops::Deref for OrderedWriteGuard<'_, LEVEL, T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
&self.guard
}
}
impl<const LEVEL: usize, T: ?Sized> std::ops::DerefMut for OrderedWriteGuard<'_, LEVEL, T> {
#[inline]
fn deref_mut(&mut self) -> &mut T {
&mut self.guard
}
}
#[cfg(debug_assertions)]
impl<const LEVEL: usize, T: ?Sized> Drop for OrderedWriteGuard<'_, LEVEL, T> {
fn drop(&mut self) {
pop_level(LEVEL);
}
}
pub type CatalogLock<T> = OrderedRwLock<{ level::CATALOG }, T>;
pub type BloomsLock<T> = OrderedRwLock<{ level::BLOOMS }, T>;
pub type TombstonesLock<T> = OrderedRwLock<{ level::TOMBSTONES }, T>;
pub type RollupRegistryLock<T> = OrderedRwLock<{ level::ROLLUP_REGISTRY }, T>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ascending_order_allowed() {
let a = OrderedRwLock::<1, _>::new(1u32);
let b = OrderedRwLock::<2, _>::new(2u32);
let ga = a.read();
let gb = b.read();
assert_eq!(*ga, 1);
assert_eq!(*gb, 2);
drop(gb);
drop(ga);
}
#[test]
fn single_lock_allowed() {
let a = OrderedRwLock::<3, _>::new("hello");
let g = a.write();
assert_eq!(*g, "hello");
drop(g);
}
#[test]
#[cfg(debug_assertions)]
#[should_panic(expected = "lock ordering violation")]
fn descending_order_panics() {
let a = OrderedRwLock::<2, _>::new(());
let b = OrderedRwLock::<1, _>::new(());
let _ga = a.read();
let _gb = b.read(); }
#[test]
fn write_guard_deref_mut() {
let a = OrderedRwLock::<1, _>::new(0u32);
{
let mut g = a.write();
*g = 42;
}
assert_eq!(*a.read(), 42);
}
}