1 use super::{TypedResource, TypedResourceIndex}; 2 use crate::{Result, bail}; 3 use alloc::vec::Vec; 4 use core::mem; 5 use wasmtime_environ::component::{TypeFutureTableIndex, TypeStreamTableIndex}; 6 7 /// The maximum handle value is specified in 8 /// <https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md> 9 /// currently and keeps the upper bits free for use in the component and ABI. 10 const MAX_HANDLE: u32 = 1 << 28; 11 12 /// Represents the state of a stream or future handle from the perspective of a 13 /// given component instance. 14 #[derive(Debug, Eq, PartialEq)] 15 pub enum TransmitLocalState { 16 /// The write end of the stream or future. 17 Write { 18 /// Whether the component instance has been notified that the stream or 19 /// future is "done" (i.e. the other end has dropped, or, in the case of 20 /// a future, a value has been transmitted). 21 done: bool, 22 }, 23 /// The read end of the stream or future. 24 Read { 25 /// Whether the component instance has been notified that the stream or 26 /// future is "done" (i.e. the other end has dropped, or, in the case of 27 /// a future, a value has been transmitted). 28 done: bool, 29 }, 30 /// A read or write is in progress. 31 Busy, 32 } 33 34 /// Return value from [`HandleTable::remove_resource`]. 35 pub enum RemovedResource { 36 /// An `own` resource was removed with the specified `rep` 37 Own { rep: u32 }, 38 /// A `borrow` resource was removed originally created within `scope`. 39 Borrow { scope: u32 }, 40 } 41 42 /// Different kinds of waitables returned by [`HandleTable::waitable_rep`]. 43 pub enum Waitable { 44 Subtask { is_host: bool }, 45 Future, 46 Stream, 47 } 48 49 #[derive(Debug)] 50 enum Slot { 51 Free { 52 next: u32, 53 }, 54 55 /// Represents an owned resource handle with the listed representation. 56 /// 57 /// The `lend_count` tracks how many times this has been lent out as a 58 /// `borrow` and if nonzero this can't be removed. 59 ResourceOwn { 60 resource: TypedResource, 61 lend_count: u32, 62 }, 63 64 /// Represents a borrowed resource handle connected to the `scope` 65 /// provided. 66 /// 67 /// The `rep` is listed and dropping this borrow will decrement the borrow 68 /// count of the `scope`. 69 ResourceBorrow { 70 resource: TypedResource, 71 scope: u32, 72 }, 73 74 /// Represents a host task handle. 75 HostTask { 76 rep: u32, 77 }, 78 79 /// Represents a guest task handle. 80 GuestTask { 81 rep: u32, 82 }, 83 84 /// Represents a guest thread handle. 85 #[cfg(feature = "component-model-async")] 86 GuestThread { 87 rep: u32, 88 }, 89 90 /// Represents a stream handle. 91 Stream { 92 ty: TypeStreamTableIndex, 93 rep: u32, 94 state: TransmitLocalState, 95 }, 96 97 /// Represents a future handle. 98 Future { 99 ty: TypeFutureTableIndex, 100 rep: u32, 101 state: TransmitLocalState, 102 }, 103 104 /// Represents a waitable-set handle. 105 WaitableSet { 106 rep: u32, 107 }, 108 109 /// Represents an error-context handle. 110 ErrorContext { 111 rep: u32, 112 }, 113 } 114 115 pub struct HandleTable { 116 next: u32, 117 slots: Vec<Slot>, 118 } 119 120 impl Default for HandleTable { default() -> Self121 fn default() -> Self { 122 Self { 123 next: 0, 124 slots: Vec::new(), 125 } 126 } 127 } 128 129 impl HandleTable { 130 /// Returns whether or not this table is empty. is_empty(&self) -> bool131 pub fn is_empty(&self) -> bool { 132 self.slots 133 .iter() 134 .all(|slot| matches!(slot, Slot::Free { .. })) 135 } 136 insert(&mut self, slot: Slot) -> Result<u32>137 fn insert(&mut self, slot: Slot) -> Result<u32> { 138 let next = self.next as usize; 139 if next == self.slots.len() { 140 self.slots.push(Slot::Free { 141 next: self.next.checked_add(1).unwrap(), 142 }); 143 } 144 let ret = self.next; 145 self.next = match mem::replace(&mut self.slots[next], slot) { 146 Slot::Free { next } => next, 147 _ => unreachable!(), 148 }; 149 // The component model reserves index 0 as never allocatable so add one 150 // to the table index to start the numbering at 1 instead. Also note 151 // that the component model places an upper-limit per-table on the 152 // maximum allowed index. 153 let ret = ret + 1; 154 if ret >= MAX_HANDLE { 155 bail!("cannot allocate another handle: index overflow"); 156 } 157 158 Ok(ret) 159 } 160 remove(&mut self, idx: u32) -> Result<()>161 fn remove(&mut self, idx: u32) -> Result<()> { 162 let to_fill = Slot::Free { next: self.next }; 163 let slot = self.get_mut(idx)?; 164 *slot = to_fill; 165 self.next = idx - 1; 166 Ok(()) 167 } 168 handle_index_to_table_index(&self, idx: u32) -> Option<usize>169 fn handle_index_to_table_index(&self, idx: u32) -> Option<usize> { 170 // NB: `idx` is decremented by one to account for the `+1` above during 171 // allocation. 172 let idx = idx.checked_sub(1)?; 173 usize::try_from(idx).ok() 174 } 175 get_mut(&mut self, idx: u32) -> Result<&mut Slot>176 fn get_mut(&mut self, idx: u32) -> Result<&mut Slot> { 177 let slot = self 178 .handle_index_to_table_index(idx) 179 .and_then(|i| self.slots.get_mut(i)); 180 match slot { 181 None | Some(Slot::Free { .. }) => bail!("unknown handle index {idx}"), 182 Some(slot) => Ok(slot), 183 } 184 } 185 186 /// Inserts a new `own` resource into this table whose type/rep are 187 /// specified by `resource`. resource_own_insert(&mut self, resource: TypedResource) -> Result<u32>188 pub fn resource_own_insert(&mut self, resource: TypedResource) -> Result<u32> { 189 self.insert(Slot::ResourceOwn { 190 resource, 191 lend_count: 0, 192 }) 193 } 194 195 /// Inserts a new `borrow` resource into this table whose type/rep are 196 /// specified by `resource`. The `scope` specified is used by 197 /// `CallContexts` to manage lending information. resource_borrow_insert(&mut self, resource: TypedResource, scope: u32) -> Result<u32>198 pub fn resource_borrow_insert(&mut self, resource: TypedResource, scope: u32) -> Result<u32> { 199 self.insert(Slot::ResourceBorrow { resource, scope }) 200 } 201 202 /// Returns the internal "rep" of the resource specified by `idx`. 203 /// 204 /// Returns an error if `idx` is out-of-bounds or doesn't point to a 205 /// resource of the appropriate type. resource_rep(&mut self, idx: TypedResourceIndex) -> Result<u32>206 pub fn resource_rep(&mut self, idx: TypedResourceIndex) -> Result<u32> { 207 match self.get_mut(idx.raw_index())? { 208 Slot::ResourceOwn { resource, .. } | Slot::ResourceBorrow { resource, .. } => { 209 resource.rep(&idx) 210 } 211 _ => bail!("index is not a resource"), 212 } 213 } 214 215 /// Accesses the "rep" of the resource pointed to by `idx` as part of a 216 /// lending operation. 217 /// 218 /// This will increase `lend_count` for owned resources and must be paired 219 /// with a `resource_undo_lend` below later on (managed by `CallContexts`). 220 /// 221 /// Upon success returns the "rep" plus whether the borrow came from an 222 /// `own` handle. resource_lend(&mut self, idx: TypedResourceIndex) -> Result<(u32, bool)>223 pub fn resource_lend(&mut self, idx: TypedResourceIndex) -> Result<(u32, bool)> { 224 match self.get_mut(idx.raw_index())? { 225 Slot::ResourceOwn { 226 resource, 227 lend_count, 228 } => { 229 let rep = resource.rep(&idx)?; 230 *lend_count = lend_count.checked_add(1).unwrap(); 231 Ok((rep, true)) 232 } 233 Slot::ResourceBorrow { resource, .. } => Ok((resource.rep(&idx)?, false)), 234 _ => bail!("index {} is not a resource", idx.raw_index()), 235 } 236 } 237 238 /// For `own` resources that were borrowed in `resource_lend`, undoes the 239 /// lending operation. resource_undo_lend(&mut self, idx: TypedResourceIndex) -> Result<()>240 pub fn resource_undo_lend(&mut self, idx: TypedResourceIndex) -> Result<()> { 241 match self.get_mut(idx.raw_index())? { 242 Slot::ResourceOwn { lend_count, .. } => { 243 *lend_count -= 1; 244 Ok(()) 245 } 246 _ => bail!("index {} is not an own resource", idx.raw_index()), 247 } 248 } 249 250 /// Removes the resource specified by `idx` from the table. 251 /// 252 /// This can fail if `idx` doesn't point to a resource, points to a 253 /// borrowed resource, or points to a resource of the wrong type. remove_resource(&mut self, idx: TypedResourceIndex) -> Result<RemovedResource>254 pub fn remove_resource(&mut self, idx: TypedResourceIndex) -> Result<RemovedResource> { 255 let ret = match self.get_mut(idx.raw_index())? { 256 Slot::ResourceOwn { 257 resource, 258 lend_count, 259 } => { 260 if *lend_count != 0 { 261 bail!("cannot remove owned resource while borrowed") 262 } 263 RemovedResource::Own { 264 rep: resource.rep(&idx)?, 265 } 266 } 267 Slot::ResourceBorrow { resource, scope } => { 268 // Ensure the drop is done with the right type 269 resource.rep(&idx)?; 270 RemovedResource::Borrow { scope: *scope } 271 } 272 _ => bail!("index {} is not a resource", idx.raw_index()), 273 }; 274 self.remove(idx.raw_index())?; 275 Ok(ret) 276 } 277 278 /// Inserts a readable-end stream of type `ty` and with the specified `rep` 279 /// into this table. 280 /// 281 /// Returns the table-local index of the stream. stream_insert_read(&mut self, ty: TypeStreamTableIndex, rep: u32) -> Result<u32>282 pub fn stream_insert_read(&mut self, ty: TypeStreamTableIndex, rep: u32) -> Result<u32> { 283 self.insert(Slot::Stream { 284 rep, 285 ty, 286 state: TransmitLocalState::Read { done: false }, 287 }) 288 } 289 290 /// Inserts a writable-end stream of type `ty` and with the specified `rep` 291 /// into this table. 292 /// 293 /// Returns the table-local index of the stream. stream_insert_write(&mut self, ty: TypeStreamTableIndex, rep: u32) -> Result<u32>294 pub fn stream_insert_write(&mut self, ty: TypeStreamTableIndex, rep: u32) -> Result<u32> { 295 self.insert(Slot::Stream { 296 rep, 297 ty, 298 state: TransmitLocalState::Write { done: false }, 299 }) 300 } 301 302 /// Returns the `rep` and `state` associated with the stream pointed to by 303 /// `idx`. 304 /// 305 /// Returns an error if `idx` is out-of-bounds or doesn't point to a stream 306 /// of type `ty`. stream_rep( &mut self, expected_ty: TypeStreamTableIndex, idx: u32, ) -> Result<(u32, &mut TransmitLocalState)>307 pub fn stream_rep( 308 &mut self, 309 expected_ty: TypeStreamTableIndex, 310 idx: u32, 311 ) -> Result<(u32, &mut TransmitLocalState)> { 312 match self.get_mut(idx)? { 313 Slot::Stream { rep, ty, state } => { 314 if *ty != expected_ty { 315 bail!("handle is a stream of a different type"); 316 } 317 Ok((*rep, state)) 318 } 319 _ => bail!("handle is not a stream"), 320 } 321 } 322 323 /// Removes the stream handle from `idx`, returning its `rep`. 324 /// 325 /// The stream must have the type `ty` and additionally be in a state 326 /// suitable for removal. 327 /// 328 /// Returns the `rep` for the stream along with whether the stream was 329 /// "done" or the writable end was witnessed as being done. stream_remove_readable( &mut self, expected_ty: TypeStreamTableIndex, idx: u32, ) -> Result<(u32, bool)>330 pub fn stream_remove_readable( 331 &mut self, 332 expected_ty: TypeStreamTableIndex, 333 idx: u32, 334 ) -> Result<(u32, bool)> { 335 let ret = match self.get_mut(idx)? { 336 Slot::Stream { rep, ty, state } => { 337 if *ty != expected_ty { 338 bail!("handle is a stream of a different type"); 339 } 340 let is_done = match state { 341 TransmitLocalState::Read { done } => *done, 342 TransmitLocalState::Write { .. } => { 343 bail!("handle is not a readable end of a stream") 344 } 345 TransmitLocalState::Busy => bail!("cannot remove busy stream"), 346 }; 347 (*rep, is_done) 348 } 349 _ => bail!("handle is not a stream"), 350 }; 351 self.remove(idx)?; 352 Ok(ret) 353 } 354 355 /// Removes the writable stream handle from `idx`, returning its `rep`. stream_remove_writable( &mut self, expected_ty: TypeStreamTableIndex, idx: u32, ) -> Result<u32>356 pub fn stream_remove_writable( 357 &mut self, 358 expected_ty: TypeStreamTableIndex, 359 idx: u32, 360 ) -> Result<u32> { 361 let ret = match self.get_mut(idx)? { 362 Slot::Stream { rep, ty, state } => { 363 if *ty != expected_ty { 364 bail!("handle is a stream of a different type"); 365 } 366 match state { 367 TransmitLocalState::Write { .. } => {} 368 TransmitLocalState::Read { .. } => { 369 bail!("passed read end to `stream.drop-writable`") 370 } 371 TransmitLocalState::Busy => bail!("cannot drop busy stream"), 372 } 373 *rep 374 } 375 _ => bail!("handle is not a stream"), 376 }; 377 self.remove(idx)?; 378 Ok(ret) 379 } 380 381 /// Inserts a readable-end future of type `ty` and with the specified `rep` 382 /// into this table. 383 /// 384 /// Returns the table-local index of the future. future_insert_read(&mut self, ty: TypeFutureTableIndex, rep: u32) -> Result<u32>385 pub fn future_insert_read(&mut self, ty: TypeFutureTableIndex, rep: u32) -> Result<u32> { 386 self.insert(Slot::Future { 387 rep, 388 ty, 389 state: TransmitLocalState::Read { done: false }, 390 }) 391 } 392 393 /// Inserts a writable-end future of type `ty` and with the specified `rep` 394 /// into this table. 395 /// 396 /// Returns the table-local index of the future. future_insert_write(&mut self, ty: TypeFutureTableIndex, rep: u32) -> Result<u32>397 pub fn future_insert_write(&mut self, ty: TypeFutureTableIndex, rep: u32) -> Result<u32> { 398 self.insert(Slot::Future { 399 rep, 400 ty, 401 state: TransmitLocalState::Write { done: false }, 402 }) 403 } 404 405 /// Returns the `rep` and `state` associated with the future pointed to by 406 /// `idx`. 407 /// 408 /// Returns an error if `idx` is out-of-bounds or doesn't point to a future 409 /// of type `ty`. future_rep( &mut self, expected_ty: TypeFutureTableIndex, idx: u32, ) -> Result<(u32, &mut TransmitLocalState)>410 pub fn future_rep( 411 &mut self, 412 expected_ty: TypeFutureTableIndex, 413 idx: u32, 414 ) -> Result<(u32, &mut TransmitLocalState)> { 415 match self.get_mut(idx)? { 416 Slot::Future { rep, ty, state } => { 417 if *ty != expected_ty { 418 bail!("handle is a future of a different type"); 419 } 420 Ok((*rep, state)) 421 } 422 _ => bail!("handle is not a future"), 423 } 424 } 425 426 /// Removes the future handle from `idx`, returning its `rep`. 427 /// 428 /// The future must have the type `ty` and additionally be in a state 429 /// suitable for removal. 430 /// 431 /// Returns the `rep` for the future along with whether the future was 432 /// "done" or the writable end was witnessed as being done. future_remove_readable( &mut self, expected_ty: TypeFutureTableIndex, idx: u32, ) -> Result<(u32, bool)>433 pub fn future_remove_readable( 434 &mut self, 435 expected_ty: TypeFutureTableIndex, 436 idx: u32, 437 ) -> Result<(u32, bool)> { 438 let ret = match self.get_mut(idx)? { 439 Slot::Future { rep, ty, state } => { 440 if *ty != expected_ty { 441 bail!("handle is a future of a different type"); 442 } 443 let is_done = match state { 444 TransmitLocalState::Read { done } => *done, 445 TransmitLocalState::Write { .. } => { 446 bail!("handle is not a readable end of a future") 447 } 448 TransmitLocalState::Busy => bail!("cannot remove busy future"), 449 }; 450 (*rep, is_done) 451 } 452 _ => bail!("handle is not a future"), 453 }; 454 self.remove(idx)?; 455 Ok(ret) 456 } 457 458 /// Removes the writable future handle from `idx`, returning its `rep`. future_remove_writable( &mut self, expected_ty: TypeFutureTableIndex, idx: u32, ) -> Result<u32>459 pub fn future_remove_writable( 460 &mut self, 461 expected_ty: TypeFutureTableIndex, 462 idx: u32, 463 ) -> Result<u32> { 464 let ret = match self.get_mut(idx)? { 465 Slot::Future { rep, ty, state } => { 466 if *ty != expected_ty { 467 bail!("handle is a future of a different type"); 468 } 469 match state { 470 TransmitLocalState::Write { .. } => {} 471 TransmitLocalState::Read { .. } => { 472 bail!("passed read end to `future.drop-writable`") 473 } 474 TransmitLocalState::Busy => bail!("cannot drop busy future"), 475 } 476 *rep 477 } 478 _ => bail!("handle is not a future"), 479 }; 480 self.remove(idx)?; 481 Ok(ret) 482 } 483 484 /// Inserts the error-context `rep` into this table, returning the index it 485 /// now resides at. error_context_insert(&mut self, rep: u32) -> Result<u32>486 pub fn error_context_insert(&mut self, rep: u32) -> Result<u32> { 487 self.insert(Slot::ErrorContext { rep }) 488 } 489 490 /// Returns the `rep` of an error-context pointed to by `idx`. error_context_rep(&mut self, idx: u32) -> Result<u32>491 pub fn error_context_rep(&mut self, idx: u32) -> Result<u32> { 492 match self.get_mut(idx)? { 493 Slot::ErrorContext { rep } => Ok(*rep), 494 _ => bail!("handle is not an error-context"), 495 } 496 } 497 498 /// Drops the error-context pointed to by `idx`. 499 /// 500 /// Returns the internal `rep`. error_context_drop(&mut self, idx: u32) -> Result<u32>501 pub fn error_context_drop(&mut self, idx: u32) -> Result<u32> { 502 let rep = match self.get_mut(idx)? { 503 Slot::ErrorContext { rep } => *rep, 504 _ => bail!("handle is not an error-context"), 505 }; 506 self.remove(idx)?; 507 Ok(rep) 508 } 509 510 /// Inserts `rep` as a guest subtask into this table. subtask_insert_guest(&mut self, rep: u32) -> Result<u32>511 pub fn subtask_insert_guest(&mut self, rep: u32) -> Result<u32> { 512 self.insert(Slot::GuestTask { rep }) 513 } 514 515 /// Inserts `rep` as a host subtask into this table. subtask_insert_host(&mut self, rep: u32) -> Result<u32>516 pub fn subtask_insert_host(&mut self, rep: u32) -> Result<u32> { 517 self.insert(Slot::HostTask { rep }) 518 } 519 520 /// Returns the `rep` of the subtask at `idx` as well as if it's a host 521 /// task or not. subtask_rep(&mut self, idx: u32) -> Result<(u32, bool)>522 pub fn subtask_rep(&mut self, idx: u32) -> Result<(u32, bool)> { 523 match self.get_mut(idx)? { 524 Slot::GuestTask { rep } => Ok((*rep, false)), 525 Slot::HostTask { rep } => Ok((*rep, true)), 526 _ => bail!("handle is not a subtask"), 527 } 528 } 529 530 /// Removes the subtask set at `idx`, returning its `rep`. subtask_remove(&mut self, idx: u32) -> Result<(u32, bool)>531 pub fn subtask_remove(&mut self, idx: u32) -> Result<(u32, bool)> { 532 let ret = match self.get_mut(idx)? { 533 Slot::GuestTask { rep } => (*rep, false), 534 Slot::HostTask { rep } => (*rep, true), 535 _ => bail!("handle is not a subtask"), 536 }; 537 self.remove(idx)?; 538 Ok(ret) 539 } 540 541 /// Inserts `rep` as a waitable set into this table. waitable_set_insert(&mut self, rep: u32) -> Result<u32>542 pub fn waitable_set_insert(&mut self, rep: u32) -> Result<u32> { 543 self.insert(Slot::WaitableSet { rep }) 544 } 545 546 /// Returns the `rep` of an waitable-set pointed to by `idx`. waitable_set_rep(&mut self, idx: u32) -> Result<u32>547 pub fn waitable_set_rep(&mut self, idx: u32) -> Result<u32> { 548 match self.get_mut(idx)? { 549 Slot::WaitableSet { rep, .. } => Ok(*rep), 550 _ => bail!("handle is not an waitable-set"), 551 } 552 } 553 554 /// Removes the waitable set at `idx`, returning its `rep`. waitable_set_remove(&mut self, idx: u32) -> Result<u32>555 pub fn waitable_set_remove(&mut self, idx: u32) -> Result<u32> { 556 let ret = match self.get_mut(idx)? { 557 Slot::WaitableSet { rep } => *rep, 558 _ => bail!("handle is not a waitable-set"), 559 }; 560 self.remove(idx)?; 561 Ok(ret) 562 } 563 564 /// Returns the `rep` for the waitable specified by `idx` along with what 565 /// kind of waitable it is. waitable_rep(&mut self, idx: u32) -> Result<(u32, Waitable)>566 pub fn waitable_rep(&mut self, idx: u32) -> Result<(u32, Waitable)> { 567 match self.get_mut(idx)? { 568 Slot::GuestTask { rep } => Ok((*rep, Waitable::Subtask { is_host: false })), 569 Slot::HostTask { rep } => Ok((*rep, Waitable::Subtask { is_host: true })), 570 Slot::Future { rep, .. } => Ok((*rep, Waitable::Future)), 571 Slot::Stream { rep, .. } => Ok((*rep, Waitable::Stream)), 572 _ => bail!("handle is not a waitable"), 573 } 574 } 575 } 576 577 #[derive(Default)] 578 #[cfg(feature = "component-model-async")] 579 pub struct ThreadHandleTable(HandleTable); 580 581 #[cfg(feature = "component-model-async")] 582 impl ThreadHandleTable { 583 /// Inserts the guest thread `rep` into this table, returning the index it 584 /// now resides at. guest_thread_insert(&mut self, rep: u32) -> Result<u32>585 pub fn guest_thread_insert(&mut self, rep: u32) -> Result<u32> { 586 self.0.insert(Slot::GuestThread { rep }) 587 } 588 589 /// Returns the `rep` of a guest thread pointed to by `idx`. guest_thread_rep(&mut self, idx: u32) -> Result<u32>590 pub fn guest_thread_rep(&mut self, idx: u32) -> Result<u32> { 591 match self.0.get_mut(idx)? { 592 Slot::GuestThread { rep } => Ok(*rep), 593 _ => bail!("handle is not a guest thread"), 594 } 595 } 596 597 /// Removes the guest thread pointed to by `idx`. 598 /// 599 /// Returns the internal `rep`. guest_thread_remove(&mut self, idx: u32) -> Result<u32>600 pub fn guest_thread_remove(&mut self, idx: u32) -> Result<u32> { 601 let rep = match self.0.get_mut(idx)? { 602 Slot::GuestThread { rep } => *rep, 603 _ => bail!("handle is not a guest thread"), 604 }; 605 self.0.remove(idx)?; 606 Ok(rep) 607 } 608 } 609