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}