Skip to main content

wide_log/
context.rs

1use std::cell::Cell;
2
3/// A thread-local cell storing a raw pointer to the current wide event.
4///
5/// This type provides safe methods for setting, getting, and saving/restoring
6/// the pointer, which is used by the generated guard to manage nested scopes.
7///
8/// # Safety Invariants
9///
10/// The raw pointer stored in this cell is sound because:
11///
12/// 1. **The guard owns the event.** The `WideEvent<K>` is a field of the guard,
13///    which is on the stack (or in the `scope()` future's state). The guard
14///    outlives any code that calls `current()` within the same scope.
15/// 2. **Set on creation, cleared on drop.** The guard's `new` sets the pointer;
16///    its `Drop` restores the previous pointer (or null) before the inner
17///    `ScopedGuard::Drop` runs.
18/// 3. **Nested scopes** save/restore the previous pointer via `replace` and the
19///    `prev_ptr` field on the guard.
20/// 4. **No cross-thread access.** `thread_local!` means each thread has its own
21///    cell. For async, `tokio::task_local!` moves with the task across threads.
22///
23/// The `&'static` lifetime returned by accessors is a "valid for the duration
24/// of the guard" lifetime — the standard pattern used by `thread_local`
25/// accessors (e.g., `tracing`'s `Span::current()`).
26pub struct ContextCell<T> {
27    ptr: Cell<*mut T>,
28}
29
30impl<T> ContextCell<T> {
31    /// Create a new cell initialized to a null pointer.
32    #[inline]
33    pub const fn new() -> Self {
34        Self {
35            ptr: Cell::new(std::ptr::null_mut()),
36        }
37    }
38
39    /// Returns the stored pointer, or `None` if null.
40    #[inline]
41    pub fn get(&self) -> Option<*mut T> {
42        let ptr = self.ptr.get();
43        if ptr.is_null() { None } else { Some(ptr) }
44    }
45
46    /// Returns the stored raw pointer (null if unset).
47    #[inline]
48    pub fn get_ptr(&self) -> *mut T {
49        self.ptr.get()
50    }
51
52    /// Sets the stored pointer. Returns the previous pointer.
53    #[inline]
54    pub fn replace(&self, ptr: *mut T) -> *mut T {
55        let prev = self.ptr.get();
56        self.ptr.set(ptr);
57        prev
58    }
59
60    /// Clears the stored pointer to null. Returns the previous pointer.
61    #[inline]
62    pub fn clear(&self) -> *mut T {
63        self.replace(std::ptr::null_mut())
64    }
65
66    /// Restores a previously saved pointer (typically from `replace` or `clear`).
67    #[inline]
68    pub fn restore(&self, ptr: *mut T) {
69        self.ptr.set(ptr);
70    }
71
72    /// Returns a `&'static mut T` reference to the event if the pointer is non-null.
73    ///
74    /// # Safety
75    ///
76    /// The caller must ensure the pointer is valid and that the referenced `T`
77    /// will not be accessed mutably from another reference for the duration of
78    /// the returned borrow. In practice, this is guaranteed by the guard's
79    /// lifetime — the guard outlives all `current()` calls within its scope.
80    #[inline]
81    pub unsafe fn deref_mut(&self) -> Option<&'static mut T> {
82        let ptr = self.ptr.get();
83        if ptr.is_null() {
84            None
85        } else {
86            Some(unsafe { &mut *ptr })
87        }
88    }
89}
90
91impl<T> Default for ContextCell<T> {
92    fn default() -> Self {
93        Self::new()
94    }
95}
96
97// SAFETY: The raw pointer inside `ContextCell` is only accessed via the
98// thread-local or task-local storage. For thread-local access, each thread
99// has its own cell, so there is no cross-thread sharing. For task-local
100// access, the task moves as a unit across threads — the pointer is only
101// dereferenced on the thread currently executing the task. The pointer is
102// never accessed concurrently from multiple threads.
103unsafe impl<T> Send for ContextCell<T> {}
104unsafe impl<T> Sync for ContextCell<T> {}
105
106#[cfg(test)]
107mod tests {
108    use super::*;
109
110    #[test]
111    fn new_is_null() {
112        let cell = ContextCell::<u32>::new();
113        assert!(cell.get().is_none());
114    }
115
116    #[test]
117    fn replace_returns_previous() {
118        let cell = ContextCell::<u32>::new();
119        let mut a: u32 = 1;
120        let mut b: u32 = 2;
121        let ptr_a = &mut a as *mut u32;
122        let ptr_b = &mut b as *mut u32;
123
124        let prev = cell.replace(ptr_a);
125        assert!(prev.is_null());
126        assert_eq!(cell.get(), Some(ptr_a));
127
128        let prev = cell.replace(ptr_b);
129        assert_eq!(prev, ptr_a);
130        assert_eq!(cell.get(), Some(ptr_b));
131    }
132
133    #[test]
134    fn clear_returns_previous() {
135        let cell = ContextCell::<u32>::new();
136        let mut a: u32 = 42;
137        let ptr_a = &mut a as *mut u32;
138
139        cell.replace(ptr_a);
140        let prev = cell.clear();
141        assert_eq!(prev, ptr_a);
142        assert!(cell.get().is_none());
143    }
144
145    #[test]
146    fn restore_sets_pointer() {
147        let cell = ContextCell::<u32>::new();
148        let mut a: u32 = 10;
149        let ptr_a = &mut a as *mut u32;
150
151        cell.restore(ptr_a);
152        assert_eq!(cell.get(), Some(ptr_a));
153    }
154
155    #[test]
156    fn deref_mut_returns_reference() {
157        let cell = ContextCell::<u32>::new();
158        let mut a: u32 = 99;
159        let ptr_a = &mut a as *mut u32;
160        cell.replace(ptr_a);
161
162        let r = unsafe { cell.deref_mut() }.unwrap();
163        assert_eq!(*r, 99);
164        *r = 100;
165        assert_eq!(a, 100);
166    }
167
168    #[test]
169    fn deref_mut_none_when_null() {
170        let cell = ContextCell::<u32>::new();
171        assert!(unsafe { cell.deref_mut() }.is_none());
172    }
173
174    #[test]
175    fn default_is_null() {
176        let cell = ContextCell::<u32>::default();
177        assert!(cell.get().is_none());
178    }
179}