Skip to main content

selium_kernel/
registry.rs

1use futures_util::future::BoxFuture;
2use sharded_slab::Slab;
3use std::{
4    any::{Any, TypeId},
5    collections::HashMap,
6    marker::PhantomData,
7    sync::{Arc, Mutex},
8    task::Waker,
9};
10use thiserror::Error;
11use tracing::{
12    Instrument, Span, debug,
13    field::{self, Empty},
14};
15
16use crate::{
17    KernelError,
18    drivers::Capability,
19    futures::FutureSharedState,
20    guest_data::GuestResult,
21    mailbox::GuestMailbox,
22    session::{Session, SessionError},
23};
24use selium_abi::{DependencyId, GuestResourceId};
25use wasmtime::{StoreLimits, StoreLimitsBuilder};
26
27/// Stable registry identifier for stored resources.
28pub type ResourceId = usize;
29type GuestFuture = Arc<FutureSharedState<GuestResult<Vec<u8>>>>;
30
31/// High-level classification of a resource stored in the registry.
32#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33pub enum ResourceType {
34    /// Guest process resource.
35    Process,
36    /// Host-side instance state.
37    Instance,
38    /// Channel resource.
39    Channel,
40    /// Reader handle resource.
41    Reader,
42    /// Writer handle resource.
43    Writer,
44    /// Session resource.
45    Session,
46    /// Network configuration or handle resource.
47    Network,
48    /// Guest-visible future state resource.
49    Future,
50    /// Uncategorised resource.
51    Other,
52}
53
54/// Metadata describing a registered resource.
55#[derive(Debug, Clone, Copy, PartialEq, Eq)]
56pub struct ResourceMetadata {
57    /// Resource identifier for this entry.
58    pub id: ResourceId,
59    /// Owner resource identifier, if recorded.
60    pub owner: Option<ResourceId>,
61    /// Resource kind classification.
62    pub kind: ResourceType,
63}
64
65/// Typed handle to a resource stored in the [`Registry`].
66#[derive(Clone)]
67pub struct ResourceHandle<T>(ResourceId, PhantomData<T>);
68
69struct Resource {
70    data: Arc<Mutex<Option<Box<dyn Any + Send>>>>,
71    kind: ResourceType,
72    /// Tracing span for resource attribution.
73    span: Span,
74}
75
76struct InstanceState {
77    process_id: Option<ResourceId>,
78    mailbox: Option<&'static GuestMailbox>,
79    extensions: HashMap<TypeId, Arc<dyn Any + Send + Sync>>,
80    limits: StoreLimits,
81}
82
83#[derive(Default)]
84struct HandleTable {
85    entries: Vec<Option<ResourceId>>,
86    free: Vec<usize>,
87}
88
89struct HandleIndex {
90    shared: HandleTable,
91    shared_reverse: HashMap<ResourceId, usize>,
92    instances: HashMap<ResourceId, HandleTable>,
93    futures: HashMap<ResourceId, HandleTable>,
94}
95
96#[derive(Default)]
97struct RelationIndex {
98    owner_of: HashMap<ResourceId, ResourceId>,
99    owned_by: HashMap<ResourceId, Vec<ResourceId>>,
100    parent_of: HashMap<ResourceId, ResourceId>,
101    children_of: HashMap<ResourceId, Vec<ResourceId>>,
102    instance_to_process: HashMap<ResourceId, ResourceId>,
103    process_to_instance: HashMap<ResourceId, ResourceId>,
104    process_log_channel: HashMap<ResourceId, ResourceId>,
105    log_channel_process: HashMap<ResourceId, ResourceId>,
106    singletons: HashMap<DependencyId, ResourceId>,
107    singleton_ids: HashMap<ResourceId, DependencyId>,
108}
109
110/// Registry of guest resources.
111pub struct Registry {
112    resources: Slab<Resource>,
113    relations: Mutex<RelationIndex>,
114    handles: Mutex<HandleIndex>,
115}
116
117/// Registry view tied to a specific guest instance.
118pub struct InstanceRegistry {
119    /// Pointer to global registry
120    registry: Arc<Registry>,
121    /// Instance state resource identifier.
122    instance_id: ResourceId,
123}
124
125/// Cloneable view for registering instance-scoped resources from async contexts.
126#[derive(Clone)]
127pub struct InstanceRegistrar {
128    registry: Arc<Registry>,
129    instance_id: ResourceId,
130}
131
132/// Errors surfaced when interacting with the registry.
133#[derive(Debug, Error)]
134pub enum RegistryError {
135    /// Registry has reached capacity and cannot accept more entries.
136    #[error("registry capacity exhausted")]
137    CapacityExhausted,
138    /// Registry state is unavailable because an internal lock is poisoned.
139    #[error("registry lock poisoned")]
140    LockPoisoned,
141    /// Attempted to initialise or access an invalid reservation.
142    #[error("invalid resource reservation")]
143    InvalidReservation,
144    /// Instance state is missing from the registry.
145    #[error("instance state missing")]
146    MissingInstance,
147}
148
149/// Stable identity associated with a running process instance.
150#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
151pub struct ProcessIdentity(ResourceId);
152
153impl InstanceState {
154    fn new() -> Self {
155        Self {
156            process_id: None,
157            mailbox: None,
158            extensions: HashMap::new(),
159            limits: StoreLimits::default(),
160        }
161    }
162}
163
164impl HandleTable {
165    fn allocate(&mut self, resource_id: ResourceId) -> usize {
166        if let Some(slot) = self.free.pop()
167            && let Some(entry) = self.entries.get_mut(slot)
168        {
169            *entry = Some(resource_id);
170            return slot;
171        }
172
173        self.entries.push(Some(resource_id));
174        self.entries.len() - 1
175    }
176
177    fn resolve(&self, handle: usize) -> Option<ResourceId> {
178        self.entries.get(handle).and_then(|entry| *entry)
179    }
180
181    fn remove(&mut self, handle: usize) -> Option<ResourceId> {
182        let entry = self.entries.get_mut(handle)?;
183        let resource_id = entry.take();
184        if resource_id.is_some() {
185            self.free.push(handle);
186        }
187        resource_id
188    }
189}
190
191impl HandleIndex {
192    fn new() -> Self {
193        Self {
194            shared: HandleTable::default(),
195            shared_reverse: HashMap::new(),
196            instances: HashMap::new(),
197            futures: HashMap::new(),
198        }
199    }
200
201    fn share_handle(&mut self, id: ResourceId) -> Result<GuestResourceId, RegistryError> {
202        if let Some(existing) = self.shared_reverse.get(&id).copied() {
203            return GuestResourceId::try_from(existing)
204                .map_err(|_| RegistryError::CapacityExhausted);
205        }
206
207        let handle = self.shared.allocate(id);
208        match GuestResourceId::try_from(handle) {
209            Ok(guest) => {
210                self.shared_reverse.insert(id, handle);
211                Ok(guest)
212            }
213            Err(_) => {
214                self.shared.remove(handle);
215                Err(RegistryError::CapacityExhausted)
216            }
217        }
218    }
219
220    fn resolve_shared(&self, handle: GuestResourceId) -> Option<ResourceId> {
221        let idx = usize::try_from(handle).ok()?;
222        self.shared.resolve(idx)
223    }
224
225    fn shared_handle(&self, id: ResourceId) -> Option<GuestResourceId> {
226        let handle = self.shared_reverse.get(&id).copied()?;
227        GuestResourceId::try_from(handle).ok()
228    }
229
230    fn remove_shared(&mut self, id: ResourceId) {
231        if let Some(handle) = self.shared_reverse.remove(&id) {
232            self.shared.remove(handle);
233        }
234    }
235
236    fn insert_instance(&mut self, instance_id: ResourceId, resource_id: ResourceId) -> usize {
237        self.instances
238            .entry(instance_id)
239            .or_default()
240            .allocate(resource_id)
241    }
242
243    fn resolve_instance(&self, instance_id: ResourceId, handle: usize) -> Option<ResourceId> {
244        self.instances
245            .get(&instance_id)
246            .and_then(|table| table.resolve(handle))
247    }
248
249    fn remove_instance(&mut self, instance_id: ResourceId, handle: usize) -> Option<ResourceId> {
250        self.instances
251            .get_mut(&instance_id)
252            .and_then(|table| table.remove(handle))
253    }
254
255    fn insert_future(&mut self, instance_id: ResourceId, resource_id: ResourceId) -> usize {
256        self.futures
257            .entry(instance_id)
258            .or_default()
259            .allocate(resource_id)
260    }
261
262    fn resolve_future(&self, instance_id: ResourceId, handle: usize) -> Option<ResourceId> {
263        self.futures
264            .get(&instance_id)
265            .and_then(|table| table.resolve(handle))
266    }
267
268    fn remove_future(&mut self, instance_id: ResourceId, handle: usize) -> Option<ResourceId> {
269        self.futures
270            .get_mut(&instance_id)
271            .and_then(|table| table.remove(handle))
272    }
273
274    fn remove_instance_tables(&mut self, instance_id: ResourceId) {
275        self.instances.remove(&instance_id);
276        self.futures.remove(&instance_id);
277    }
278}
279
280impl RelationIndex {
281    fn set_owner(&mut self, id: ResourceId, owner: ResourceId) {
282        if let Some(previous) = self.owner_of.insert(id, owner)
283            && previous != owner
284        {
285            Self::remove_from_list(self.owned_by.get_mut(&previous), id);
286        }
287        Self::push_unique(self.owned_by.entry(owner).or_default(), id);
288    }
289
290    fn owner(&self, id: ResourceId) -> Option<ResourceId> {
291        self.owner_of.get(&id).copied()
292    }
293
294    fn owned_by(&self, owner: ResourceId) -> Vec<ResourceId> {
295        self.owned_by.get(&owner).cloned().unwrap_or_default()
296    }
297
298    fn set_parent(&mut self, id: ResourceId, parent: ResourceId) {
299        if let Some(previous) = self.parent_of.insert(id, parent)
300            && previous != parent
301        {
302            Self::remove_from_list(self.children_of.get_mut(&previous), id);
303        }
304        Self::push_unique(self.children_of.entry(parent).or_default(), id);
305    }
306
307    fn parent(&self, id: ResourceId) -> Option<ResourceId> {
308        self.parent_of.get(&id).copied()
309    }
310
311    fn children(&self, parent: ResourceId) -> Vec<ResourceId> {
312        self.children_of.get(&parent).cloned().unwrap_or_default()
313    }
314
315    fn set_instance_process(&mut self, instance_id: ResourceId, process_id: ResourceId) {
316        if let Some(previous) = self.instance_to_process.insert(instance_id, process_id)
317            && previous != process_id
318        {
319            self.process_to_instance.remove(&previous);
320        }
321        self.process_to_instance.insert(process_id, instance_id);
322    }
323
324    fn instance_process(&self, instance_id: ResourceId) -> Option<ResourceId> {
325        self.instance_to_process.get(&instance_id).copied()
326    }
327
328    fn process_instance(&self, process_id: ResourceId) -> Option<ResourceId> {
329        self.process_to_instance.get(&process_id).copied()
330    }
331
332    fn set_log_channel(&mut self, process_id: ResourceId, channel_id: ResourceId) {
333        if let Some(previous) = self.process_log_channel.insert(process_id, channel_id)
334            && previous != channel_id
335        {
336            self.log_channel_process.remove(&previous);
337        }
338
339        if let Some(previous) = self.log_channel_process.insert(channel_id, process_id)
340            && previous != process_id
341        {
342            self.process_log_channel.remove(&previous);
343        }
344    }
345
346    fn log_channel(&self, process_id: ResourceId) -> Option<ResourceId> {
347        self.process_log_channel.get(&process_id).copied()
348    }
349
350    fn register_singleton(&mut self, id: DependencyId, resource: ResourceId) -> bool {
351        if self.singletons.contains_key(&id) || self.singleton_ids.contains_key(&resource) {
352            return false;
353        }
354
355        self.singletons.insert(id, resource);
356        self.singleton_ids.insert(resource, id);
357        true
358    }
359
360    fn singleton(&self, id: DependencyId) -> Option<ResourceId> {
361        self.singletons.get(&id).copied()
362    }
363
364    fn remove_resource(&mut self, id: ResourceId) {
365        if let Some(owner) = self.owner_of.remove(&id) {
366            Self::remove_from_list(self.owned_by.get_mut(&owner), id);
367        }
368
369        if let Some(parent) = self.parent_of.remove(&id) {
370            Self::remove_from_list(self.children_of.get_mut(&parent), id);
371        }
372
373        if let Some(process) = self.instance_to_process.remove(&id) {
374            self.process_to_instance.remove(&process);
375        }
376
377        if let Some(instance) = self.process_to_instance.remove(&id) {
378            self.instance_to_process.remove(&instance);
379        }
380
381        if let Some(channel) = self.process_log_channel.remove(&id) {
382            self.log_channel_process.remove(&channel);
383        }
384
385        if let Some(process) = self.log_channel_process.remove(&id) {
386            self.process_log_channel.remove(&process);
387        }
388
389        if let Some(singleton_id) = self.singleton_ids.remove(&id) {
390            self.singletons.remove(&singleton_id);
391        }
392    }
393
394    fn push_unique(list: &mut Vec<ResourceId>, id: ResourceId) {
395        if !list.contains(&id) {
396            list.push(id);
397        }
398    }
399
400    fn remove_from_list(list: Option<&mut Vec<ResourceId>>, id: ResourceId) {
401        if let Some(list) = list {
402            list.retain(|entry| *entry != id);
403        }
404    }
405}
406
407impl ProcessIdentity {
408    /// Create a new identity from a resource id.
409    pub fn new(id: ResourceId) -> Self {
410        Self(id)
411    }
412
413    /// Return the raw numeric representation of this identity.
414    pub fn raw(&self) -> ResourceId {
415        self.0
416    }
417}
418
419impl<T> ResourceHandle<T> {
420    /// Create a typed handle from a raw resource identifier.
421    pub fn new(id: ResourceId) -> ResourceHandle<T> {
422        Self(id, PhantomData)
423    }
424
425    /// Return the raw numeric representation of this handle.
426    pub fn into_id(self) -> ResourceId {
427        self.0
428    }
429}
430
431impl Registry {
432    fn resource_span(kind: ResourceType, owner: Option<ResourceId>) -> Span {
433        tracing::debug_span!(
434            "kernel.resource",
435            resource_id = Empty,
436            kind = ?kind,
437            owner = ?owner,
438            resource_type = Empty,
439            guest_slot = Empty,
440            host_ptr = Empty,
441            parent_id = Empty,
442            shared_id = Empty,
443        )
444    }
445
446    /// Create a new registry.
447    pub fn new() -> Arc<Self> {
448        let registry = Arc::new(Self {
449            resources: Slab::new(),
450            relations: Mutex::new(RelationIndex::default()),
451            handles: Mutex::new(HandleIndex::new()),
452        });
453
454        // Reserve the first ID (id=0) for system use
455        registry.resources.insert(Resource {
456            data: Arc::new(Mutex::new(Some(Box::new(())))),
457            kind: ResourceType::Other,
458            span: Self::resource_span(ResourceType::Other, None),
459        });
460
461        registry
462    }
463
464    /// Create an [`InstanceRegistry`] view tied to this registry.
465    pub fn instance(self: &Arc<Self>) -> Result<InstanceRegistry, RegistryError> {
466        let instance = self.add(InstanceState::new(), None, ResourceType::Instance)?;
467        Ok(InstanceRegistry {
468            registry: self.clone(),
469            instance_id: instance.into_id(),
470        })
471    }
472
473    /// Add a resource to the registry and return its typed handle.
474    pub fn add<T: Send + 'static>(
475        &self,
476        resource: T,
477        owner: Option<ResourceId>,
478        kind: ResourceType,
479    ) -> Result<ResourceHandle<T>, RegistryError> {
480        let r = Resource {
481            data: Arc::new(Mutex::new(Some(Box::new(resource)))),
482            kind,
483            span: Self::resource_span(kind, owner),
484        };
485        let raw = self
486            .resources
487            .insert(r)
488            .ok_or(RegistryError::CapacityExhausted)?;
489        if let Some(owner) = owner {
490            let mut relations = self
491                .relations
492                .lock()
493                .map_err(|_| RegistryError::LockPoisoned)?;
494            relations.set_owner(raw, owner);
495        }
496        self.record_resource_added::<T>(raw);
497        Ok(ResourceHandle(raw, PhantomData))
498    }
499
500    /// Reserve a slot for a resource and return its identifier.
501    pub fn reserve(
502        &self,
503        owner: Option<ResourceId>,
504        kind: ResourceType,
505    ) -> Result<ResourceId, RegistryError> {
506        let r = Resource {
507            data: Arc::new(Mutex::new(None)),
508            kind,
509            span: Self::resource_span(kind, owner),
510        };
511
512        let id = self
513            .resources
514            .insert(r)
515            .ok_or(RegistryError::CapacityExhausted)?;
516        if let Some(owner) = owner {
517            let mut relations = self
518                .relations
519                .lock()
520                .map_err(|_| RegistryError::LockPoisoned)?;
521            relations.set_owner(id, owner);
522        }
523        self.record_resource_reserved(id);
524        Ok(id)
525    }
526
527    /// Initialise a reserved slot with the given resource.
528    pub fn initialise<T: Send + 'static>(
529        &self,
530        id: ResourceId,
531        resource: T,
532    ) -> Result<ResourceHandle<T>, RegistryError> {
533        let entry = self
534            .resources
535            .get(id)
536            .ok_or(RegistryError::InvalidReservation)?;
537        let mut guard = entry.data.lock().map_err(|_| RegistryError::LockPoisoned)?;
538
539        if guard.is_some() {
540            return Err(RegistryError::InvalidReservation);
541        }
542
543        *guard = Some(Box::new(resource));
544        self.record_resource_initialised::<T>(id);
545        Ok(ResourceHandle(id, PhantomData))
546    }
547
548    /// Remove a resource from the registry, returning ownership.
549    pub fn remove<T: 'static>(&self, id: ResourceHandle<T>) -> Option<T> {
550        self.record_resource_removed(id.0);
551        let kind = self.resources.get(id.0).map(|resource| resource.kind);
552        if let Ok(mut handles) = self.handles.lock() {
553            handles.remove_shared(id.0);
554            if matches!(kind, Some(ResourceType::Instance)) {
555                handles.remove_instance_tables(id.0);
556            }
557        }
558        if let Ok(mut relations) = self.relations.lock() {
559            relations.remove_resource(id.0);
560        }
561        self.resources.take(id.0).and_then(|resource| {
562            let data = Arc::try_unwrap(resource.data).ok()?;
563            let boxed_opt = data.into_inner().ok()?;
564            let boxed = boxed_opt?;
565            boxed.downcast::<T>().map(|b| *b).ok()
566        })
567    }
568
569    /// Discard a resource entry without attempting to downcast its payload.
570    pub fn discard(&self, id: ResourceId) -> bool {
571        self.record_resource_removed(id);
572        let kind = self.resources.get(id).map(|resource| resource.kind);
573        if let Ok(mut handles) = self.handles.lock() {
574            handles.remove_shared(id);
575            if matches!(kind, Some(ResourceType::Instance)) {
576                handles.remove_instance_tables(id);
577            }
578        }
579        if let Ok(mut relations) = self.relations.lock() {
580            relations.remove_resource(id);
581        }
582        self.resources.take(id).is_some()
583    }
584
585    /// Borrow a resource mutably by erased handle and run a closure with it.
586    pub fn with<T: 'static, R>(
587        &self,
588        id: ResourceHandle<T>,
589        func: impl FnOnce(&mut T) -> R,
590    ) -> Option<R> {
591        let (data, span) = {
592            let entry = self.resources.get(id.0)?;
593            (entry.data.clone(), entry.span.clone())
594        };
595        let mut guard = data.lock().ok()?;
596        let t = guard.as_mut().and_then(|boxed| boxed.downcast_mut::<T>())?;
597        Some(span.in_scope(|| func(t)))
598    }
599
600    /// Borrow a resource mutably by erased handle and run a closure with it.
601    pub(crate) async fn with_async<T: Send + 'static, R>(
602        &self,
603        id: ResourceHandle<T>,
604        func: impl for<'a> FnOnce(&'a mut T) -> BoxFuture<'a, R>,
605    ) -> Option<R> {
606        let (data, span) = {
607            let entry = self.resources.get(id.0)?;
608            (entry.data.clone(), entry.span.clone())
609        };
610        let boxed = {
611            let mut guard = data.lock().ok()?;
612            guard.take()?
613        };
614        let mut resource = boxed.downcast::<T>().ok()?;
615        let result = func(resource.as_mut()).instrument(span).await;
616        let boxed: Box<dyn Any + Send> = resource;
617        let mut guard = data.lock().ok()?;
618        *guard = Some(boxed);
619        Some(result)
620    }
621
622    /// Create or retrieve a shared guest handle for the resource id.
623    pub fn share_handle(&self, id: ResourceId) -> Result<GuestResourceId, RegistryError> {
624        let shared = {
625            let mut handles = self
626                .handles
627                .lock()
628                .map_err(|_| RegistryError::LockPoisoned)?;
629            handles.share_handle(id)
630        }?;
631
632        self.record_shared_handle(id, shared);
633
634        Ok(shared)
635    }
636
637    /// Resolve a shared guest handle into its resource id.
638    pub fn resolve_shared(&self, handle: GuestResourceId) -> Option<ResourceId> {
639        let resolved = {
640            let handles = self.handles.lock().ok()?;
641            handles.resolve_shared(handle)
642        };
643        if let Some(id) = resolved
644            && let Some(resource) = self.resources.get(id)
645        {
646            debug!(parent: &resource.span, shared_handle = handle, "resolve shared handle");
647        }
648        resolved
649    }
650
651    /// Return the shared guest handle for a resource id, if one exists.
652    pub fn shared_handle(&self, id: ResourceId) -> Option<GuestResourceId> {
653        let handles = self.handles.lock().ok()?;
654        handles.shared_handle(id)
655    }
656
657    /// Fetch metadata for a resource.
658    pub fn metadata(&self, id: ResourceId) -> Option<ResourceMetadata> {
659        let resource = self.resources.get(id)?;
660        let owner = self.relations.lock().ok()?.owner(id);
661        Some(ResourceMetadata {
662            id,
663            owner,
664            kind: resource.kind,
665        })
666    }
667
668    /// Return the recorded owner for a resource.
669    pub fn owner(&self, id: ResourceId) -> Option<ResourceId> {
670        self.relations.lock().ok()?.owner(id)
671    }
672
673    /// Return the resources owned by the provided resource id.
674    pub fn owned_resources(&self, owner: ResourceId) -> Vec<ResourceId> {
675        self.relations
676            .lock()
677            .map(|relations| relations.owned_by(owner))
678            .unwrap_or_default()
679    }
680
681    /// Return the recorded parent for a resource.
682    pub fn parent(&self, id: ResourceId) -> Option<ResourceId> {
683        self.relations.lock().ok()?.parent(id)
684    }
685
686    /// Return the children linked to the provided resource id.
687    pub fn children(&self, id: ResourceId) -> Vec<ResourceId> {
688        self.relations
689            .lock()
690            .map(|relations| relations.children(id))
691            .unwrap_or_default()
692    }
693
694    /// Return the process id associated with the provided instance id.
695    pub fn instance_process(&self, instance_id: ResourceId) -> Option<ResourceId> {
696        self.relations.lock().ok()?.instance_process(instance_id)
697    }
698
699    /// Return the instance id associated with the provided process id.
700    pub fn process_instance(&self, process_id: ResourceId) -> Option<ResourceId> {
701        self.relations.lock().ok()?.process_instance(process_id)
702    }
703
704    /// Return the registered log channel resource for the process, if present.
705    pub fn log_channel(&self, process_id: ResourceId) -> Option<ResourceId> {
706        self.relations.lock().ok()?.log_channel(process_id)
707    }
708
709    /// Return the registered log channel handle for the process, if present.
710    pub fn log_channel_handle(&self, process_id: ResourceId) -> Option<GuestResourceId> {
711        let channel_id = self.log_channel(process_id)?;
712        self.shared_handle(channel_id)
713    }
714
715    /// Register a singleton dependency identifier against the supplied resource.
716    ///
717    /// Returns `false` if the identifier or resource is already registered.
718    pub fn register_singleton(
719        &self,
720        id: DependencyId,
721        resource: ResourceId,
722    ) -> Result<bool, RegistryError> {
723        let mut relations = self
724            .relations
725            .lock()
726            .map_err(|_| RegistryError::LockPoisoned)?;
727        Ok(relations.register_singleton(id, resource))
728    }
729
730    /// Resolve a singleton dependency identifier to its backing resource id.
731    pub fn singleton(&self, id: DependencyId) -> Option<ResourceId> {
732        self.relations.lock().ok()?.singleton(id)
733    }
734
735    fn record_resource_added<T: 'static>(&self, id: ResourceId) {
736        if let Some(resource) = self.resources.get(id) {
737            resource.span.record("resource_id", field::display(id));
738            resource
739                .span
740                .record("resource_type", field::display(std::any::type_name::<T>()));
741            debug!(parent: &resource.span, "resource registered");
742        }
743    }
744
745    fn record_resource_reserved(&self, id: ResourceId) {
746        if let Some(resource) = self.resources.get(id) {
747            resource.span.record("resource_id", field::display(id));
748            debug!(parent: &resource.span, "resource reserved");
749        }
750    }
751
752    fn record_resource_initialised<T: 'static>(&self, id: ResourceId) {
753        if let Some(resource) = self.resources.get(id) {
754            resource
755                .span
756                .record("resource_type", field::display(std::any::type_name::<T>()));
757            debug!(parent: &resource.span, "resource initialised");
758        }
759    }
760
761    fn record_resource_removed(&self, id: ResourceId) {
762        if let Some(resource) = self.resources.get(id) {
763            debug!(parent: &resource.span, "resource removed");
764        }
765    }
766
767    fn record_guest_slot(&self, id: ResourceId, slot: usize) {
768        if let Some(resource) = self.resources.get(id) {
769            resource.span.record("guest_slot", field::display(slot));
770            debug!(parent: &resource.span, guest_slot = %slot, "resource slot assigned");
771        }
772    }
773
774    fn record_slot_detached(&self, id: ResourceId, slot: usize) {
775        if let Some(resource) = self.resources.get(id) {
776            debug!(parent: &resource.span, guest_slot = %slot, "resource slot detached");
777        }
778    }
779
780    fn record_shared_handle(&self, id: ResourceId, shared: GuestResourceId) {
781        if let Some(resource) = self.resources.get(id) {
782            resource.span.record("shared_id", field::display(shared));
783            debug!(parent: &resource.span, shared_handle = shared, "resource shared");
784        } else {
785            debug!(resource_id = %id, shared_handle = shared, "resource shared");
786        }
787    }
788
789    /// Record a host pointer identifier for the specified resource.
790    pub(crate) fn record_host_ptr(&self, id: ResourceId, ptr: &str) {
791        if let Some(resource) = self.resources.get(id) {
792            resource.span.record("host_ptr", field::display(ptr));
793            debug!(parent: &resource.span, host_ptr = %ptr, "resource host pointer");
794        }
795    }
796
797    /// Record the parent resource that produced this resource.
798    pub(crate) fn record_parent(&self, id: ResourceId, parent: ResourceId) {
799        if let Ok(mut relations) = self.relations.lock() {
800            relations.set_parent(id, parent);
801        }
802        if let Some(resource) = self.resources.get(id) {
803            resource.span.record("parent_id", field::display(parent));
804            debug!(parent: &resource.span, parent_id = %parent, "resource parent linked");
805        }
806    }
807
808    /// Associate a process instance with a registry instance.
809    pub(crate) fn set_instance_process(
810        &self,
811        instance_id: ResourceId,
812        process_id: ResourceId,
813    ) -> Result<(), RegistryError> {
814        if self.resources.get(instance_id).is_none() {
815            return Err(RegistryError::InvalidReservation);
816        }
817        if self.resources.get(process_id).is_none() {
818            return Err(RegistryError::InvalidReservation);
819        }
820        let mut relations = self
821            .relations
822            .lock()
823            .map_err(|_| RegistryError::LockPoisoned)?;
824        relations.set_instance_process(instance_id, process_id);
825        relations.set_owner(instance_id, process_id);
826        Ok(())
827    }
828
829    /// Associate a process with its log channel resource.
830    pub(crate) fn set_log_channel(
831        &self,
832        process_id: ResourceId,
833        channel_id: ResourceId,
834    ) -> Result<(), RegistryError> {
835        if self.resources.get(process_id).is_none() {
836            return Err(RegistryError::InvalidReservation);
837        }
838        if self.resources.get(channel_id).is_none() {
839            return Err(RegistryError::InvalidReservation);
840        }
841        let mut relations = self
842            .relations
843            .lock()
844            .map_err(|_| RegistryError::LockPoisoned)?;
845        relations.set_log_channel(process_id, channel_id);
846        Ok(())
847    }
848}
849
850impl InstanceRegistry {
851    fn with_instance_state<R>(&self, f: impl FnOnce(&mut InstanceState) -> R) -> Option<R> {
852        self.registry.with(ResourceHandle::new(self.instance_id), f)
853    }
854
855    /// Attach a mailbox used for guest async wake-ups.
856    ///
857    /// Returns an error if the instance state is missing.
858    pub fn load_mailbox(&mut self, mb: &'static GuestMailbox) -> Result<(), RegistryError> {
859        self.with_instance_state(|state| state.mailbox = Some(mb))
860            .ok_or(RegistryError::MissingInstance)
861    }
862
863    /// Create a lightweight registrar for instance-scoped resources.
864    pub fn registrar(&self) -> InstanceRegistrar {
865        InstanceRegistrar {
866            registry: self.registry.clone(),
867            instance_id: self.instance_id,
868        }
869    }
870
871    /// Refresh mailbox base pointers after guest memory growth.
872    pub fn refresh_mailbox(&self, base: usize) {
873        if let Some(mb) = self.mailbox() {
874            mb.refresh_base(base);
875        }
876    }
877
878    /// Close the mailbox to prevent further guest wake-ups.
879    pub fn close_mailbox(&self) {
880        if let Some(mb) = self.mailbox() {
881            mb.close();
882        }
883    }
884
885    /// Set a hard memory limit for this instance.
886    ///
887    /// Returns an error if the instance state is missing.
888    pub fn set_memory_limit(&mut self, bytes: usize) -> Result<(), RegistryError> {
889        self.with_instance_state(|state| {
890            state.limits = StoreLimitsBuilder::new().memory_size(bytes).build();
891        })
892        .ok_or(RegistryError::MissingInstance)
893    }
894
895    fn insert_instance_handle(&self, resource_id: ResourceId) -> Result<usize, RegistryError> {
896        let mut handles = self
897            .registry
898            .handles
899            .lock()
900            .map_err(|_| RegistryError::LockPoisoned)?;
901        Ok(handles.insert_instance(self.instance_id, resource_id))
902    }
903
904    fn remove_instance_handle(&self, handle: usize) -> Option<ResourceId> {
905        let mut handles = self.registry.handles.lock().ok()?;
906        handles.remove_instance(self.instance_id, handle)
907    }
908
909    fn resolve_instance_handle(&self, handle: usize) -> Option<ResourceId> {
910        let handles = self.registry.handles.lock().ok()?;
911        handles.resolve_instance(self.instance_id, handle)
912    }
913
914    fn insert_future_handle(&self, resource_id: ResourceId) -> Result<usize, RegistryError> {
915        let mut handles = self
916            .registry
917            .handles
918            .lock()
919            .map_err(|_| RegistryError::LockPoisoned)?;
920        Ok(handles.insert_future(self.instance_id, resource_id))
921    }
922
923    fn resolve_future_handle(&self, handle: usize) -> Option<ResourceId> {
924        let handles = self.registry.handles.lock().ok()?;
925        handles.resolve_future(self.instance_id, handle)
926    }
927
928    fn remove_future_handle(&self, handle: usize) -> Option<ResourceId> {
929        let mut handles = self.registry.handles.lock().ok()?;
930        handles.remove_future(self.instance_id, handle)
931    }
932
933    /// Insert a resource entry and return its slot index.
934    pub fn insert<T: Send + 'static>(
935        &mut self,
936        entry: T,
937        owner: Option<ResourceId>,
938        kind: ResourceType,
939    ) -> Result<usize, RegistryError> {
940        let owner = self.process_id()?.or(owner);
941        let entry = self.registry.add(entry, owner, kind)?;
942        let resource_id = entry.0;
943        let slot = self.insert_instance_handle(resource_id)?;
944        self.registry.record_guest_slot(resource_id, slot);
945        Ok(slot)
946    }
947
948    /// Insert a resource ID and return its slot index.
949    pub fn insert_id(&mut self, id: ResourceId) -> Result<usize, RegistryError> {
950        let slot = self.insert_instance_handle(id)?;
951        self.registry.record_guest_slot(id, slot);
952        Ok(slot)
953    }
954
955    /// Retrieve the entry for the given slot.
956    pub fn entry(&self, idx: usize) -> Option<ResourceId> {
957        self.resolve_instance_handle(idx)
958    }
959
960    /// Borrow a resource by table index and apply a closure.
961    pub fn with<T: 'static, R>(&self, idx: ResourceId, f: impl FnOnce(&mut T) -> R) -> Option<R> {
962        let resource = self.resolve_instance_handle(idx)?;
963        self.registry
964            .with::<T, R>(ResourceHandle(resource, PhantomData), f)
965    }
966
967    /// Attach custom extension data to the instance.
968    ///
969    /// Returns an error if the instance state is missing.
970    pub fn insert_extension<T: Any + Send + Sync>(
971        &mut self,
972        value: T,
973    ) -> Result<(), RegistryError> {
974        let ext: Arc<dyn Any + Send + Sync> = Arc::new(value);
975        self.with_instance_state(|state| {
976            state.extensions.insert(TypeId::of::<T>(), ext);
977        })
978        .ok_or(RegistryError::MissingInstance)
979    }
980
981    /// Borrow extension data by type.
982    pub fn extension<T: Any + Send + Sync>(&self) -> Option<Arc<T>> {
983        self.with_instance_state(|state| {
984            state
985                .extensions
986                .get(&TypeId::of::<T>())
987                .and_then(|boxed| Arc::clone(boxed).downcast::<T>().ok())
988        })
989        .flatten()
990    }
991
992    /// Remove an entry by slot index.
993    pub fn remove<T: 'static>(&mut self, idx: usize) -> Option<T> {
994        let resource_id = self.remove_instance_handle(idx)?;
995        self.registry
996            .remove(ResourceHandle(resource_id, PhantomData))
997    }
998
999    /// Remove a slot entry without deleting the underlying resource.
1000    pub fn detach_slot(&mut self, idx: usize) -> Option<ResourceId> {
1001        let resource_id = self.remove_instance_handle(idx);
1002        if let Some(resource_id) = resource_id {
1003            self.registry.record_slot_detached(resource_id, idx);
1004        }
1005        resource_id
1006    }
1007
1008    /// Produce a waker for the specified guest task if the mailbox is available.
1009    pub fn waker(&self, task_id: usize) -> Option<Waker> {
1010        self.mailbox().map(|mailbox| mailbox.waker(task_id))
1011    }
1012
1013    /// Access the guest mailbox backing async wake-ups.
1014    pub fn mailbox(&self) -> Option<&'static GuestMailbox> {
1015        self.with_instance_state(|state| state.mailbox).flatten()
1016    }
1017
1018    /// Get a reference to the global registry.
1019    pub fn registry(&self) -> &Registry {
1020        &self.registry
1021    }
1022
1023    /// Clone the underlying global registry reference.
1024    pub fn registry_arc(&self) -> Arc<Registry> {
1025        self.registry.clone()
1026    }
1027
1028    /// Set the process identifier for this instance. Must be called before guest
1029    /// code can create resources.
1030    ///
1031    /// Returns an error if the instance state is missing.
1032    pub fn set_process_id(&mut self, process_id: ResourceId) -> Result<(), RegistryError> {
1033        self.with_instance_state(|state| state.process_id = Some(process_id))
1034            .ok_or(RegistryError::MissingInstance)?;
1035        self.registry
1036            .set_instance_process(self.instance_id, process_id)
1037    }
1038
1039    fn process_id(&self) -> Result<Option<ResourceId>, RegistryError> {
1040        self.with_instance_state(|state| state.process_id)
1041            .ok_or(RegistryError::MissingInstance)
1042    }
1043
1044    /// Grant a resource capability to the specified session entry.
1045    pub fn grant_session_resource(
1046        &self,
1047        session_slot: usize,
1048        capability: Capability,
1049        resource: ResourceId,
1050    ) -> Result<bool, KernelError> {
1051        self.with::<Session, _>(session_slot, |session| {
1052            session.grant_resource(capability, resource)
1053        })
1054        .ok_or(KernelError::InvalidHandle)
1055    }
1056
1057    /// Revoke a resource capability from the specified session entry.
1058    pub fn revoke_session_resource(
1059        &self,
1060        session_slot: usize,
1061        capability: Capability,
1062        resource: ResourceId,
1063    ) -> Result<Result<bool, SessionError>, KernelError> {
1064        self.with::<Session, _>(session_slot, |session| {
1065            session.revoke_resource(capability, resource)
1066        })
1067        .ok_or(KernelError::InvalidHandle)
1068    }
1069
1070    /// Insert a guest future and return its handle.
1071    pub fn insert_future(
1072        &mut self,
1073        state: Arc<FutureSharedState<GuestResult<Vec<u8>>>>,
1074    ) -> Result<usize, RegistryError> {
1075        let owner = self.process_id()?;
1076        let entry = self.registry.add(state, owner, ResourceType::Future)?;
1077        let handle = self.insert_future_handle(entry.0)?;
1078        Ok(handle)
1079    }
1080
1081    /// Retrieve the shared state for a given future handle.
1082    pub(crate) fn future_state(&self, handle: usize) -> Option<GuestFuture> {
1083        let resource_id = self.resolve_future_handle(handle)?;
1084        self.registry.with(
1085            ResourceHandle::new(resource_id),
1086            |state: &mut GuestFuture| Arc::clone(state),
1087        )
1088    }
1089
1090    /// Remove a future handle, returning the shared state if present.
1091    pub fn remove_future(&mut self, handle: usize) -> Option<GuestFuture> {
1092        let resource_id = self.remove_future_handle(handle)?;
1093        self.registry
1094            .remove(ResourceHandle::<GuestFuture>::new(resource_id))
1095    }
1096}
1097
1098impl InstanceRegistrar {
1099    fn with_instance_state<R>(&self, f: impl FnOnce(&mut InstanceState) -> R) -> Option<R> {
1100        self.registry.with(ResourceHandle::new(self.instance_id), f)
1101    }
1102
1103    fn process_id(&self) -> Result<Option<ResourceId>, RegistryError> {
1104        self.with_instance_state(|state| state.process_id)
1105            .ok_or(RegistryError::MissingInstance)
1106    }
1107
1108    fn insert_instance_handle(&self, resource_id: ResourceId) -> Result<usize, RegistryError> {
1109        let mut handles = self
1110            .registry
1111            .handles
1112            .lock()
1113            .map_err(|_| RegistryError::LockPoisoned)?;
1114        Ok(handles.insert_instance(self.instance_id, resource_id))
1115    }
1116
1117    fn resolve_instance_handle(&self, handle: usize) -> Option<ResourceId> {
1118        let handles = self.registry.handles.lock().ok()?;
1119        handles.resolve_instance(self.instance_id, handle)
1120    }
1121
1122    /// Insert a resource entry and return its slot index.
1123    pub fn insert<T: Send + 'static>(
1124        &self,
1125        entry: T,
1126        owner: Option<ResourceId>,
1127        kind: ResourceType,
1128    ) -> Result<usize, RegistryError> {
1129        let owner = self.process_id()?.or(owner);
1130        let entry = self.registry.add(entry, owner, kind)?;
1131        let resource_id = entry.0;
1132        let slot = self.insert_instance_handle(resource_id)?;
1133        self.registry.record_guest_slot(resource_id, slot);
1134        Ok(slot)
1135    }
1136
1137    /// Insert a resource ID and return its slot index.
1138    pub fn insert_id(&self, id: ResourceId) -> Result<usize, RegistryError> {
1139        let slot = self.insert_instance_handle(id)?;
1140        self.registry.record_guest_slot(id, slot);
1141        Ok(slot)
1142    }
1143
1144    /// Retrieve the entry for the given slot.
1145    pub fn entry(&self, idx: usize) -> Option<ResourceId> {
1146        self.resolve_instance_handle(idx)
1147    }
1148}
1149
1150impl Drop for InstanceRegistry {
1151    fn drop(&mut self) {
1152        if let Some(mb) = self.mailbox() {
1153            mb.close();
1154        }
1155        self.registry.discard(self.instance_id);
1156    }
1157}
1158
1159#[cfg(test)]
1160mod tests {
1161    use super::*;
1162    use std::sync::Arc;
1163
1164    #[test]
1165    fn detach_slot_returns_resource_id_without_dropping() {
1166        let registry = Registry::new();
1167        let resource = registry
1168            .add(5u32, None, ResourceType::Other)
1169            .expect("insert resource");
1170        let id = resource.into_id();
1171
1172        let mut instance = registry.instance().expect("instance registry");
1173        let slot = instance.insert_id(id).expect("insert id");
1174        let detached = instance.detach_slot(slot).expect("detach handle");
1175        assert_eq!(detached, id);
1176
1177        let value = registry
1178            .with(ResourceHandle::<u32>::new(detached), |value| *value)
1179            .expect("resource present");
1180        assert_eq!(value, 5);
1181    }
1182
1183    #[test]
1184    fn shared_handle_is_stable_and_cleared_on_remove() {
1185        let registry = Registry::new();
1186        let resource = registry
1187            .add(10u32, None, ResourceType::Other)
1188            .expect("insert resource");
1189        let id = resource.into_id();
1190
1191        let handle_a = registry.share_handle(id).expect("share handle");
1192        let handle_b = registry.share_handle(id).expect("share handle");
1193        assert_eq!(handle_a, handle_b);
1194
1195        let removed = registry.remove(ResourceHandle::<u32>::new(id));
1196        assert_eq!(removed, Some(10));
1197
1198        assert!(registry.resolve_shared(handle_a).is_none());
1199        assert!(registry.shared_handle(id).is_none());
1200    }
1201
1202    #[test]
1203    fn instance_process_relation_is_recorded() {
1204        let registry = Registry::new();
1205        let process = registry
1206            .add((), None, ResourceType::Process)
1207            .expect("insert process");
1208        let process_id = process.into_id();
1209
1210        let mut instance = registry.instance().expect("instance registry");
1211        let instance_id = instance.instance_id;
1212        instance.set_process_id(process_id).expect("set process id");
1213
1214        assert_eq!(registry.instance_process(instance_id), Some(process_id));
1215        assert_eq!(registry.process_instance(process_id), Some(instance_id));
1216        assert_eq!(registry.owner(instance_id), Some(process_id));
1217    }
1218
1219    #[test]
1220    fn parent_child_relation_roundtrip() {
1221        let registry = Registry::new();
1222        let parent = registry
1223            .add((), None, ResourceType::Other)
1224            .expect("insert parent")
1225            .into_id();
1226        let child = registry
1227            .add((), None, ResourceType::Other)
1228            .expect("insert child")
1229            .into_id();
1230
1231        registry.record_parent(child, parent);
1232        assert_eq!(registry.parent(child), Some(parent));
1233        assert!(registry.children(parent).contains(&child));
1234
1235        registry.discard(child);
1236        assert!(!registry.children(parent).contains(&child));
1237    }
1238
1239    #[test]
1240    fn owned_resources_updates_on_remove() {
1241        let registry = Registry::new();
1242        let owner = registry
1243            .add((), None, ResourceType::Other)
1244            .expect("insert owner")
1245            .into_id();
1246        let first = registry
1247            .add(5u32, Some(owner), ResourceType::Other)
1248            .expect("insert owned")
1249            .into_id();
1250        let second = registry
1251            .add(6u64, Some(owner), ResourceType::Other)
1252            .expect("insert owned")
1253            .into_id();
1254
1255        let owned = registry.owned_resources(owner);
1256        assert!(owned.contains(&first));
1257        assert!(owned.contains(&second));
1258
1259        registry.remove(ResourceHandle::<u32>::new(first));
1260        let owned = registry.owned_resources(owner);
1261        assert!(!owned.contains(&first));
1262        assert!(owned.contains(&second));
1263    }
1264
1265    #[test]
1266    fn future_handle_roundtrip() {
1267        let registry = Registry::new();
1268        let mut instance = registry.instance().expect("instance registry");
1269        let state = FutureSharedState::<GuestResult<Vec<u8>>>::new();
1270        let handle = instance
1271            .insert_future(Arc::clone(&state))
1272            .expect("insert future");
1273
1274        let resolved = instance.future_state(handle).expect("future state");
1275        assert!(Arc::ptr_eq(&state, &resolved));
1276
1277        let removed = instance.remove_future(handle).expect("remove future");
1278        assert!(Arc::ptr_eq(&state, &removed));
1279        assert!(instance.future_state(handle).is_none());
1280    }
1281
1282    #[test]
1283    fn instance_handle_reuse() {
1284        let registry = Registry::new();
1285        let mut instance = registry.instance().expect("instance registry");
1286        let _slot_a = instance
1287            .insert(1u32, None, ResourceType::Other)
1288            .expect("insert resource");
1289        let slot_b = instance
1290            .insert(2u32, None, ResourceType::Other)
1291            .expect("insert resource");
1292        let removed = instance.remove::<u32>(slot_b).expect("remove resource");
1293        assert_eq!(removed, 2);
1294
1295        let slot_c = instance
1296            .insert(3u32, None, ResourceType::Other)
1297            .expect("insert resource");
1298        assert_eq!(slot_c, slot_b);
1299    }
1300
1301    #[test]
1302    fn registrar_inserts_into_instance_table_and_sets_owner() {
1303        let registry = Registry::new();
1304        let process = registry
1305            .add((), None, ResourceType::Process)
1306            .expect("insert process");
1307        let process_id = process.into_id();
1308
1309        let mut instance = registry.instance().expect("instance registry");
1310        instance.set_process_id(process_id).expect("set process id");
1311        let registrar = instance.registrar();
1312
1313        let slot = registrar
1314            .insert(42u32, None, ResourceType::Other)
1315            .expect("registrar insert");
1316        let resource_id = instance.entry(slot).expect("entry present");
1317        let value = registry
1318            .with(ResourceHandle::<u32>::new(resource_id), |value| *value)
1319            .expect("resource present");
1320        assert_eq!(value, 42);
1321        assert_eq!(registry.owner(resource_id), Some(process_id));
1322    }
1323}