wasmtime/runtime/component/
resource_table.rs

1use super::Resource;
2use crate::prelude::*;
3use alloc::collections::{BTreeMap, BTreeSet};
4use core::any::Any;
5use core::fmt;
6
7#[derive(Debug)]
8/// Errors returned by operations on `ResourceTable`
9pub enum ResourceTableError {
10    /// ResourceTable has no free keys
11    Full,
12    /// Resource not present in table
13    NotPresent,
14    /// Resource present in table, but with a different type
15    WrongType,
16    /// Resource cannot be deleted because child resources exist in the table. Consult wit docs for
17    /// the particular resource to see which methods may return child resources.
18    HasChildren,
19}
20
21impl fmt::Display for ResourceTableError {
22    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23        match self {
24            Self::Full => write!(f, "resource table has no free keys"),
25            Self::NotPresent => write!(f, "resource not present"),
26            Self::WrongType => write!(f, "resource is of another type"),
27            Self::HasChildren => write!(f, "resource has children"),
28        }
29    }
30}
31
32impl core::error::Error for ResourceTableError {}
33
34/// The `ResourceTable` type maps a `Resource<T>` to its `T`.
35#[derive(Debug)]
36pub struct ResourceTable {
37    entries: Vec<Entry>,
38    free_head: Option<usize>,
39}
40
41#[derive(Debug)]
42enum Entry {
43    Free { next: Option<usize> },
44    Occupied { entry: TableEntry },
45}
46
47impl Entry {
48    pub fn occupied(&self) -> Option<&TableEntry> {
49        match self {
50            Self::Occupied { entry } => Some(entry),
51            Self::Free { .. } => None,
52        }
53    }
54
55    pub fn occupied_mut(&mut self) -> Option<&mut TableEntry> {
56        match self {
57            Self::Occupied { entry } => Some(entry),
58            Self::Free { .. } => None,
59        }
60    }
61}
62
63/// This structure tracks parent and child relationships for a given table entry.
64///
65/// Parents and children are referred to by table index. We maintain the
66/// following invariants to prevent orphans and cycles:
67/// * parent can only be assigned on creating the entry.
68/// * parent, if some, must exist when creating the entry.
69/// * whenever a child is created, its index is added to children.
70/// * whenever a child is deleted, its index is removed from children.
71/// * an entry with children may not be deleted.
72#[derive(Debug)]
73struct TableEntry {
74    /// The entry in the table, as a boxed dynamically-typed object
75    entry: Box<dyn Any + Send>,
76    /// The index of the parent of this entry, if it has one.
77    parent: Option<u32>,
78    /// The indices of any children of this entry.
79    children: BTreeSet<u32>,
80}
81
82impl TableEntry {
83    fn new(entry: Box<dyn Any + Send>, parent: Option<u32>) -> Self {
84        Self {
85            entry,
86            parent,
87            children: BTreeSet::new(),
88        }
89    }
90    fn add_child(&mut self, child: u32) {
91        debug_assert!(!self.children.contains(&child));
92        self.children.insert(child);
93    }
94    fn remove_child(&mut self, child: u32) {
95        let was_removed = self.children.remove(&child);
96        debug_assert!(was_removed);
97    }
98}
99
100impl ResourceTable {
101    /// Create an empty table
102    pub fn new() -> Self {
103        ResourceTable {
104            entries: Vec::new(),
105            free_head: None,
106        }
107    }
108
109    /// Create an empty table with at least the specified capacity.
110    pub fn with_capacity(capacity: usize) -> Self {
111        ResourceTable {
112            entries: Vec::with_capacity(capacity),
113            free_head: None,
114        }
115    }
116
117    /// Inserts a new value `T` into this table, returning a corresponding
118    /// `Resource<T>` which can be used to refer to it after it was inserted.
119    pub fn push<T>(&mut self, entry: T) -> Result<Resource<T>, ResourceTableError>
120    where
121        T: Send + 'static,
122    {
123        let idx = self.push_(TableEntry::new(Box::new(entry), None))?;
124        Ok(Resource::new_own(idx))
125    }
126
127    /// Pop an index off of the free list, if it's not empty.
128    fn pop_free_list(&mut self) -> Option<usize> {
129        if let Some(ix) = self.free_head {
130            // Advance free_head to the next entry if one is available.
131            match &self.entries[ix] {
132                Entry::Free { next } => self.free_head = *next,
133                Entry::Occupied { .. } => unreachable!(),
134            }
135            Some(ix)
136        } else {
137            None
138        }
139    }
140
141    /// Free an entry in the table, returning its [`TableEntry`]. Add the index to the free list.
142    fn free_entry(&mut self, ix: usize) -> TableEntry {
143        let entry = match core::mem::replace(
144            &mut self.entries[ix],
145            Entry::Free {
146                next: self.free_head,
147            },
148        ) {
149            Entry::Occupied { entry } => entry,
150            Entry::Free { .. } => unreachable!(),
151        };
152
153        self.free_head = Some(ix);
154
155        entry
156    }
157
158    /// Push a new entry into the table, returning its handle. This will prefer to use free entries
159    /// if they exist, falling back on pushing new entries onto the end of the table.
160    fn push_(&mut self, e: TableEntry) -> Result<u32, ResourceTableError> {
161        if let Some(free) = self.pop_free_list() {
162            self.entries[free] = Entry::Occupied { entry: e };
163            Ok(free.try_into().unwrap())
164        } else {
165            let ix = self
166                .entries
167                .len()
168                .try_into()
169                .map_err(|_| ResourceTableError::Full)?;
170            self.entries.push(Entry::Occupied { entry: e });
171            Ok(ix)
172        }
173    }
174
175    fn occupied(&self, key: u32) -> Result<&TableEntry, ResourceTableError> {
176        self.entries
177            .get(key as usize)
178            .and_then(Entry::occupied)
179            .ok_or(ResourceTableError::NotPresent)
180    }
181
182    fn occupied_mut(&mut self, key: u32) -> Result<&mut TableEntry, ResourceTableError> {
183        self.entries
184            .get_mut(key as usize)
185            .and_then(Entry::occupied_mut)
186            .ok_or(ResourceTableError::NotPresent)
187    }
188
189    /// Insert a resource at the next available index, and track that it has a
190    /// parent resource.
191    ///
192    /// The parent must exist to create a child. All children resources must
193    /// be destroyed before a parent can be destroyed - otherwise
194    /// [`ResourceTable::delete`] will fail with
195    /// [`ResourceTableError::HasChildren`].
196    ///
197    /// Parent-child relationships are tracked inside the table to ensure that
198    /// a parent resource is not deleted while it has live children. This
199    /// allows child resources to hold "references" to a parent by table
200    /// index, to avoid needing e.g. an `Arc<Mutex<parent>>` and the associated
201    /// locking overhead and design issues, such as child existence extending
202    /// lifetime of parent referent even after parent resource is destroyed,
203    /// possibility for deadlocks.
204    ///
205    /// Parent-child relationships may not be modified once created. There
206    /// is no way to observe these relationships through the [`ResourceTable`]
207    /// methods except for erroring on deletion, or the [`std::fmt::Debug`]
208    /// impl.
209    pub fn push_child<T, U>(
210        &mut self,
211        entry: T,
212        parent: &Resource<U>,
213    ) -> Result<Resource<T>, ResourceTableError>
214    where
215        T: Send + 'static,
216        U: 'static,
217    {
218        let parent = parent.rep();
219        self.occupied(parent)?;
220        let child = self.push_(TableEntry::new(Box::new(entry), Some(parent)))?;
221        self.occupied_mut(parent)?.add_child(child);
222        Ok(Resource::new_own(child))
223    }
224
225    /// Get an immutable reference to a resource of a given type at a given
226    /// index.
227    ///
228    /// Multiple shared references can be borrowed at any given time.
229    pub fn get<T: Any + Sized>(&self, key: &Resource<T>) -> Result<&T, ResourceTableError> {
230        self.get_(key.rep())?
231            .downcast_ref()
232            .ok_or(ResourceTableError::WrongType)
233    }
234
235    fn get_(&self, key: u32) -> Result<&dyn Any, ResourceTableError> {
236        let r = self.occupied(key)?;
237        Ok(&*r.entry)
238    }
239
240    /// Get an mutable reference to a resource of a given type at a given
241    /// index.
242    pub fn get_mut<T: Any + Sized>(
243        &mut self,
244        key: &Resource<T>,
245    ) -> Result<&mut T, ResourceTableError> {
246        self.get_any_mut(key.rep())?
247            .downcast_mut()
248            .ok_or(ResourceTableError::WrongType)
249    }
250
251    /// Returns the raw `Any` at the `key` index provided.
252    pub fn get_any_mut(&mut self, key: u32) -> Result<&mut dyn Any, ResourceTableError> {
253        let r = self.occupied_mut(key)?;
254        Ok(&mut *r.entry)
255    }
256
257    /// Same as `delete`, but typed
258    pub fn delete<T>(&mut self, resource: Resource<T>) -> Result<T, ResourceTableError>
259    where
260        T: Any,
261    {
262        debug_assert!(resource.owned());
263        let entry = self.delete_entry(resource.rep())?;
264        match entry.entry.downcast() {
265            Ok(t) => Ok(*t),
266            Err(_e) => Err(ResourceTableError::WrongType),
267        }
268    }
269
270    fn delete_entry(&mut self, key: u32) -> Result<TableEntry, ResourceTableError> {
271        if !self.occupied(key)?.children.is_empty() {
272            return Err(ResourceTableError::HasChildren);
273        }
274        let e = self.free_entry(key as usize);
275        if let Some(parent) = e.parent {
276            // Remove deleted resource from parent's child list.
277            // Parent must still be present because it can't be deleted while still having
278            // children:
279            self.occupied_mut(parent)
280                .expect("missing parent")
281                .remove_child(key);
282        }
283        Ok(e)
284    }
285
286    /// Zip the values of the map with mutable references to table entries corresponding to each
287    /// key. As the keys in the `BTreeMap` are unique, this iterator can give mutable references
288    /// with the same lifetime as the mutable reference to the [ResourceTable].
289    pub fn iter_entries<'a, T>(
290        &'a mut self,
291        map: BTreeMap<u32, T>,
292    ) -> impl Iterator<Item = (Result<&'a mut dyn Any, ResourceTableError>, T)> {
293        map.into_iter().map(move |(k, v)| {
294            let item = self
295                .occupied_mut(k)
296                .map(|e| Box::as_mut(&mut e.entry))
297                // Safety: extending the lifetime of the mutable reference.
298                .map(|item| unsafe { &mut *(item as *mut dyn Any) });
299            (item, v)
300        })
301    }
302
303    /// Iterate over all children belonging to the provided parent
304    pub fn iter_children<T>(
305        &self,
306        parent: &Resource<T>,
307    ) -> Result<impl Iterator<Item = &(dyn Any + Send)> + use<'_, T>, ResourceTableError>
308    where
309        T: 'static,
310    {
311        let parent_entry = self.occupied(parent.rep())?;
312        Ok(parent_entry.children.iter().map(|child_index| {
313            let child = self.occupied(*child_index).expect("missing child");
314            child.entry.as_ref()
315        }))
316    }
317}
318
319impl Default for ResourceTable {
320    fn default() -> Self {
321        ResourceTable::new()
322    }
323}
324
325#[test]
326pub fn test_free_list() {
327    let mut table = ResourceTable::new();
328
329    let x = table.push(()).unwrap();
330    assert_eq!(x.rep(), 0);
331
332    let y = table.push(()).unwrap();
333    assert_eq!(y.rep(), 1);
334
335    // Deleting x should put it on the free list, so the next entry should have the same rep.
336    table.delete(x).unwrap();
337    let x = table.push(()).unwrap();
338    assert_eq!(x.rep(), 0);
339
340    // Deleting x and then y should yield indices 1 and then 0 for new entries.
341    table.delete(x).unwrap();
342    table.delete(y).unwrap();
343
344    let y = table.push(()).unwrap();
345    assert_eq!(y.rep(), 1);
346
347    let x = table.push(()).unwrap();
348    assert_eq!(x.rep(), 0);
349
350    // As the free list is empty, this entry will have a new id.
351    let x = table.push(()).unwrap();
352    assert_eq!(x.rep(), 2);
353}