Skip to main content

byteflow/scheduler/
registry.rs

1//! Named registry: `name → CapId` (address), never `name → FlowId`.
2//!
3//! Host `whereis` returns the **stored** Cap (the token passed to
4//! `register_name`). Bytecode `Whereis` remints a SEND Cap for the
5//! *caller* (`CapTable::mint_or_reuse`) so knowing a name is not an
6//! ambient grant of the registered token.
7//!
8//! Entries are swept when the **target** flow exits ([`Registry::unregister_flow`]).
9//! A revoked Cap cannot be registered; lookup after exit returns `None`.
10
11use std::collections::HashMap;
12
13use crate::bytecode::CapId;
14use super::error::{LifecycleError, RuntimeError};
15use super::process::FlowId;
16use super::sync_lock;
17
18/// Registry key. Interned as `Box<str>` so lookups do not allocate a `String`
19/// on the happy path when the caller already has a `&str`.
20#[derive(Debug, Clone, PartialEq, Eq, Hash)]
21pub struct RegistryName(Box<str>);
22
23impl RegistryName {
24    pub fn new(name: impl Into<Box<str>>) -> Self {
25        Self(name.into())
26    }
27
28    pub fn as_str(&self) -> &str {
29        &self.0
30    }
31}
32
33impl From<&str> for RegistryName {
34    fn from(name: &str) -> Self {
35        Self(name.into())
36    }
37}
38
39struct Entry {
40    cap: CapId,
41    flow: FlowId,
42}
43
44pub struct Registry {
45    by_name: HashMap<RegistryName, Entry>,
46    by_flow: HashMap<FlowId, Vec<RegistryName>>,
47}
48
49impl Registry {
50    pub fn new() -> Self {
51        Self {
52            by_name: HashMap::new(),
53            by_flow: HashMap::new(),
54        }
55    }
56
57    pub fn register(
58        &mut self,
59        name: RegistryName,
60        cap: CapId,
61        flow: FlowId,
62    ) -> Result<(), LifecycleError> {
63        if name.as_str().is_empty() {
64            return Err(LifecycleError::EmptyName);
65        }
66        if self.by_name.contains_key(&name) {
67            return Err(LifecycleError::AlreadyRegistered);
68        }
69        self.by_flow
70            .entry(flow)
71            .or_default()
72            .push(name.clone());
73        self.by_name.insert(name, Entry { cap, flow });
74        Ok(())
75    }
76
77    pub fn whereis(&self, name: &str) -> Option<CapId> {
78        self.by_name.get(&RegistryName::from(name)).map(|e| e.cap)
79    }
80
81    pub fn target(&self, name: &str) -> Option<FlowId> {
82        self.by_name.get(&RegistryName::from(name)).map(|e| e.flow)
83    }
84
85    pub fn unregister(&mut self, name: &str) -> bool {
86        let key = RegistryName::from(name);
87        match self.by_name.remove(&key) {
88            Some(entry) => {
89                if let Some(names) = self.by_flow.get_mut(&entry.flow) {
90                    names.retain(|n| n != &key);
91                    if names.is_empty() {
92                        self.by_flow.remove(&entry.flow);
93                    }
94                }
95                true
96            }
97            None => false,
98        }
99    }
100
101    /// Drop every name that pointed at `flow` (called from finalize).
102    pub fn unregister_flow(&mut self, flow: FlowId) {
103        if let Some(names) = self.by_flow.remove(&flow) {
104            for name in names {
105                self.by_name.remove(&name);
106            }
107        }
108    }
109}
110
111impl Default for Registry {
112    fn default() -> Self {
113        Self::new()
114    }
115}
116
117pub struct RegistryStore {
118    inner: std::sync::Mutex<Registry>,
119}
120
121impl RegistryStore {
122    pub fn new() -> Self {
123        Self {
124            inner: std::sync::Mutex::new(Registry::new()),
125        }
126    }
127
128    pub fn register(
129        &self,
130        name: RegistryName,
131        cap: CapId,
132        flow: FlowId,
133    ) -> Result<Result<(), LifecycleError>, RuntimeError> {
134        Ok(sync_lock::lock(&self.inner, "RegistryStore::register")?.register(name, cap, flow))
135    }
136
137    pub fn whereis(&self, name: &str) -> Result<Option<CapId>, RuntimeError> {
138        Ok(sync_lock::lock(&self.inner, "RegistryStore::whereis")?.whereis(name))
139    }
140
141    pub fn target(&self, name: &str) -> Result<Option<FlowId>, RuntimeError> {
142        Ok(sync_lock::lock(&self.inner, "RegistryStore::target")?.target(name))
143    }
144
145    pub fn unregister(&self, name: &str) -> Result<bool, RuntimeError> {
146        Ok(sync_lock::lock(&self.inner, "RegistryStore::unregister")?.unregister(name))
147    }
148
149    pub fn unregister_flow(&self, flow: FlowId) -> Result<(), RuntimeError> {
150        sync_lock::lock(&self.inner, "RegistryStore::unregister_flow")?.unregister_flow(flow);
151        Ok(())
152    }
153}
154
155impl Default for RegistryStore {
156    fn default() -> Self {
157        Self::new()
158    }
159}
160
161#[cfg(test)]
162mod tests {
163    use super::*;
164    use crate::bytecode::CapId;
165    use crate::scheduler::process::next_flow_id;
166
167    #[test]
168    fn unregister_flow_clears_names() {
169        let mut reg = Registry::new();
170        let flow = next_flow_id();
171        let cap = CapId::from_raw(42);
172        assert!(reg.register(RegistryName::from("svc"), cap, flow).is_ok());
173        assert_eq!(reg.whereis("svc"), Some(cap));
174        reg.unregister_flow(flow);
175        assert_eq!(reg.whereis("svc"), None);
176    }
177
178    #[test]
179    fn duplicate_name_is_rejected() {
180        let mut reg = Registry::new();
181        let flow = next_flow_id();
182        assert!(reg
183            .register(RegistryName::from("svc"), CapId::from_raw(1), flow)
184            .is_ok());
185        assert_eq!(
186            reg.register(RegistryName::from("svc"), CapId::from_raw(2), flow),
187            Err(LifecycleError::AlreadyRegistered)
188        );
189    }
190
191    #[test]
192    fn empty_name_is_rejected() {
193        let mut reg = Registry::new();
194        let flow = next_flow_id();
195        assert_eq!(
196            reg.register(RegistryName::from(""), CapId::from_raw(1), flow),
197            Err(LifecycleError::EmptyName)
198        );
199    }
200}