Skip to main content

evm_fork_cache/
cancellation.rs

1//! Cooperative cancellation for a logical scope of EVM simulations.
2//!
3//! The signal itself is provider-free and is observed at EVM instruction
4//! boundaries. It cannot interrupt a database callback or precompile that is
5//! already executing; callers that require a wholly provider-free cancellation
6//! path must construct their overlays without an external database.
7
8use std::sync::{
9    Arc,
10    atomic::{AtomicU8, Ordering},
11};
12
13const STARTED: u8 = 1;
14const CANCELLED: u8 = 1 << 1;
15
16/// A cloneable cancellation signal for one logical simulation scope.
17///
18/// Clones share one atomic state. The executing inspector marks the token as
19/// started at its first instruction boundary, while an ingress owner may call
20/// [`cancel`](Self::cancel) from another thread. One scope may contain several
21/// related overlay calls, including multi-chain or access-list replay calls;
22/// every clone and call observes the same cancellation decision. Cancellation
23/// is monotonic: a token cannot be reset and must not be carried into a later,
24/// independent simulation scope.
25#[derive(Clone, Debug, Default)]
26pub struct SimulationCancellationToken {
27    state: Arc<AtomicU8>,
28}
29
30impl SimulationCancellationToken {
31    /// Create a fresh, unstarted and uncancelled simulation scope.
32    pub fn new() -> Self {
33        Self::default()
34    }
35
36    /// Request cancellation.
37    ///
38    /// This operation is idempotent and safe before, during, or after EVM
39    /// execution. A running cancellable overlay observes it at an instruction
40    /// boundary.
41    pub fn cancel(&self) {
42        self.state.fetch_or(CANCELLED, Ordering::AcqRel);
43    }
44
45    /// Whether cancellation has been requested.
46    pub fn is_cancelled(&self) -> bool {
47        self.state.load(Ordering::Acquire) & CANCELLED != 0
48    }
49
50    /// Whether any cancellable EVM in this scope reached its first instruction
51    /// boundary.
52    pub fn has_started(&self) -> bool {
53        self.state.load(Ordering::Acquire) & STARTED != 0
54    }
55
56    pub(crate) fn mark_started(&self) {
57        self.state.fetch_or(STARTED, Ordering::Release);
58    }
59}