use crate::queue::state;
use std::cell::RefCell;
struct Savepoint {
pending: state::Mark,
locks: usize,
frames: usize,
suspension: crate::suspend::Suspension,
}
thread_local! {
static SAVEPOINTS: RefCell<Vec<Savepoint>> = const { RefCell::new(Vec::new()) };
}
pub fn start() {
let savepoint = Savepoint {
pending: state::mark(),
locks: crate::concurrency::mark(),
frames: crate::executor::depth(),
suspension: crate::suspend::snapshot(),
};
SAVEPOINTS.with_borrow_mut(|s| s.push(savepoint));
}
pub fn commit() {
if let Some(savepoint) = SAVEPOINTS.with_borrow_mut(Vec::pop) {
state::release(savepoint.pending);
}
}
pub fn abort() {
if let Some(savepoint) = SAVEPOINTS.with_borrow_mut(Vec::pop) {
state::rollback(savepoint.pending);
crate::concurrency::rollback(savepoint.locks);
crate::executor::truncate_to(savepoint.frames);
crate::suspend::restore(savepoint.suspension);
}
}
pub fn clear() {
SAVEPOINTS.with_borrow_mut(Vec::clear);
}
#[cfg(test)]
pub fn depth() -> usize {
SAVEPOINTS.with_borrow(Vec::len)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::queue::RefreshKey;
#[test]
fn depth_follows_start_commit_and_abort() {
clear();
state::clear();
start();
start();
abort();
assert_eq!(depth(), 1);
start();
commit();
commit();
assert_eq!(depth(), 0);
}
#[test]
fn abort_restores_queue_and_suspension_together() {
clear();
state::clear();
crate::suspend::force_resume();
state::queue_insert(RefreshKey::pk("user", 1));
start();
crate::suspend::suspend();
state::queue_insert(RefreshKey::pk("user", 2));
abort();
assert!(!crate::suspend::is_suspended());
assert_eq!(state::get_queue_contents(), vec![RefreshKey::pk("user", 1)]);
state::clear();
}
}