use std::cell::Cell;
use std::sync::atomic::{AtomicUsize, Ordering};
const SHARD_CNT: usize = 9;
pub(crate) const GEN_CNT: usize = 2;
#[repr(align(64))]
#[derive(Default)]
pub struct Shard(pub(crate) [AtomicUsize; GEN_CNT]);
impl Shard {
pub(crate) fn snapshot(&self) -> [usize; GEN_CNT] {
[
self.0[0].load(Ordering::Acquire),
self.0[1].load(Ordering::Acquire),
]
}
}
pub unsafe trait LockStorage: Default {
type Shards: AsRef<[Shard]>;
fn gen_idx(&self) -> &AtomicUsize;
fn shards(&self) -> &Self::Shards;
fn choose_shard(&self) -> usize;
}
static GEN_IDX: AtomicUsize = AtomicUsize::new(0);
macro_rules! sh {
() => {
Shard([AtomicUsize::new(0), AtomicUsize::new(0)])
};
}
type Shards = [Shard; SHARD_CNT];
static SHARDS: [Shard; SHARD_CNT] = [
sh!(),
sh!(),
sh!(),
sh!(),
sh!(),
sh!(),
sh!(),
sh!(),
sh!(),
];
static THREAD_ID_GEN: AtomicUsize = AtomicUsize::new(0);
thread_local! {
static THREAD_SHARD: Cell<usize> = Cell::new(SHARD_CNT);
}
#[derive(Default)]
pub struct Global;
unsafe impl LockStorage for Global {
type Shards = Shards;
#[inline]
fn gen_idx(&self) -> &AtomicUsize {
&GEN_IDX
}
#[inline]
fn shards(&self) -> &Shards {
&SHARDS
}
#[inline]
fn choose_shard(&self) -> usize {
THREAD_SHARD
.try_with(|ts| {
let mut val = ts.get();
if val >= SHARD_CNT {
val = THREAD_ID_GEN.fetch_add(1, Ordering::Relaxed) % SHARD_CNT;
ts.set(val);
}
val
})
.unwrap_or(0)
}
}
#[derive(Default)]
pub struct PrivateUnsharded {
gen_idx: AtomicUsize,
shard: [Shard; 1],
}
unsafe impl LockStorage for PrivateUnsharded {
type Shards = [Shard; 1];
#[inline]
fn gen_idx(&self) -> &AtomicUsize {
&self.gen_idx
}
#[inline]
fn shards(&self) -> &[Shard; 1] {
&self.shard
}
#[inline]
fn choose_shard(&self) -> usize {
0
}
}
#[derive(Default)]
pub struct PrivateSharded<S> {
gen_idx: AtomicUsize,
shards: S,
}
static PRIV_THREAD_ID_GEN: AtomicUsize = AtomicUsize::new(0);
thread_local! {
static PRIV_THREAD_ID: usize = PRIV_THREAD_ID_GEN.fetch_add(1, Ordering::Relaxed);
}
unsafe impl<S: AsRef<[Shard]> + Default> LockStorage for PrivateSharded<S> {
type Shards = S;
#[inline]
fn gen_idx(&self) -> &AtomicUsize {
&self.gen_idx
}
#[inline]
fn shards(&self) -> &Self::Shards {
&self.shards
}
#[inline]
fn choose_shard(&self) -> usize {
PRIV_THREAD_ID
.try_with(|id| id % self.shards.as_ref().len())
.unwrap_or(0)
}
}
#[cfg(test)]
mod tests {
extern crate crossbeam_utils;
use std::sync::Arc;
use self::crossbeam_utils::thread;
use super::super::{ArcSwapAny, SignalSafety};
use super::*;
const ITERATIONS: usize = 100;
fn basic_check<S: LockStorage + Send + Sync>() {
for _ in 0..ITERATIONS {
let shared = ArcSwapAny::<_, S>::from(Arc::new(usize::max_value()));
thread::scope(|scope| {
for i in 0..2 {
let shared = &shared;
scope.spawn(move |_| {
for j in 0..50 {
if j % 2 == i {
while **shared.lock_internal(SignalSafety::Unsafe) != j {}
} else {
shared.store(Arc::new(j));
}
}
});
}
})
.unwrap();
}
}
#[test]
fn basic_check_global() {
basic_check::<Global>();
}
#[test]
fn basic_check_private_unsharded() {
basic_check::<PrivateUnsharded>();
}
#[test]
fn basic_check_private_sharded_2() {
basic_check::<PrivateSharded<[Shard; 2]>>();
}
#[test]
fn basic_check_private_sharded_63() {
basic_check::<PrivateSharded<[Shard; 31]>>();
}
}