pax-runtime-api 0.39.1

Userland constructs used at the runtime API boundary of Pax Engine
Documentation
use super::{
    private::PropertyId,
    properties_table::{PropertyType, PROPERTY_TABLE},
    PropertyValue,
};

/// Reactive untyped property. Shallow clones can be cheaply made. Manages
/// refcounting and deletion of underlying data when all instances with a
/// specific PropertyId has been dropped.
///
/// Untyped graph handles retain their owner-thread restriction:
///
/// ```compile_fail
/// use pax_runtime_api::LocalProperty;
/// let value = LocalProperty::new(1_u32).untyped();
/// std::thread::spawn(move || drop(value));
/// ```
///
/// A lock cannot make a thread-local graph handle transferable:
///
/// ```compile_fail
/// use pax_runtime_api::LocalProperty;
/// use std::sync::{Arc, Mutex};
/// let value = Arc::new(Mutex::new(LocalProperty::new(1_u32)));
/// std::thread::spawn(move || value.lock().unwrap().get());
/// ```
#[derive(Debug)]
pub struct UntypedProperty {
    // Slotmap id for the backing property table entry.
    pub(crate) id: PropertyId,
    // The key is meaningful only in the creating thread's property table.
    owner: std::marker::PhantomData<std::rc::Rc<()>>,
}

impl Clone for UntypedProperty {
    fn clone(&self) -> Self {
        PROPERTY_TABLE.with(|t| {
            t.increase_ref_count(self.id);
        });
        UntypedProperty {
            id: self.id,
            owner: std::marker::PhantomData,
        }
    }
}

impl Drop for UntypedProperty {
    fn drop(&mut self) {
        // Process shutdown can drop properties while the thread-local property table
        // itself is being destroyed. In that phase, bookkeeping is no longer useful
        // and re-entering the TLS would panic.
        let _ = PROPERTY_TABLE.try_with(|t| {
            let ref_count = t.decrease_ref_count(self.id);
            if ref_count == 0 {
                t.remove_entry(self.id);
            }
        });
    }
}

impl UntypedProperty {
    pub(super) fn try_from_id(id: PropertyId) -> Option<Self> {
        PROPERTY_TABLE.with(|table| {
            if !table.has_live_entry(id) {
                return None;
            }
            table.increase_ref_count(id);
            Some(Self {
                id,
                owner: std::marker::PhantomData,
            })
        })
    }
    // Allocates a new table entry and returns its untyped handle.
    pub(crate) fn new<T: PropertyValue>(
        val: T,
        inbound: Vec<PropertyId>,
        data: PropertyType<T>,
        debug_name: Option<&str>,
    ) -> Self {
        UntypedProperty {
            id: PROPERTY_TABLE.with(|t| t.add_entry(val, inbound, data, debug_name)),
            owner: std::marker::PhantomData,
        }
    }

    // Returns the table id backing this untyped property handle.
    pub fn get_id(&self) -> PropertyId {
        self.id
    }

    // Inspect storage as well as the adapter's declared type before rewrapping.
    pub(crate) fn has_value_type<T: PropertyValue>(&self) -> bool {
        PROPERTY_TABLE.with(|table| table.has_value_type::<T>(self.id))
    }
}