jay-compositor 1.15.0

The Jay compositor
use crate::utils::keep_alive::KeepAlive;
use crate::utils::stack::Stack;
use crate::utils::thread_id::ThreadId;
use isnt::std_1::primitive::IsntConstPtrExt;
use std::cell::Cell;
use std::convert::Infallible;
use std::rc::Rc;

#[cfg(test)]
mod tests;

pub struct ThreadLocalData<T>
where
    T: 'static,
{
    thread_id: ThreadId,
    ptr: Cell<*const T>,
}

thread_local! {
    static HOLDER: Stack<Rc<dyn KeepAlive>> = const { Stack::new() };
}

unsafe impl<T> Send for ThreadLocalData<T> {}
unsafe impl<T> Sync for ThreadLocalData<T> {}

impl<T> ThreadLocalData<T>
where
    T: 'static,
{
    pub fn new(thread_id: ThreadId) -> Self {
        Self {
            thread_id,
            ptr: Default::default(),
        }
    }

    #[cfg_attr(not(test), expect(unused))]
    pub fn get_or_create(&self, init: impl FnOnce() -> Rc<T>) -> Rc<T> {
        self.get_or_try_create::<Infallible>(|| Ok(init())).unwrap()
    }

    pub fn get_or_try_create<E>(
        &self,
        init: impl FnOnce() -> Result<Rc<T>, E>,
    ) -> Result<Rc<T>, E> {
        assert!(self.thread_id.is_current());
        HOLDER.with(|h| {
            let ptr = self.ptr.get();
            if ptr.is_not_null() {
                unsafe {
                    Rc::increment_strong_count(ptr);
                    return Ok(Rc::from_raw(ptr));
                }
            }
            let rc = init()?;
            h.push(rc.clone());
            self.ptr.set(Rc::as_ptr(&rc));
            Ok(rc)
        })
    }
}