use std::sync::atomic::{AtomicU64, Ordering};
#[derive(Debug)]
pub struct AtomicNodeLocks {
bits: Vec<AtomicU64>,
}
impl AtomicNodeLocks {
pub fn new(capacity: usize) -> Self {
let num_slots = capacity.div_ceil(64);
Self {
bits: (0..num_slots).map(|_| AtomicU64::new(0)).collect(),
}
}
#[inline(always)]
pub fn try_lock(&self, idx: usize) -> bool {
let slot = idx / 64;
let bit = 1u64 << (idx % 64);
let prev = self.bits[slot].fetch_or(bit, Ordering::Acquire);
(prev & bit) != 0
}
#[inline(always)]
pub fn lock(&self, idx: usize) {
while self.try_lock(idx) {
std::hint::spin_loop();
}
}
#[inline(always)]
pub fn unlock(&self, idx: usize) {
let slot = idx / 64;
let bit = !(1u64 << (idx % 64));
self.bits[slot].fetch_and(bit, Ordering::Release);
}
#[inline(always)]
pub fn lock_guard(&self, idx: usize) -> NodeLockGuard<'_> {
self.lock(idx);
NodeLockGuard { locks: self, idx }
}
}
pub struct NodeLockGuard<'a> {
locks: &'a AtomicNodeLocks,
idx: usize,
}
impl<'a> Drop for NodeLockGuard<'a> {
fn drop(&mut self) {
self.locks.unlock(self.idx);
}
}