embedded_threadsafe/safecells/
local.rs1use crate::{runtime, LazyCell};
4use core::{
5 cell::UnsafeCell,
6 fmt::{self, Debug, Formatter},
7};
8
9pub struct LocalCell<T> {
14 inner: UnsafeCell<T>,
16 thread_id: usize,
18}
19impl<T> LocalCell<T> {
20 pub const fn new_with_threadid(value: T, thread_id: usize) -> Self {
22 Self { inner: UnsafeCell::new(value), thread_id }
23 }
24
25 pub fn new(value: T) -> Self {
27 let thread_id = unsafe { runtime::_runtime_threadid_ZhZIZBv4() };
29 Self::new_with_threadid(value, thread_id)
30 }
31
32 pub fn scope<F, FR>(&self, scope: F) -> FR
37 where
38 F: FnOnce(&mut T) -> FR,
39 {
40 let is_interrupted = unsafe { runtime::_runtime_isinterrupted_v5tnnoC7() };
42 assert!(!is_interrupted, "cannot access local cell from an interrupt handler");
43
44 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 unsafe { self.raw(scope) }
50 }
51
52 pub unsafe fn raw<F, FR>(&self, scope: F) -> FR
58 where
59 F: FnOnce(&mut T) -> FR,
60 {
61 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 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 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 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 self.scope(|value| value.fmt(f))
98 }
99}
100unsafe impl<T> Sync for LocalCell<T>
101where
102 T: Send,
103{
104 }