use crate::memtable::Memtable;
use crate::memtable::own_line::OwnLine;
use alloc::sync::Arc;
use core::cell::{Cell, UnsafeCell};
use core::sync::atomic::{AtomicUsize, Ordering};
use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard};
const SHARDS: usize = 64;
pub(crate) struct InsertGate {
shards: [OwnLine<RwLock<()>>; SHARDS],
active: UnsafeCell<Arc<Memtable>>,
}
unsafe impl Sync for InsertGate {}
pub(crate) struct Entered<'a> {
_shard: RwLockReadGuard<'a, ()>,
memtable: &'a Memtable,
}
impl Entered<'_> {
pub(crate) fn memtable(&self) -> &Memtable {
self.memtable
}
}
pub(crate) struct Closed<'a> {
gate: &'a InsertGate,
_shards: [RwLockWriteGuard<'a, ()>; SHARDS],
}
impl Closed<'_> {
pub(crate) fn set_active(&self, memtable: Arc<Memtable>) {
unsafe { *self.gate.active.get() = memtable };
}
}
static NEXT_SHARD: AtomicUsize = AtomicUsize::new(0);
std::thread_local! {
static SHARD: Cell<usize> = const { Cell::new(usize::MAX) };
}
fn this_threads_shard() -> usize {
SHARD.with(|shard| {
let mut index = shard.get();
if index == usize::MAX {
index = NEXT_SHARD.fetch_add(1, Ordering::Relaxed) % SHARDS;
shard.set(index);
}
index
})
}
impl InsertGate {
pub(crate) fn new(active: Arc<Memtable>) -> Self {
Self {
shards: core::array::from_fn(|_| OwnLine(RwLock::new(()))),
active: UnsafeCell::new(active),
}
}
#[inline]
#[expect(
clippy::indexing_slicing,
reason = "the shard index is reduced modulo SHARDS"
)]
pub(crate) fn enter(&self) -> Entered<'_> {
let shard = self.shards[this_threads_shard()].read();
let memtable = unsafe { &**self.active.get() };
Entered {
_shard: shard,
memtable,
}
}
pub(crate) fn close(&self) -> Closed<'_> {
Closed {
gate: self,
_shards: core::array::from_fn(|i| {
#[expect(clippy::indexing_slicing, reason = "i < SHARDS")]
self.shards[i].write()
}),
}
}
}
#[cfg(test)]
#[expect(clippy::expect_used, reason = "test code")]
mod tests;