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