use std::cell::RefCell;
use std::collections::VecDeque;
use std::rc::Rc;
use wasm_bindgen::prelude::*;
pub struct MvccQueue {
next_transaction_id: Rc<RefCell<u64>>,
write_queue: Rc<RefCell<VecDeque<PendingWrite>>>,
active_write: Rc<RefCell<Option<u64>>>,
}
struct PendingWrite {
transaction_id: u64,
resolve: js_sys::Function,
}
impl MvccQueue {
pub fn new() -> Self {
Self {
next_transaction_id: Rc::new(RefCell::new(0)),
write_queue: Rc::new(RefCell::new(VecDeque::new())),
active_write: Rc::new(RefCell::new(None)),
}
}
pub fn begin_transaction(&self) -> u64 {
let mut id = self.next_transaction_id.borrow_mut();
*id += 1;
*id
}
pub fn acquire_write_lock(&self) -> js_sys::Promise {
let transaction_id = self.begin_transaction();
let active_write = self.active_write.clone();
let write_queue = self.write_queue.clone();
js_sys::Promise::new(&mut move |resolve, _reject| {
if active_write.borrow().is_none() {
*active_write.borrow_mut() = Some(transaction_id);
log::debug!("MVCC: Write {} acquired lock immediately", transaction_id);
let _ = resolve.call1(&JsValue::NULL, &JsValue::from(transaction_id));
} else {
log::debug!(
"MVCC: Write {} queued (active: {:?})",
transaction_id,
*active_write.borrow()
);
write_queue.borrow_mut().push_back(PendingWrite {
transaction_id,
resolve,
});
}
})
}
pub fn release_write_lock(&self, transaction_id: u64) {
let current = self.active_write.borrow();
if *current != Some(transaction_id) {
log::warn!(
"MVCC: Attempted to release write lock for {} but active is {:?}",
transaction_id,
*current
);
return;
}
drop(current);
*self.active_write.borrow_mut() = None;
log::debug!("MVCC: Write {} released lock", transaction_id);
if let Some(next) = self.write_queue.borrow_mut().pop_front() {
*self.active_write.borrow_mut() = Some(next.transaction_id);
log::debug!("MVCC: Processing queued write {}", next.transaction_id);
let _ = next
.resolve
.call1(&JsValue::NULL, &JsValue::from(next.transaction_id));
}
}
pub fn has_active_write(&self) -> bool {
self.active_write.borrow().is_some()
}
pub fn pending_writes_count(&self) -> usize {
self.write_queue.borrow().len()
}
}
impl Default for MvccQueue {
fn default() -> Self {
Self::new()
}
}