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}