use crate::{runtime, LazyCell};
use core::{
cell::UnsafeCell,
fmt::{self, Debug, Formatter},
};
pub struct LocalCell<T> {
inner: UnsafeCell<T>,
thread_id: usize,
}
impl<T> LocalCell<T> {
pub const fn new_with_threadid(value: T, thread_id: usize) -> Self {
Self { inner: UnsafeCell::new(value), thread_id }
}
pub fn new(value: T) -> Self {
let thread_id = unsafe { runtime::_runtime_threadid_ZhZIZBv4() };
Self::new_with_threadid(value, thread_id)
}
pub fn scope<F, FR>(&self, scope: F) -> FR
where
F: FnOnce(&mut T) -> FR,
{
let is_interrupted = unsafe { runtime::_runtime_isinterrupted_v5tnnoC7() };
assert!(!is_interrupted, "cannot access local cell from an interrupt handler");
let thread_id = unsafe { runtime::_runtime_threadid_ZhZIZBv4() };
assert_eq!(thread_id, self.thread_id, "cannot access local cell from another thread");
unsafe { self.raw(scope) }
}
pub unsafe fn raw<F, FR>(&self, scope: F) -> FR
where
F: FnOnce(&mut T) -> FR,
{
let inner_ptr = self.inner.get();
let value = inner_ptr.as_mut().expect("unexpected NULL pointer inside cell");
scope(value)
}
}
impl<T> LocalCell<LazyCell<T>> {
pub fn lazy_scope<F, FR>(&self, scope: F) -> FR
where
F: FnOnce(&mut T) -> FR,
{
self.scope(|lazy| lazy.scope_mut(scope))
}
}
impl<T> Debug for LocalCell<T>
where
T: Debug,
{
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let thread_id = unsafe { runtime::_runtime_threadid_ZhZIZBv4() };
if thread_id != self.thread_id {
return f.debug_tuple("LocalCell").field(&"<opaque due to different thread>").finish();
}
let is_interrupted = unsafe { runtime::_runtime_isinterrupted_v5tnnoC7() };
if is_interrupted {
return f.debug_tuple("LocalCell").field(&"<opaque due to interrupt context>").finish();
}
self.scope(|value| value.fmt(f))
}
}
unsafe impl<T> Sync for LocalCell<T>
where
T: Send,
{
}