embedded_threadsafe/safecells/
interrupt.rs

1//! A fast, thread-local cell that can be safely shared accross interrupt contexts
2
3use crate::{runtime, LazyCell};
4use core::{
5    cell::UnsafeCell,
6    fmt::{self, Debug, Formatter},
7};
8
9/// A fast, thread-local cell that can be safely shared accross interrupt contexts
10///
11/// # Warning
12/// This cell must not be accessed from another thread; doing so will raise a panic.
13pub struct InterruptCell<T> {
14    /// The wrapped value
15    inner: UnsafeCell<T>,
16    /// The associated thread ID
17    thread_id: usize,
18}
19impl<T> InterruptCell<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 access this from the correct thread ID
41        let thread_id = unsafe { runtime::_runtime_threadid_ZhZIZBv4() };
42        assert_eq!(thread_id, self.thread_id, "cannot access local cell from another thread");
43
44        // Create mutable slots to transfer state to/from the closure and create the caller
45        let mut scope = Some(scope);
46        let mut result: Option<FR> = None;
47        let mut call_scope = || {
48            // Consume and call the scope
49            let scope = scope.take().expect("missing scope function");
50            let result_ = unsafe { self.raw(scope) };
51            result = Some(result_);
52        };
53
54        // Run the implementation in a threadsafe context and return the result
55        unsafe { runtime::_runtime_interruptsafe_1l52Ge5e(&mut call_scope) };
56        result.expect("implementation scope did not set result value")
57    }
58
59    /// Provides an unsafe raw scoped access to the underlying value
60    ///
61    /// # Safety
62    /// This function provides unchecked, mutable access to the underlying value, so incorrect use of this function may
63    /// lead to race conditions or undefined behavior.
64    pub unsafe fn raw<F, FR>(&self, scope: F) -> FR
65    where
66        F: FnOnce(&mut T) -> FR,
67    {
68        // Provide access to the inner value
69        let inner_ptr = self.inner.get();
70        let value = inner_ptr.as_mut().expect("unexpected NULL pointer inside cell");
71        scope(value)
72    }
73}
74impl<T> InterruptCell<LazyCell<T>> {
75    /// Provides scoped access to the underlying lazy cell
76    ///
77    /// # Panic
78    /// This function will panic if called from another thread or interrupt context
79    pub fn lazy_scope<F, FR>(&self, scope: F) -> FR
80    where
81        F: FnOnce(&mut T) -> FR,
82    {
83        self.scope(|lazy| lazy.scope_mut(scope))
84    }
85}
86impl<T> Debug for InterruptCell<T>
87where
88    T: Debug,
89{
90    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
91        // Return an opaque description if we are in a different thread context
92        let thread_id = unsafe { runtime::_runtime_threadid_ZhZIZBv4() };
93        if thread_id != self.thread_id {
94            return f.debug_tuple("InterruptCell").field(&"<opaque due to different thread>").finish();
95        }
96
97        // Debug the value
98        self.scope(|value| value.fmt(f))
99    }
100}
101unsafe impl<T> Sync for InterruptCell<T>
102where
103    T: Send,
104{
105    // Marker trait, no members to implement
106}