Skip to main content

sim_lib_expr_tree/
handle.rs

1use std::{
2    collections::BTreeMap,
3    sync::{
4        Arc, Mutex,
5        atomic::{AtomicU64, Ordering},
6    },
7};
8
9use sim_kernel::{Cx, HandleSeed, Object, ObjectCompat, Result};
10
11use crate::runtime::TreeState;
12
13const MAX_STORAGE_NAME_BYTES: usize = 256;
14
15// sim-non-citizen(reason = "live writer scope and backend authority", kind = "handle", descriptor = "")
16/// Opaque live expression-tree handle.
17///
18/// Handles are cloneable references to one writer scope. Their default object
19/// expression is `core/opaque-object`; they never expose Citizen reconstruction.
20#[derive(Clone)]
21pub struct TreeHandle {
22    pub(crate) state: Arc<Mutex<TreeState>>,
23}
24
25impl TreeHandle {
26    fn new(state: Arc<Mutex<TreeState>>) -> Self {
27        Self { state }
28    }
29}
30
31impl Object for TreeHandle {
32    fn display(&self, _cx: &mut Cx) -> Result<String> {
33        let state = self
34            .state
35            .lock()
36            .map_err(|_| sim_kernel::Error::Eval("expression-tree state poisoned".to_owned()))?;
37        Ok(format!("#<expr-tree {}>", state.storage_name()))
38    }
39
40    fn as_any(&self) -> &dyn std::any::Any {
41        self
42    }
43}
44
45impl ObjectCompat for TreeHandle {}
46
47pub(crate) struct TreeRuntime {
48    stores: Mutex<BTreeMap<String, Arc<Mutex<TreeState>>>>,
49    next_handle_seed: Arc<AtomicU64>,
50}
51
52impl TreeRuntime {
53    pub(crate) fn new(first_handle_seed: HandleSeed) -> Self {
54        Self {
55            stores: Mutex::new(BTreeMap::new()),
56            next_handle_seed: Arc::new(AtomicU64::new(first_handle_seed.0)),
57        }
58    }
59
60    pub(crate) fn open(
61        &self,
62        cx: &Cx,
63        storage_name: &str,
64    ) -> std::result::Result<TreeHandle, String> {
65        if storage_name.is_empty() || storage_name.len() > MAX_STORAGE_NAME_BYTES {
66            return Err(format!(
67                "storage name must contain 1..={MAX_STORAGE_NAME_BYTES} bytes"
68            ));
69        }
70        let mut stores = self
71            .stores
72            .lock()
73            .map_err(|_| "expression-tree storage registry poisoned".to_owned())?;
74        let state = match stores.get(storage_name) {
75            Some(state) => Arc::clone(state),
76            None => {
77                let state = Arc::new(Mutex::new(TreeState::new(
78                    cx,
79                    storage_name.to_owned(),
80                    Arc::clone(&self.next_handle_seed),
81                )?));
82                stores.insert(storage_name.to_owned(), Arc::clone(&state));
83                state
84            }
85        };
86        Ok(TreeHandle::new(state))
87    }
88}
89
90pub(crate) fn next_handle_seed(sequence: &AtomicU64) -> HandleSeed {
91    let seed = sequence
92        .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |seed| {
93            seed.checked_add(1)
94        })
95        .expect("expression-tree handle seed space exhausted");
96    HandleSeed::new(seed)
97}