1 // TODO: This duplicates a lot of resource_table.rs; consider reducing that
2 // duplication: https://github.com/bytecodealliance/wasmtime/issues/11190.
3 //
4 // The main difference between this and resource_table.rs is that the key type,
5 // `TableId<T>` implements `Copy`, making them much easier to work with than
6 // `Resource<T>`.  I've also added a `Table::delete_any` function, useful for
7 // implementing `subtask.drop`.
8 
9 use std::any::Any;
10 use std::boxed::Box;
11 use std::cmp::Ordering;
12 use std::collections::BTreeSet;
13 use std::fmt;
14 use std::hash::{Hash, Hasher};
15 use std::marker::PhantomData;
16 use std::vec::Vec;
17 
18 pub struct TableId<T> {
19     rep: u32,
20     _marker: PhantomData<fn() -> T>,
21 }
22 
23 pub trait TableDebug {
24     fn type_name() -> &'static str;
25 }
26 
27 impl<T: TableDebug> fmt::Debug for TableId<T> {
28     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
29         write!(f, "{}({})", T::type_name(), self.rep)
30     }
31 }
32 
33 impl<T> Hash for TableId<T> {
34     fn hash<H: Hasher>(&self, state: &mut H) {
35         self.rep.hash(state)
36     }
37 }
38 
39 impl<T> PartialEq for TableId<T> {
40     fn eq(&self, other: &Self) -> bool {
41         self.rep == other.rep
42     }
43 }
44 
45 impl<T> Eq for TableId<T> {}
46 
47 impl<T> PartialOrd for TableId<T> {
48     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
49         self.rep.partial_cmp(&other.rep)
50     }
51 }
52 
53 impl<T> Ord for TableId<T> {
54     fn cmp(&self, other: &Self) -> Ordering {
55         self.rep.cmp(&other.rep)
56     }
57 }
58 
59 impl<T> TableId<T> {
60     pub fn new(rep: u32) -> Self {
61         Self {
62             rep,
63             _marker: PhantomData,
64         }
65     }
66 }
67 
68 impl<T> Clone for TableId<T> {
69     fn clone(&self) -> Self {
70         Self::new(self.rep)
71     }
72 }
73 
74 impl<T> Copy for TableId<T> {}
75 
76 impl<T> TableId<T> {
77     pub fn rep(&self) -> u32 {
78         self.rep
79     }
80 }
81 
82 #[derive(Debug)]
83 /// Errors returned by operations on `Table`
84 pub enum TableError {
85     /// Table has no free keys
86     Full,
87     /// Entry not present in table
88     NotPresent,
89     /// Resource present in table, but with a different type
90     WrongType,
91     /// Entry cannot be deleted because child entrys exist in the table.
92     HasChildren,
93 }
94 
95 impl std::fmt::Display for TableError {
96     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97         match self {
98             Self::Full => write!(f, "table has no free keys"),
99             Self::NotPresent => write!(f, "entry not present"),
100             Self::WrongType => write!(f, "entry is of another type"),
101             Self::HasChildren => write!(f, "entry has children"),
102         }
103     }
104 }
105 impl std::error::Error for TableError {}
106 
107 /// The `Table` type maps a `TableId` to its entry.
108 #[derive(Default)]
109 pub struct Table {
110     entries: Vec<Entry>,
111     free_head: Option<usize>,
112     debug: bool,
113 }
114 
115 enum Entry {
116     Free { next: Option<usize> },
117     Occupied { entry: TableEntry },
118 }
119 
120 impl Entry {
121     pub fn occupied(&self) -> Option<&TableEntry> {
122         match self {
123             Self::Occupied { entry } => Some(entry),
124             Self::Free { .. } => None,
125         }
126     }
127 
128     pub fn occupied_mut(&mut self) -> Option<&mut TableEntry> {
129         match self {
130             Self::Occupied { entry } => Some(entry),
131             Self::Free { .. } => None,
132         }
133     }
134 }
135 
136 struct Tombstone;
137 
138 /// This structure tracks parent and child relationships for a given table entry.
139 ///
140 /// Parents and children are referred to by table index. We maintain the
141 /// following invariants to prevent orphans and cycles:
142 /// * parent can only be assigned on creating the entry.
143 /// * parent, if some, must exist when creating the entry.
144 /// * whenever a child is created, its index is added to children.
145 /// * whenever a child is deleted, its index is removed from children.
146 /// * an entry with children may not be deleted.
147 struct TableEntry {
148     /// The entry in the table
149     entry: Box<dyn Any + Send + Sync>,
150     /// The index of the parent of this entry, if it has one.
151     parent: Option<u32>,
152     /// The indicies of any children of this entry.
153     children: BTreeSet<u32>,
154 }
155 
156 impl TableEntry {
157     fn new(entry: Box<dyn Any + Send + Sync>, parent: Option<u32>) -> Self {
158         Self {
159             entry,
160             parent,
161             children: BTreeSet::new(),
162         }
163     }
164     fn add_child(&mut self, child: u32) {
165         assert!(self.children.insert(child));
166     }
167     fn remove_child(&mut self, child: u32) {
168         assert!(self.children.remove(&child));
169     }
170 }
171 
172 impl Table {
173     /// Create an empty table
174     pub fn new() -> Self {
175         let mut me = Self {
176             entries: Vec::new(),
177             free_head: None,
178             debug: false,
179         };
180 
181         // Reserve 0 as an invalid entry.
182         me.push(Tombstone).unwrap();
183 
184         me
185     }
186 
187     /// Returns whether or not this table is empty.
188     pub fn is_empty(&self) -> bool {
189         self.entries.iter().all(|entry| match entry {
190             Entry::Free { .. } => true,
191             Entry::Occupied { entry } => entry.entry.downcast_ref::<Tombstone>().is_some(),
192         })
193     }
194 
195     /// Enable or disable "debug mode".
196     ///
197     /// When this is enabled, the `delete` method will leave a tombstone in
198     /// place of the deleted item rather than add the entry to the free list.
199     /// This can help uncover "use-after-delete" or "double-delete" bugs which
200     /// might otherwise go unnoticed if an entry is repopulated.
201     pub fn enable_debug(&mut self, enable: bool) {
202         self.debug = enable;
203     }
204 
205     /// Inserts a new entry into this table, returning a corresponding
206     /// `TableId<T>` which can be used to refer to it after it was inserted.
207     pub fn push<T: Send + Sync + 'static>(&mut self, entry: T) -> Result<TableId<T>, TableError> {
208         let idx = self.push_(TableEntry::new(Box::new(entry), None))?;
209         Ok(TableId::new(idx))
210     }
211 
212     /// Pop an index off of the free list, if it's not empty.
213     fn pop_free_list(&mut self) -> Option<usize> {
214         if let Some(ix) = self.free_head {
215             // Advance free_head to the next entry if one is available.
216             match &self.entries[ix] {
217                 Entry::Free { next } => self.free_head = *next,
218                 Entry::Occupied { .. } => unreachable!(),
219             }
220             Some(ix)
221         } else {
222             None
223         }
224     }
225 
226     /// Free an entry in the table, returning its [`TableEntry`]. Add the index to the free list.
227     fn free_entry(&mut self, ix: usize) -> TableEntry {
228         if self.debug {
229             match std::mem::replace(
230                 &mut self.entries[ix],
231                 Entry::Occupied {
232                     entry: TableEntry {
233                         entry: Box::new(Tombstone),
234                         parent: None,
235                         children: BTreeSet::new(),
236                     },
237                 },
238             ) {
239                 Entry::Occupied { entry } => entry,
240                 Entry::Free { .. } => unreachable!(),
241             }
242         } else {
243             let entry = match std::mem::replace(
244                 &mut self.entries[ix],
245                 Entry::Free {
246                     next: self.free_head,
247                 },
248             ) {
249                 Entry::Occupied { entry } => entry,
250                 Entry::Free { .. } => unreachable!(),
251             };
252 
253             self.free_head = Some(ix);
254 
255             entry
256         }
257     }
258 
259     /// Push a new entry into the table, returning its handle. This will prefer to use free entries
260     /// if they exist, falling back on pushing new entries onto the end of the table.
261     fn push_(&mut self, e: TableEntry) -> Result<u32, TableError> {
262         if let Some(free) = self.pop_free_list() {
263             self.entries[free] = Entry::Occupied { entry: e };
264             Ok(u32::try_from(free).unwrap())
265         } else {
266             let ix = self
267                 .entries
268                 .len()
269                 .try_into()
270                 .map_err(|_| TableError::Full)?;
271             self.entries.push(Entry::Occupied { entry: e });
272             Ok(ix)
273         }
274     }
275 
276     fn occupied(&self, key: u32) -> Result<&TableEntry, TableError> {
277         self.entries
278             .get(key as usize)
279             .and_then(Entry::occupied)
280             .ok_or(TableError::NotPresent)
281     }
282 
283     fn occupied_mut(&mut self, key: u32) -> Result<&mut TableEntry, TableError> {
284         self.entries
285             .get_mut(key as usize)
286             .and_then(Entry::occupied_mut)
287             .ok_or(TableError::NotPresent)
288     }
289 
290     pub fn add_child<T, U>(
291         &mut self,
292         child: TableId<T>,
293         parent: TableId<U>,
294     ) -> Result<(), TableError> {
295         let entry = self.occupied_mut(child.rep())?;
296         assert!(entry.parent.is_none());
297         entry.parent = Some(parent.rep());
298         self.occupied_mut(parent.rep())?.add_child(child.rep());
299         Ok(())
300     }
301 
302     pub fn remove_child<T, U>(
303         &mut self,
304         child: TableId<T>,
305         parent: TableId<U>,
306     ) -> Result<(), TableError> {
307         let entry = self.occupied_mut(child.rep())?;
308         assert_eq!(entry.parent, Some(parent.rep()));
309         entry.parent = None;
310         self.occupied_mut(parent.rep())?.remove_child(child.rep());
311         Ok(())
312     }
313 
314     /// Get an immutable reference to a task of a given type at a given index.
315     ///
316     /// Multiple shared references can be borrowed at any given time.
317     pub fn get<T: 'static>(&self, key: TableId<T>) -> Result<&T, TableError> {
318         self.get_(key.rep())?
319             .downcast_ref()
320             .ok_or(TableError::WrongType)
321     }
322 
323     fn get_(&self, key: u32) -> Result<&dyn Any, TableError> {
324         let r = self.occupied(key)?;
325         Ok(&*r.entry)
326     }
327 
328     /// Get an mutable reference to a task of a given type at a given index.
329     pub fn get_mut<T: 'static>(&mut self, key: TableId<T>) -> Result<&mut T, TableError> {
330         self.get_mut_(key.rep())?
331             .downcast_mut()
332             .ok_or(TableError::WrongType)
333     }
334 
335     pub fn get_mut_(&mut self, key: u32) -> Result<&mut dyn Any, TableError> {
336         let r = self.occupied_mut(key)?;
337         Ok(&mut *r.entry)
338     }
339 
340     /// Delete the specified task
341     pub fn delete<T: 'static>(&mut self, key: TableId<T>) -> Result<T, TableError> {
342         self.delete_entry(key.rep())?
343             .entry
344             .downcast()
345             .map(|v| *v)
346             .map_err(|_| TableError::WrongType)
347     }
348 
349     fn delete_entry(&mut self, key: u32) -> Result<TableEntry, TableError> {
350         if !self.occupied(key)?.children.is_empty() {
351             return Err(TableError::HasChildren);
352         }
353         let e = self.free_entry(key as usize);
354         if let Some(parent) = e.parent {
355             // Remove deleted task from parent's child list.  Parent must still
356             // be present because it cant be deleted while still having
357             // children:
358             self.occupied_mut(parent)
359                 .expect("missing parent")
360                 .remove_child(key);
361         }
362         Ok(e)
363     }
364 
365     pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut (dyn Any + Send + Sync)> {
366         self.entries.iter_mut().filter_map(|entry| match entry {
367             Entry::Occupied { entry } => Some(&mut *entry.entry),
368             Entry::Free { .. } => None,
369         })
370     }
371 }
372 
373 impl fmt::Debug for Table {
374     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
375         write!(f, "[")?;
376         let mut wrote = false;
377         for (index, entry) in self.entries.iter().enumerate() {
378             if let Entry::Occupied { entry } = entry {
379                 if entry.entry.downcast_ref::<Tombstone>().is_none() {
380                     if wrote {
381                         write!(f, ", ")?;
382                     } else {
383                         wrote = true;
384                     }
385                     write!(f, "{index}")?;
386                 }
387             }
388         }
389         write!(f, "]")
390     }
391 }
392