use std::{
fmt::{self, Debug, Formatter},
marker::PhantomData,
ops::Deref,
sync::atomic::{AtomicI64, Ordering},
};
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> {
pub const STRIPE_MASK: usize = N - 1;
pub const fn new() -> Self {
const {
assert!(
N > 0 && N.is_power_of_two(),
"StripedCounter 条带数必须为 2 的正整数次幂"
);
}
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, W = CacheAlignedLock<T>> {
stripes: Box<[W]>,
_marker: PhantomData<T>,
}
impl<T, const N: usize, W> Default for StripedRwLock<T, N, W>
where
T: Default,
W: From<RwLock<T>> + Deref<Target = RwLock<T>>,
{
#[inline]
fn default() -> Self {
Self::new()
}
}
impl<T, const N: usize, W> StripedRwLock<T, N, W>
where
W: Deref<Target = RwLock<T>>,
{
pub const STRIPE_MASK: usize = N - 1;
pub fn with_initializer<F>(mut init: F) -> Self
where
F: FnMut(usize) -> T,
W: From<RwLock<T>>,
{
const {
assert!(
N > 0 && N.is_power_of_two(),
"StripedRwLock 条带数必须为 2 的正整数次幂"
);
}
let mut stripes = Vec::with_capacity(N);
stripes.extend((0..N).map(|i| W::from(RwLock::new(init(i)))));
Self {
stripes: stripes.into_boxed_slice(),
_marker: PhantomData,
}
}
#[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> {
self.read_at(Self::stripe_index(hash))
}
#[inline]
pub fn write(&self, hash: u64) -> RwLockWriteGuard<'_, T> {
self.write_at(Self::stripe_index(hash))
}
#[inline]
pub fn read_at(&self, index: usize) -> RwLockReadGuard<'_, T> {
let idx = index & Self::STRIPE_MASK;
unsafe { self.stripes.get_unchecked(idx) }.read()
}
#[inline]
pub fn write_at(&self, index: usize) -> RwLockWriteGuard<'_, T> {
let idx = index & Self::STRIPE_MASK;
unsafe { self.stripes.get_unchecked(idx) }.write()
}
#[inline]
pub fn try_read_at(&self, index: usize) -> Option<RwLockReadGuard<'_, T>> {
let idx = index & Self::STRIPE_MASK;
unsafe { self.stripes.get_unchecked(idx) }.try_read()
}
#[inline]
pub fn try_write(&self, hash: u64) -> Option<RwLockWriteGuard<'_, T>> {
self.try_write_at(Self::stripe_index(hash))
}
#[inline]
pub fn try_write_at(&self, index: usize) -> Option<RwLockWriteGuard<'_, T>> {
let idx = index & Self::STRIPE_MASK;
unsafe { self.stripes.get_unchecked(idx) }.try_write()
}
#[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) -> &[W] {
&self.stripes
}
}
impl<T, const N: usize, W> Debug for StripedRwLock<T, N, W> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("StripedRwLock").field("len", &N).finish()
}
}
impl<T, const N: usize, W> StripedRwLock<T, N, W>
where
T: Default,
W: From<RwLock<T>> + Deref<Target = RwLock<T>>,
{
pub fn new() -> Self {
Self::with_initializer(|_| T::default())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[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 idx = StripedRwLock::<(), 16>::stripe_index(1);
let r = lock.try_read_at(idx);
assert!(r.is_some());
assert!(lock.try_write_at(idx).is_none());
drop(r);
let w = lock.try_write_at(idx);
assert!(w.is_some());
assert!(lock.try_read_at(idx).is_none());
drop(w);
assert!(lock.try_read_at(idx).is_some());
}
#[test]
fn test_striped_rwlock_padded64_slot() {
use crate::align::CachePadded64;
let lock = StripedRwLock::<i32, 8, CachePadded64<RwLock<i32>>>::new();
assert_eq!(lock.len(), 8);
{
let mut w = lock.write_at(3);
*w = 7;
}
assert_eq!(*lock.read_at(3), 7);
let default_slot_lock = StripedRwLock::<(), 8>::new();
let r = default_slot_lock.try_read_at(2);
assert!(r.is_some());
assert!(default_slot_lock.try_write_at(2).is_none());
drop(r);
assert!(default_slot_lock.try_write_at(2).is_some());
}
}