use crate::epoch::Epoch;
use parking_lot::Mutex;
use std::collections::BTreeMap;
use std::collections::HashSet;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
#[derive(Default)]
pub struct ActiveSpills {
inner: Mutex<HashSet<u64>>,
}
impl ActiveSpills {
pub fn new() -> Self {
Self {
inner: Mutex::new(HashSet::new()),
}
}
pub fn register(self: &Arc<Self>, txn_id: u64) -> SpillGuard {
self.inner.lock().insert(txn_id);
SpillGuard {
registry: Arc::clone(self),
txn_id,
}
}
pub fn is_active(&self, txn_id: u64) -> bool {
self.inner.lock().contains(&txn_id)
}
pub fn is_idle(&self) -> bool {
self.inner.lock().is_empty()
}
fn release(&self, txn_id: u64) {
self.inner.lock().remove(&txn_id);
}
}
pub struct SpillGuard {
registry: Arc<ActiveSpills>,
txn_id: u64,
}
impl Drop for SpillGuard {
fn drop(&mut self) {
self.registry.release(self.txn_id);
}
}
#[derive(Default)]
pub struct SnapshotRegistry {
live: Mutex<BTreeMap<u64, u64>>,
history_epochs: AtomicU64,
history_start: AtomicU64,
}
impl SnapshotRegistry {
pub fn new() -> Self {
Self {
live: Mutex::new(BTreeMap::new()),
history_epochs: AtomicU64::new(0),
history_start: AtomicU64::new(0),
}
}
pub fn configure_history(&self, epochs: u64, start_epoch: Epoch) {
self.history_start.store(start_epoch.0, Ordering::Release);
self.history_epochs.store(epochs, Ordering::Release);
}
pub fn history_config(&self) -> (u64, Epoch) {
(
self.history_epochs.load(Ordering::Acquire),
Epoch(self.history_start.load(Ordering::Acquire)),
)
}
pub fn history_floor(&self, visible: Epoch) -> Option<Epoch> {
let (epochs, start) = self.history_config();
(epochs > 0).then(|| Epoch(start.0.max(visible.0.saturating_sub(epochs))))
}
pub fn register(&self, epoch: Epoch) -> SnapshotGuard<'_> {
let mut live = self.live.lock();
*live.entry(epoch.0).or_insert(0) += 1;
SnapshotGuard {
registry: self,
epoch,
}
}
pub fn min_active(&self, visible: Epoch) -> Epoch {
match self.live.lock().keys().next().copied() {
Some(min) => Epoch(min),
None => visible,
}
}
pub fn min_pinned(&self) -> Option<Epoch> {
self.live.lock().keys().next().copied().map(Epoch)
}
fn release(&self, epoch: Epoch) {
let mut live = self.live.lock();
if let Some(count) = live.get_mut(&epoch.0) {
*count -= 1;
if *count == 0 {
live.remove(&epoch.0);
}
}
}
}
pub struct SnapshotGuard<'r> {
registry: &'r SnapshotRegistry,
epoch: Epoch,
}
impl Drop for SnapshotGuard<'_> {
fn drop(&mut self) {
self.registry.release(self.epoch);
}
}
pub struct OwnedSnapshotGuard {
registry: Arc<SnapshotRegistry>,
epoch: Epoch,
}
impl OwnedSnapshotGuard {
pub fn epoch(&self) -> Epoch {
self.epoch
}
}
impl Drop for OwnedSnapshotGuard {
fn drop(&mut self) {
self.registry.release(self.epoch);
}
}
impl SnapshotRegistry {
pub fn register_owned(self: &Arc<Self>, epoch: Epoch) -> OwnedSnapshotGuard {
{
let mut live = self.live.lock();
*live.entry(epoch.0).or_insert(0) += 1;
}
OwnedSnapshotGuard {
registry: Arc::clone(self),
epoch,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn retention_tracks_min_active_snapshot() {
let r = SnapshotRegistry::new();
assert_eq!(r.min_active(Epoch(10)), Epoch(10));
let g1 = r.register(Epoch(5));
let g2 = r.register(Epoch(8));
assert_eq!(r.min_active(Epoch(10)), Epoch(5));
drop(g1);
assert_eq!(r.min_active(Epoch(10)), Epoch(8));
drop(g2);
assert_eq!(r.min_active(Epoch(10)), Epoch(10));
}
#[test]
fn retention_refcounts_duplicate_epochs() {
let r = SnapshotRegistry::new();
let a = r.register(Epoch(3));
let b = r.register(Epoch(3));
assert_eq!(r.min_active(Epoch(9)), Epoch(3));
drop(a);
assert_eq!(r.min_active(Epoch(9)), Epoch(3));
drop(b);
assert_eq!(r.min_active(Epoch(9)), Epoch(9));
}
}