Skip to main content

omega_cache/core/
request_quota.rs

1/// `RequestQuota` provides a short-lived, ephemeral budget for a single cache operation.
2///
3/// While [`ThreadContext`] manages the hardware-level timing (how long to wait),
4/// `RequestQuota` manages the logical retries (how many times to try). It acts as
5/// a safety valve to prevent "Livelock"—a scenario where a thread enters an
6/// infinite retry loop because a high-contention slot never becomes available.
7///
8/// ### Characteristics
9/// * **Stack-Allocated**: Designed to be created on the stack at the start of an operation.
10/// * **Single-Use**: Typically discarded once the `push` or `pop` operation completes.
11/// * **Deterministic**: Ensures that an operation will eventually return (either with
12///   success or a "Busy" error) within a bounded number of attempts.
13pub struct RequestQuota {
14    remaining: usize,
15}
16
17impl RequestQuota {
18    /// Creates a new quota with the specified number of allowed retries.
19    ///
20    /// # Parameters
21    /// * `remaining`: The maximum number of failed atomic attempts to tolerate
22    ///   before aborting the request.
23    #[inline(always)]
24    pub fn new(remaining: usize) -> Self {
25        Self { remaining }
26    }
27
28    /// Attempts to consume one unit of the retry budget.
29    ///
30    /// This should be called every time a contention-sensitive operation (like
31    /// a Compare-And-Swap) fails, but *before* performing a backoff yield.
32    ///
33    /// # Returns
34    /// * `true`: Budget is available; the operation should retry.
35    /// * `false`: Budget is exhausted; the operation should abort to prevent
36    ///   stalling the system.
37    #[inline(always)]
38    pub fn consume(&mut self) -> bool {
39        if self.remaining == 0 {
40            return false;
41        }
42
43        self.remaining -= 1;
44        true
45    }
46
47    #[inline(always)]
48    pub fn has_attempts(&self) -> bool {
49        self.remaining > 0
50    }
51}
52
53impl Default for RequestQuota {
54    fn default() -> Self {
55        const DEFAULT_ATTEMPTS: usize = 64;
56        Self {
57            remaining: DEFAULT_ATTEMPTS,
58        }
59    }
60}