Skip to main content

omega_cache/core/
thread_context.rs

1use crate::core::backoff::Backoff;
2use std::cell::UnsafeCell;
3
4/// `ThreadContext` maintains the long-lived, persistent contention state of a
5/// specific thread as it interacts with a specific cache instance.
6///
7/// Unlike a request-scoped budget (quota), `ThreadContext` acts as the "Thread Memory."
8/// It tracks historical contention via [`Backoff`], allowing the thread to adapt its
9/// timing based on how "hot" the cache has been in recent operations.
10///
11/// ### Safety and Thread Locality
12/// This struct is explicitly **!Send** and **!Sync**.
13///
14/// It uses [`UnsafeCell`] to provide interior mutability with zero runtime overhead.
15/// This is only safe when the context is stored in a way that guarantees exclusive
16/// access by a single thread (e.g., inside a `ThreadLocal` ). The non-thread-safe
17/// markers ensure this state cannot accidentally leak or be shared across thread boundaries.
18pub struct ThreadContext {
19    backoff: UnsafeCell<Backoff>,
20}
21
22impl ThreadContext {
23    /// Creates a new `ThreadContext` with the provided backoff policy.
24    ///
25    /// Usually initialized lazily when a thread first interacts with a cache instance.
26    #[inline(always)]
27    pub fn new(backoff: Backoff) -> Self {
28        Self {
29            backoff: UnsafeCell::new(backoff),
30        }
31    }
32
33    /// Signals hardware-level contention and triggers a thread yield.
34    ///
35    /// This should be called when an atomic operation (like a CAS) fails.
36    /// It increases the internal frustration level and performs a hardware-friendly
37    /// stall (e.g., `spin_loop` or `yield`) based on the persistent backoff state.
38    #[inline(always)]
39    pub fn wait(&self) {
40        unsafe { &mut *self.backoff.get() }.wait();
41    }
42
43    /// Signals a successful operation, allowing the thread-local "heat" to dissipate.
44    ///
45    /// This should be called after a successful operation. It decays the
46    /// persistent frustration level, ensuring that subsequent calls from
47    /// this thread are not unnecessarily throttled.
48    #[inline(always)]
49    pub fn decay(&self) {
50        unsafe { &mut *self.backoff.get() }.wait();
51    }
52}
53
54impl Default for ThreadContext {
55    #[inline(always)]
56    fn default() -> Self {
57        const DEFAULT_LIMIT: usize = 32;
58
59        Self {
60            backoff: UnsafeCell::new(Backoff::linear(DEFAULT_LIMIT)),
61        }
62    }
63}