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::{self, 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         Self {
176             entries: Vec::new(),
177             free_head: None,
178             debug: false,
179         }
180     }
181 
182     /// Returns whether or not this table is empty.
183     pub fn is_empty(&self) -> bool {
184         self.entries.iter().all(|entry| match entry {
185             Entry::Free { .. } => true,
186             Entry::Occupied { entry } => entry.entry.downcast_ref::<Tombstone>().is_some(),
187         })
188     }
189 
190     /// Enable or disable "debug mode".
191     ///
192     /// When this is enabled, the `delete` method will leave a tombstone in
193     /// place of the deleted item rather than add the entry to the free list.
194     /// This can help uncover "use-after-delete" or "double-delete" bugs which
195     /// might otherwise go unnoticed if an entry is repopulated.
196     pub fn enable_debug(&mut self, enable: bool) {
197         self.debug = enable;
198     }
199 
200     /// Inserts a new entry into this table, returning a corresponding
201     /// `TableId<T>` which can be used to refer to it after it was inserted.
202     pub fn push<T: Send + Sync + 'static>(&mut self, entry: T) -> Result<TableId<T>, TableError> {
203         let idx = self.push_(TableEntry::new(Box::new(entry), None))?;
204         Ok(TableId::new(idx))
205     }
206 
207     /// Pop an index off of the free list, if it's not empty.
208     fn pop_free_list(&mut self) -> Option<usize> {
209         if let Some(ix) = self.free_head {
210             // Advance free_head to the next entry if one is available.
211             match &self.entries[ix] {
212                 Entry::Free { next } => self.free_head = *next,
213                 Entry::Occupied { .. } => unreachable!(),
214             }
215             Some(ix)
216         } else {
217             None
218         }
219     }
220 
221     /// Free an entry in the table, returning its [`TableEntry`]. Add the index to the free list.
222     fn free_entry(&mut self, ix: usize) -> TableEntry {
223         if self.debug {
224             match std::mem::replace(
225                 &mut self.entries[ix],
226                 Entry::Occupied {
227                     entry: TableEntry {
228                         entry: Box::new(Tombstone),
229                         parent: None,
230                         children: BTreeSet::new(),
231                     },
232                 },
233             ) {
234                 Entry::Occupied { entry } => entry,
235                 Entry::Free { .. } => unreachable!(),
236             }
237         } else {
238             let entry = match std::mem::replace(
239                 &mut self.entries[ix],
240                 Entry::Free {
241                     next: self.free_head,
242                 },
243             ) {
244                 Entry::Occupied { entry } => entry,
245                 Entry::Free { .. } => unreachable!(),
246             };
247 
248             self.free_head = Some(ix);
249 
250             entry
251         }
252     }
253 
254     /// Push a new entry into the table, returning its handle. This will prefer to use free entries
255     /// if they exist, falling back on pushing new entries onto the end of the table.
256     fn push_(&mut self, e: TableEntry) -> Result<u32, TableError> {
257         if let Some(free) = self.pop_free_list() {
258             self.entries[free] = Entry::Occupied { entry: e };
259             Ok(u32::try_from(free).unwrap())
260         } else {
261             let ix = self
262                 .entries
263                 .len()
264                 .try_into()
265                 .map_err(|_| TableError::Full)?;
266             self.entries.push(Entry::Occupied { entry: e });
267             Ok(ix)
268         }
269     }
270 
271     fn occupied(&self, key: u32) -> Result<&TableEntry, TableError> {
272         self.entries
273             .get(key as usize)
274             .and_then(Entry::occupied)
275             .ok_or(TableError::NotPresent)
276     }
277 
278     fn occupied_mut(&mut self, key: u32) -> Result<&mut TableEntry, TableError> {
279         self.entries
280             .get_mut(key as usize)
281             .and_then(Entry::occupied_mut)
282             .ok_or(TableError::NotPresent)
283     }
284 
285     pub fn add_child<T, U>(
286         &mut self,
287         child: TableId<T>,
288         parent: TableId<U>,
289     ) -> Result<(), TableError> {
290         let entry = self.occupied_mut(child.rep())?;
291         assert!(entry.parent.is_none());
292         entry.parent = Some(parent.rep());
293         self.occupied_mut(parent.rep())?.add_child(child.rep());
294         Ok(())
295     }
296 
297     pub fn remove_child<T, U>(
298         &mut self,
299         child: TableId<T>,
300         parent: TableId<U>,
301     ) -> Result<(), TableError> {
302         let entry = self.occupied_mut(child.rep())?;
303         assert_eq!(entry.parent, Some(parent.rep()));
304         entry.parent = None;
305         self.occupied_mut(parent.rep())?.remove_child(child.rep());
306         Ok(())
307     }
308 
309     /// Get an immutable reference to a task of a given type at a given index.
310     ///
311     /// Multiple shared references can be borrowed at any given time.
312     pub fn get<T: 'static>(&self, key: TableId<T>) -> Result<&T, TableError> {
313         self.get_(key.rep())?
314             .downcast_ref()
315             .ok_or(TableError::WrongType)
316     }
317 
318     fn get_(&self, key: u32) -> Result<&dyn Any, TableError> {
319         let r = self.occupied(key)?;
320         Ok(&*r.entry)
321     }
322 
323     /// Get an mutable reference to a task of a given type at a given index.
324     pub fn get_mut<T: 'static>(&mut self, key: TableId<T>) -> Result<&mut T, TableError> {
325         self.get_mut_(key.rep())?
326             .downcast_mut()
327             .ok_or(TableError::WrongType)
328     }
329 
330     pub fn get_mut_(&mut self, key: u32) -> Result<&mut dyn Any, TableError> {
331         let r = self.occupied_mut(key)?;
332         Ok(&mut *r.entry)
333     }
334 
335     /// Delete the specified task
336     pub fn delete<T: 'static>(&mut self, key: TableId<T>) -> Result<T, TableError> {
337         self.delete_entry(key.rep())?
338             .entry
339             .downcast()
340             .map(|v| *v)
341             .map_err(|_| TableError::WrongType)
342     }
343 
344     fn delete_entry(&mut self, key: u32) -> Result<TableEntry, TableError> {
345         if !self.occupied(key)?.children.is_empty() {
346             return Err(TableError::HasChildren);
347         }
348         let e = self.free_entry(key as usize);
349         if let Some(parent) = e.parent {
350             // Remove deleted task from parent's child list.  Parent must still
351             // be present because it cant be deleted while still having
352             // children:
353             self.occupied_mut(parent)
354                 .expect("missing parent")
355                 .remove_child(key);
356         }
357         Ok(e)
358     }
359 }
360 
361 pub struct TableIterator(vec::IntoIter<Entry>);
362 
363 impl Iterator for TableIterator {
364     type Item = Box<dyn Any + Send + Sync>;
365 
366     fn next(&mut self) -> Option<Self::Item> {
367         loop {
368             if let Some(entry) = self.0.next() {
369                 if let Entry::Occupied { entry } = entry {
370                     break Some(entry.entry);
371                 }
372             } else {
373                 break None;
374             }
375         }
376     }
377 }
378 
379 impl IntoIterator for Table {
380     type Item = Box<dyn Any + Send + Sync>;
381     type IntoIter = TableIterator;
382 
383     fn into_iter(self) -> TableIterator {
384         TableIterator(self.entries.into_iter())
385     }
386 }
387 
388 impl fmt::Debug for Table {
389     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
390         write!(f, "[")?;
391         let mut wrote = false;
392         for (index, entry) in self.entries.iter().enumerate() {
393             if let Entry::Occupied { entry } = entry {
394                 if entry.entry.downcast_ref::<Tombstone>().is_none() {
395                     if wrote {
396                         write!(f, ", ")?;
397                     } else {
398                         wrote = true;
399                     }
400                     write!(f, "{index}")?;
401                 }
402             }
403         }
404         write!(f, "]")
405     }
406 }
407