embedded_threadsafe/safecells/
local.rs

1//! A fast, thread-local cell
2
3use crate::{runtime, LazyCell};
4use core::{
5    cell::UnsafeCell,
6    fmt::{self, Debug, Formatter},
7};
8
9/// A fast, thread-local cell
10///
11/// # Warning
12/// This cell must not be accessed from another thread or an interrupt context; doing so will raise a panic.
13pub struct LocalCell<T> {
14    /// The wrapped value
15    inner: UnsafeCell<T>,
16    /// The associated thread ID
17    thread_id: usize,
18}
19impl<T> LocalCell<T> {
20    /// Creates a new thread-local cell
21    pub const fn new_with_threadid(value: T, thread_id: usize) -> Self {
22        Self { inner: UnsafeCell::new(value), thread_id }
23    }
24
25    /// Creates a new thread-local cell
26    pub fn new(value: T) -> Self {
27        // Get the thread ID and init self
28        let thread_id = unsafe { runtime::_runtime_threadid_ZhZIZBv4() };
29        Self::new_with_threadid(value, thread_id)
30    }
31
32    /// Provides scoped access to the underlying value
33    ///
34    /// # Panic
35    /// This function will panic if called from another thread or interrupt context
36    pub fn scope<F, FR>(&self, scope: F) -> FR
37    where
38        F: FnOnce(&mut T) -> FR,
39    {
40        // Ensure that we are not in an interrupt handler
41        let is_interrupted = unsafe { runtime::_runtime_isinterrupted_v5tnnoC7() };
42        assert!(!is_interrupted, "cannot access local cell from an interrupt handler");
43
44        // Ensure that we access this from the correct thread
45        let thread_id = unsafe { runtime::_runtime_threadid_ZhZIZBv4() };
46        assert_eq!(thread_id, self.thread_id, "cannot access local cell from another thread");
47
48        // Provide access to the value
49        unsafe { self.raw(scope) }
50    }
51
52    /// Provides an unsafe raw scoped access to the underlying value
53    ///
54    /// # Safety
55    /// This function provides unchecked, mutable access to the underlying value, so incorrect use of this function may
56    /// lead to race conditions or undefined behavior.
57    pub unsafe fn raw<F, FR>(&self, scope: F) -> FR
58    where
59        F: FnOnce(&mut T) -> FR,
60    {
61        // Provide access to the inner value
62        let inner_ptr = self.inner.get();
63        let value = inner_ptr.as_mut().expect("unexpected NULL pointer inside cell");
64        scope(value)
65    }
66}
67impl<T> LocalCell<LazyCell<T>> {
68    /// Provides scoped access to the underlying lazy cell
69    ///
70    /// # Panic
71    /// This function will panic if called from another thread or interrupt context
72    pub fn lazy_scope<F, FR>(&self, scope: F) -> FR
73    where
74        F: FnOnce(&mut T) -> FR,
75    {
76        self.scope(|lazy| lazy.scope_mut(scope))
77    }
78}
79impl<T> Debug for LocalCell<T>
80where
81    T: Debug,
82{
83    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
84        // Return an opaque description if we are in a different thread
85        let thread_id = unsafe { runtime::_runtime_threadid_ZhZIZBv4() };
86        if thread_id != self.thread_id {
87            return f.debug_tuple("LocalCell").field(&"<opaque due to different thread>").finish();
88        }
89
90        // Return an opaque description if we are in an interrupt context
91        let is_interrupted = unsafe { runtime::_runtime_isinterrupted_v5tnnoC7() };
92        if is_interrupted {
93            return f.debug_tuple("LocalCell").field(&"<opaque due to interrupt context>").finish();
94        }
95
96        // Debug the value
97        self.scope(|value| value.fmt(f))
98    }
99}
100unsafe impl<T> Sync for LocalCell<T>
101where
102    T: Send,
103{
104    // Marker trait, no members to implement
105}