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