use core::fmt::Display;
use std::{
cell::Cell,
num::NonZeroUsize,
sync::atomic::{AtomicUsize, Ordering},
};
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct StoreId(NonZeroUsize);
#[cfg(feature = "artifact-size")]
impl loupe::MemoryUsage for StoreId {
fn size_of_val(&self, _visited: &mut dyn loupe::MemoryUsageTracker) -> usize {
std::mem::size_of_val(self)
}
}
impl StoreId {
pub fn as_raw(&self) -> NonZeroUsize {
self.0
}
}
const CHUNK_SIZE: usize = 256;
static NEXT_CHUNK_START: AtomicUsize = AtomicUsize::new(1);
#[derive(Clone, Copy)]
struct ChunkCursor {
next: usize,
end: usize,
}
impl ChunkCursor {
const EMPTY: Self = Self { next: 0, end: 0 };
}
thread_local! {
static LOCAL_CURSOR: Cell<ChunkCursor> = const { Cell::new(ChunkCursor::EMPTY) };
}
impl Default for StoreId {
fn default() -> Self {
let raw = LOCAL_CURSOR.with(|cell| {
let mut cursor = cell.get();
if cursor.next == cursor.end {
cursor.next = NEXT_CHUNK_START.fetch_add(CHUNK_SIZE, Ordering::Relaxed);
cursor.end = cursor.next + CHUNK_SIZE;
}
let id = cursor.next;
cursor.next += 1;
cell.set(cursor);
id
});
Self(NonZeroUsize::new(raw).expect("chunked allocator never returns 0"))
}
}
impl Display for StoreId {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let val: usize = self.0.into();
if val == usize::MAX {
write!(f, "unknown")
} else {
write!(f, "{}", self.0)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashSet;
use std::sync::Mutex;
use std::thread;
#[test]
fn ids_unique_within_a_thread_across_chunk_boundaries() {
let n = CHUNK_SIZE * 3 + 17;
let mut seen = HashSet::with_capacity(n);
for _ in 0..n {
let id = StoreId::default();
assert!(
seen.insert(id.as_raw()),
"duplicate ID handed out within a single thread",
);
}
}
#[test]
fn ids_unique_across_threads_under_load() {
const THREADS: usize = 16;
const PER_THREAD: usize = CHUNK_SIZE * 8;
let collected: Mutex<HashSet<NonZeroUsize>> =
Mutex::new(HashSet::with_capacity(THREADS * PER_THREAD));
thread::scope(|s| {
for _ in 0..THREADS {
s.spawn(|| {
let mut local = Vec::with_capacity(PER_THREAD);
for _ in 0..PER_THREAD {
local.push(StoreId::default().as_raw());
}
let mut guard = collected.lock().unwrap();
for id in local {
assert!(
guard.insert(id),
"duplicate ID handed out across threads: {id}",
);
}
});
}
});
let total = collected.into_inner().unwrap().len();
assert_eq!(
total,
THREADS * PER_THREAD,
"expected every produced ID to be unique",
);
}
#[test]
fn allocator_never_returns_zero() {
for _ in 0..10_000 {
let id = StoreId::default();
assert_ne!(id.as_raw().get(), 0);
}
}
}