use std::{
fmt::{self, Debug, Formatter},
sync::atomic::{AtomicI64, Ordering},
time::Duration,
};
use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard};
use crate::align::CachePadded;
pub struct StripedCounter<const N: usize = 64> {
slots: [CachePadded<AtomicI64>; N],
}
impl<const N: usize> Default for StripedCounter<N> {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl<const N: usize> StripedCounter<N> {
const ASSERT_POWER_OF_TWO: () = assert!(
N > 0 && (N & (N - 1)) == 0,
"StripedCounter 条带数必须为 2 的正整数次幂"
);
pub const STRIPE_MASK: usize = N - 1;
pub const fn new() -> Self {
#[expect(clippy::let_unit_value, reason = "编译期触发静态断言求值")]
let _ = Self::ASSERT_POWER_OF_TWO;
Self {
slots: [const { CachePadded::new(AtomicI64::new(0)) }; N],
}
}
#[must_use]
#[inline(always)]
pub const fn stripe_index(index: usize) -> usize {
index & Self::STRIPE_MASK
}
#[inline]
pub fn add(&self, stripe_index: usize, delta: i64) {
let idx = Self::stripe_index(stripe_index);
unsafe { self.slots.get_unchecked(idx) }.fetch_add(delta, Ordering::Relaxed);
}
#[inline]
pub fn sub(&self, stripe_index: usize, delta: i64) {
let idx = Self::stripe_index(stripe_index);
unsafe { self.slots.get_unchecked(idx) }.fetch_sub(delta, Ordering::Relaxed);
}
#[must_use]
#[inline]
pub fn get(&self) -> i64 {
self.slots.iter().map(|s| s.load(Ordering::Relaxed)).sum()
}
#[must_use]
#[inline]
pub fn get_positive(&self) -> usize {
self.get().max(0) as usize
}
#[inline]
pub fn reset(&self) {
for slot in &self.slots {
slot.store(0, Ordering::Relaxed);
}
}
#[must_use]
#[inline(always)]
pub const fn len(&self) -> usize {
N
}
#[must_use]
#[inline(always)]
pub const fn is_empty(&self) -> bool {
false
}
#[must_use]
#[inline(always)]
pub fn slots(&self) -> &[CachePadded<AtomicI64>] {
&self.slots
}
}
impl<const N: usize> Debug for StripedCounter<N> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("StripedCounter")
.field("len", &N)
.field("total", &self.get())
.finish()
}
}
pub type CacheAlignedLock<T> = CachePadded<RwLock<T>>;
pub struct StripedRwLock<T = (), const N: usize = 128> {
stripes: Box<[CacheAlignedLock<T>]>,
}
impl<T: Default, const N: usize> Default for StripedRwLock<T, N> {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl<T, const N: usize> StripedRwLock<T, N> {
const ASSERT_POWER_OF_TWO: () = assert!(
N > 0 && (N & (N - 1)) == 0,
"StripedRwLock 条带数必须为 2 的正整数次幂"
);
pub const STRIPE_MASK: usize = N - 1;
pub fn with_initializer<F>(mut init: F) -> Self
where
F: FnMut(usize) -> T,
{
#[expect(clippy::let_unit_value, reason = "编译期触发静态断言求值")]
let _ = Self::ASSERT_POWER_OF_TWO;
let mut stripes = Vec::with_capacity(N);
stripes.extend((0..N).map(|i| CachePadded::new(RwLock::new(init(i)))));
Self {
stripes: stripes.into_boxed_slice(),
}
}
#[must_use]
#[inline(always)]
pub const fn stripe_index(hash: u64) -> usize {
(hash as usize) & Self::STRIPE_MASK
}
#[inline]
pub fn read(&self, hash: u64) -> RwLockReadGuard<'_, T> {
let idx = Self::stripe_index(hash);
unsafe { self.stripes.get_unchecked(idx) }.0.read()
}
#[inline]
pub fn write(&self, hash: u64) -> RwLockWriteGuard<'_, T> {
let idx = Self::stripe_index(hash);
unsafe { self.stripes.get_unchecked(idx) }.0.write()
}
#[inline]
pub fn read_at(&self, index: usize) -> RwLockReadGuard<'_, T> {
let idx = index & Self::STRIPE_MASK;
unsafe { self.stripes.get_unchecked(idx) }.0.read()
}
#[inline]
pub fn write_at(&self, index: usize) -> RwLockWriteGuard<'_, T> {
let idx = index & Self::STRIPE_MASK;
unsafe { self.stripes.get_unchecked(idx) }.0.write()
}
#[inline]
pub fn try_read(&self, hash: u64) -> Option<RwLockReadGuard<'_, T>> {
self.try_read_at(Self::stripe_index(hash))
}
#[inline]
pub fn try_write(&self, hash: u64) -> Option<RwLockWriteGuard<'_, T>> {
self.try_write_at(Self::stripe_index(hash))
}
#[inline]
pub fn try_read_at(&self, index: usize) -> Option<RwLockReadGuard<'_, T>> {
let idx = index & Self::STRIPE_MASK;
unsafe { self.stripes.get_unchecked(idx) }.0.try_read()
}
#[inline]
pub fn try_write_at(&self, index: usize) -> Option<RwLockWriteGuard<'_, T>> {
let idx = index & Self::STRIPE_MASK;
unsafe { self.stripes.get_unchecked(idx) }.0.try_write()
}
#[inline]
pub fn try_read_for(&self, hash: u64, timeout: Duration) -> Option<RwLockReadGuard<'_, T>> {
self.try_read_at_for(Self::stripe_index(hash), timeout)
}
#[inline]
pub fn try_write_for(&self, hash: u64, timeout: Duration) -> Option<RwLockWriteGuard<'_, T>> {
self.try_write_at_for(Self::stripe_index(hash), timeout)
}
#[inline]
pub fn try_read_at_for(&self, index: usize, timeout: Duration) -> Option<RwLockReadGuard<'_, T>> {
if timeout.is_zero() {
return self.try_read_at(index);
}
let idx = index & Self::STRIPE_MASK;
unsafe { self.stripes.get_unchecked(idx) }
.0
.try_read_for(timeout)
}
#[inline]
pub fn try_write_at_for(
&self,
index: usize,
timeout: Duration,
) -> Option<RwLockWriteGuard<'_, T>> {
if timeout.is_zero() {
return self.try_write_at(index);
}
let idx = index & Self::STRIPE_MASK;
unsafe { self.stripes.get_unchecked(idx) }
.0
.try_write_for(timeout)
}
#[must_use]
#[inline(always)]
pub const fn len(&self) -> usize {
N
}
#[must_use]
#[inline(always)]
pub const fn is_empty(&self) -> bool {
false
}
#[must_use]
#[inline(always)]
pub fn stripes(&self) -> &[CacheAlignedLock<T>] {
&self.stripes
}
}
impl<T, const N: usize> Debug for StripedRwLock<T, N> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("StripedRwLock").field("len", &N).finish()
}
}
impl<T: Default, const N: usize> StripedRwLock<T, N> {
pub fn new() -> Self {
Self::with_initializer(|_| T::default())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_striped_counter() {
let counter = StripedCounter::<64>::new();
assert_eq!(counter.len(), 64);
assert!(!counter.is_empty());
assert_eq!(counter.get(), 0);
counter.add(0, 10);
counter.add(64, 5); counter.sub(1, 3);
assert_eq!(counter.get(), 12);
assert_eq!(counter.get_positive(), 12);
counter.reset();
assert_eq!(counter.get(), 0);
}
#[test]
fn test_striped_rwlock_basic() {
let lock = StripedRwLock::<i32, 16>::new();
assert_eq!(lock.len(), 16);
assert!(!lock.is_empty());
{
let mut w = lock.write(0);
*w = 42;
}
{
let r1 = lock.read(0);
let r2 = lock.read_at(16); assert_eq!(*r1, 42);
assert_eq!(*r2, 42);
}
}
#[test]
fn test_striped_rwlock_try_lock() {
let lock = StripedRwLock::<(), 16>::new();
let r = lock.try_read(1);
assert!(r.is_some());
assert!(lock.try_write(1).is_none());
assert!(lock.try_write_for(1, Duration::ZERO).is_none());
assert!(lock.try_write_at_for(1, Duration::from_millis(1)).is_none());
drop(r);
let w = lock.try_write(1);
assert!(w.is_some());
assert!(lock.try_read(1).is_none());
drop(w);
assert!(lock.try_read_for(1, Duration::ZERO).is_some());
}
}