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