icydb_core/db/commit/
guard.rs1use crate::error::{ErrorClass, ErrorOrigin, InternalError};
2use std::panic::{AssertUnwindSafe, catch_unwind};
3
4pub struct CommitApplyGuard {
21 phase: &'static str,
22 finished: bool,
23 rollbacks: Vec<Box<dyn FnOnce()>>,
24}
25
26impl CommitApplyGuard {
27 pub(crate) const fn new(phase: &'static str) -> Self {
28 Self {
29 phase,
30 finished: false,
31 rollbacks: Vec::new(),
32 }
33 }
34
35 pub(crate) fn record_rollback(&mut self, rollback: impl FnOnce() + 'static) {
36 self.rollbacks.push(Box::new(rollback));
37 }
38
39 pub(crate) fn finish(mut self) -> Result<(), InternalError> {
40 if self.finished {
41 return Err(InternalError::new(
42 ErrorClass::InvariantViolation,
43 ErrorOrigin::Executor,
44 format!(
45 "commit apply guard invariant violated: finish called twice ({})",
46 self.phase
47 ),
48 ));
49 }
50
51 self.finished = true;
52 self.rollbacks.clear();
53 Ok(())
54 }
55
56 fn rollback_best_effort(&mut self) {
57 if self.finished {
58 return;
60 }
61
62 while let Some(rollback) = self.rollbacks.pop() {
66 let _ = catch_unwind(AssertUnwindSafe(rollback));
67 }
68 }
69}
70
71impl Drop for CommitApplyGuard {
72 fn drop(&mut self) {
73 if !self.finished {
74 self.rollback_best_effort();
75 }
76 }
77}