mod histogram;
mod history;
mod cell;
mod vacuum;
#[cfg(test)] mod tests;
use histogram::{TokenHist, Token};
use guest_cell::{Guest, id::{UniqueId,Id}};
use parking_lot::Mutex;
use std::collections::HashSet;
use cell::ErasedCell;
pub use cell::MvccCell;
pub use vacuum::Vacuum;
#[cfg(not(feature = "wide_id"))] type TxnId = u64;
#[cfg(feature = "wide_id")] type TxnId = u128;
pub struct Mvcc {
id: Id,
guard: Mutex<UniqueId>,
current: Mutex<TxnId>,
txns: TokenHist<TxnId>
}
pub struct Transaction<'a> {
mvcc: &'a Mvcc,
seq: Token<'a,TxnId>,
pending: Guest<'a>,
visible: Mutex<HashSet<ErasedCell<'a>>>,
}
impl Mvcc {
pub fn new()->Self {
let unique_id = UniqueId::default();
Mvcc {
id: *unique_id,
guard: Mutex::new(unique_id),
current: Mutex::new(0),
txns: TokenHist::default()
}
}
pub fn begin(&self)->Transaction<'_> {
let current: TxnId = *self.current.lock();
Transaction {
mvcc: self,
seq: self.txns.make_token(current),
pending: Guest::default(),
visible: Mutex::new(HashSet::new()),
}
}
pub(crate) fn id(&self)->Id {
self.id
}
pub fn update<O>(&self, mut f:impl FnMut(&mut Transaction<'_>)->O)->O {
loop {
let mut txn = self.begin();
let out = f(&mut txn);
if txn.try_commit().is_ok() { return out; }
}
}
}
impl Transaction<'_> {
pub fn try_commit(mut self)->Result<(),Self> {
let mut guard = self.mvcc.guard.lock();
if !self.can_commit() {
return Err(self)
}
let seq = self.mvcc.current.lock().checked_add(1).unwrap();
for ErasedCell(erased) in self.visible.get_mut().drain() {
erased.commit(&mut guard, seq, &mut self.pending);
}
*self.mvcc.current.lock() = seq;
Ok(())
}
pub fn can_commit(&self)->bool {
let visible = self.visible.lock();
for &ErasedCell(ref erased) in &*visible {
if erased.last_changed() > *self.seq {
return false;
}
}
true
}
}