Skip to main content

wasmtime/runtime/vm/gc/
gc_runtime.rs

1//! Traits for abstracting over our different garbage collectors.
2
3use crate::bail_bug;
4use crate::prelude::*;
5use crate::runtime::vm::{
6    ExternRefHostDataId, GcHeapObject, GcStoreTraceState, SendSyncPtr, TypedGcRef, VMArrayRef,
7    VMExternRef, VMGcHeader, VMGcObjectData, VMGcRef, ValRaw,
8};
9use crate::store::Asyncness;
10use crate::vm::VMMemoryDefinition;
11use core::ptr::NonNull;
12use core::slice;
13use core::{alloc::Layout, any::Any, marker, mem, ops::Range, ptr};
14use wasmtime_environ::{GcArrayLayout, GcStructLayout, GcTypeLayouts, VMSharedTypeIndex};
15
16/// Trait for integrating a garbage collector with the runtime.
17///
18/// This trait is responsible for:
19///
20/// * GC barriers used by runtime code (as opposed to compiled Wasm code)
21///
22/// * Creating and managing GC heaps for individual stores
23///
24/// * Running garbage collection
25///
26/// # Safety
27///
28/// The collector, its GC heaps, and GC barriers when taken together as a whole
29/// must be safe. Additionally, they must work with the GC barriers emitted into
30/// compiled Wasm code via the collector's corresponding `GcCompiler`
31/// implementation. That is, if callers only call safe methods on this trait
32/// (while pairing it with its associated `GcCompiler`, `GcHeap`, and etc...)
33/// and uphold all the documented safety invariants of this trait's unsafe
34/// methods, then it must be impossible for callers to violate memory
35/// safety. Implementations of this trait may not add new safety invariants, not
36/// already documented in this trait's interface, that callers need to uphold.
37pub unsafe trait GcRuntime: 'static + Send + Sync {
38    /// Get this collector's GC type layouts.
39    fn layouts(&self) -> &dyn GcTypeLayouts;
40
41    /// Construct a new GC heap.
42    #[cfg(feature = "gc")]
43    fn new_gc_heap(&self, engine: &crate::Engine) -> Result<Box<dyn GcHeap>>;
44}
45
46/// A heap that manages garbage-collected objects.
47///
48/// Each `wasmtime::Store` is associated with a single `GcHeap`, and a `GcHeap`
49/// is only ever used with one store at a time, but `GcHeap`s may be reused with
50/// new stores after its original store is dropped. The `reset` method will be
51/// called in between each such reuse. (This reuse allows for better integration
52/// with the pooling allocator).
53///
54/// If a `GcHeap` mapped any memory, its `Drop` implementation should unmap that
55/// memory.
56///
57/// # Safety
58///
59/// The trait methods below are all safe: implementations of this trait must
60/// ensure that these methods cannot be misused to create memory unsafety. The
61/// expectation is that -- given that `VMGcRef` is a newtype over an index --
62/// implementations perform similar tricks as Wasm linear memory
63/// implementations. The heap should internally be a contiguous region of memory
64/// and `VMGcRef` indices into the heap must be bounds checked (explicitly or
65/// implicitly via virtual memory tricks).
66///
67/// Furthermore, if heap corruption occurs because (for example) a `VMGcRef`
68/// from a different heap is used with this heap, then that corruption must be
69/// limited to within this heap. Every heap is a mini sandbox. It follows that
70/// native pointers should never be written into or read out from the GC heap,
71/// since that could spread corruption from inside the GC heap out to the native
72/// host heap. The host data for an `externref`, therefore, is stored in a side
73/// table (`ExternRefHostDataTable`) and never inside the heap. Only an id
74/// referencing a slot in that table should ever be written into the GC heap.
75///
76/// These constraints give us great amounts of safety compared to working with
77/// raw pointers. The worst that could happen is corruption local to heap and a
78/// panic, or perhaps reading stale heap data from a previous Wasm instance. A
79/// corrupt `GcHeap` can *never* result in the native host's corruption.
80///
81/// The downside is that we are introducing `heap_base + index` computations and
82/// bounds checking to access GC memory, adding performance overhead. This is
83/// deemed to be a worthy trade off. Furthermore, it isn't even a clear cut
84/// performance degradation since this allows us to use 32-bit "pointers",
85/// giving us more compact data representations and the improved cache
86/// utilization that implies.
87pub unsafe trait GcHeap: 'static + Send + Sync {
88    ////////////////////////////////////////////////////////////////////////////
89    // Life Cycle GC Heap Methods
90
91    /// Is this GC heap currently attached to a memory?
92    fn is_attached(&self) -> bool;
93
94    /// Attach this GC heap to a memory.
95    ///
96    /// Once attached, this GC heap can be used with Wasm.
97    fn attach(&mut self, memory: crate::vm::Memory);
98
99    /// Reset this heap.
100    ///
101    /// Calling this method unassociates this heap with the store that it has
102    /// been associated with, making it available to be associated with a new
103    /// heap.
104    ///
105    /// This should refill free lists, reset bump pointers, and etc... as if
106    /// nothing were allocated in this heap (because nothing is allocated in
107    /// this heap anymore).
108    ///
109    /// This should retain any allocated memory from the global allocator and
110    /// any virtual memory mappings.
111    fn detach(&mut self) -> crate::vm::Memory;
112
113    ////////////////////////////////////////////////////////////////////////////
114    // `Any` methods
115
116    /// Get this heap as an `&Any`.
117    fn as_any(&self) -> &dyn Any;
118
119    /// Get this heap as an `&mut Any`.
120    fn as_any_mut(&mut self) -> &mut dyn Any;
121
122    ////////////////////////////////////////////////////////////////////////////
123    // No-GC Scope Methods
124
125    /// Enter a no-GC scope.
126    ///
127    /// Calling the `gc` method when we are inside a no-GC scope should panic.
128    ///
129    /// We can enter multiple, nested no-GC scopes and this method should
130    /// account for that.
131    fn enter_no_gc_scope(&mut self);
132
133    /// Exit a no-GC scope.
134    ///
135    /// Dual to `enter_no_gc_scope`.
136    fn exit_no_gc_scope(&mut self);
137
138    ////////////////////////////////////////////////////////////////////////////
139    // GC Barriers
140
141    /// Read barrier called every time the runtime clones a GC reference.
142    ///
143    /// Callers should pass a valid `VMGcRef` that belongs to the given
144    /// heap. Failure to do so is memory safe, but may result in general
145    /// failures such as panics or incorrect results.
146    fn clone_gc_ref(&mut self, gc_ref: &VMGcRef) -> VMGcRef;
147
148    /// Write barrier called whenever the runtime is nulling out a GC reference.
149    ///
150    /// Default implemented in terms of the `write_gc_ref` barrier.
151    ///
152    /// If an `externref` is reclaimed, then its associated entry in the
153    /// `host_data_table` should be removed.
154    ///
155    /// Callers should pass a valid `VMGcRef` that belongs to the given
156    /// heap. Failure to do so is memory safe, but may result in general
157    /// failures such as panics or incorrect results.
158    ///
159    /// The given `gc_ref` should not be used again.
160    fn drop_gc_ref(&mut self, gc_ref: VMGcRef) {
161        let mut dest = Some(gc_ref);
162
163        // Similar to `clone_gc_ref` not being fallible this method,
164        // `drop_gc_ref`, is also not fallible as it's seen as too painful to
165        // propagate this result. The consequence is that if the GC heap is
166        // corrupted at this point it'll get a ltitle more corrupted from this
167        // operation, but that's the tradeoff we're making ignoring the error
168        // here.
169        if let Err(e) = self.write_gc_ref(&mut dest, None) {
170            if cfg!(debug_assertions) {
171                panic!("heap corruption detected: {e}");
172            }
173        }
174    }
175
176    /// Write barrier called every time the runtime overwrites a GC reference.
177    ///
178    /// The `source` is a borrowed GC reference, and should not have been cloned
179    /// already for this write operation. This allows implementations to fuse
180    /// the `source`'s read barrier into this write barrier.
181    ///
182    /// If an `externref` is reclaimed, then its associated entry in the
183    /// `host_data_table` should be removed.
184    ///
185    /// Callers should pass a valid `VMGcRef` that belongs to the given heap for
186    /// both the `source` and `destination`. Failure to do so is memory safe,
187    /// but may result in general failures such as panics or incorrect results.
188    fn write_gc_ref(
189        &mut self,
190        destination: &mut Option<VMGcRef>,
191        source: Option<&VMGcRef>,
192    ) -> Result<()>;
193
194    /// Read barrier called whenever a GC reference is passed from the runtime
195    /// to Wasm: an argument to a host-to-Wasm call, or a return from a
196    /// Wasm-to-host call.
197    ///
198    /// Callers should pass a valid `VMGcRef` that belongs to the given
199    /// heap. Failure to do so is memory safe, but may result in general
200    /// failures such as panics or incorrect results.
201    fn expose_gc_ref_to_wasm(&mut self, gc_ref: VMGcRef) -> Result<()>;
202
203    ////////////////////////////////////////////////////////////////////////////
204    // `externref` Methods
205
206    /// Allocate a `VMExternRef` with space for host data described by the given
207    /// layout.
208    ///
209    /// Return values:
210    ///
211    /// * `Ok(Ok(_))`: The allocation was successful.
212    ///
213    /// * `Ok(Err(n))`: There is currently not enough available space for this
214    ///   allocation of size `n`. The caller should either grow the heap or run
215    ///   a collection to reclaim space, and then try allocating again.
216    ///
217    /// * `Err(_)`: The collector cannot satisfy this allocation request, and
218    ///   would not be able to even after the caller were to trigger a
219    ///   collection. This could be because, for example, the requested
220    ///   allocation is larger than this collector's implementation limit for
221    ///   object size.
222    fn alloc_externref(
223        &mut self,
224        host_data: ExternRefHostDataId,
225    ) -> Result<Result<VMExternRef, u64>>;
226
227    /// Get the host data ID associated with the given `externref`.
228    ///
229    /// Callers should pass a valid `externref` that belongs to the given
230    /// heap. Failure to do so is memory safe, but may result in general
231    /// failures such as panics or incorrect results.
232    fn externref_host_data(&self, externref: &VMExternRef) -> Result<ExternRefHostDataId>;
233
234    ////////////////////////////////////////////////////////////////////////////
235    // Struct, array, and general GC object methods
236
237    /// Get the header of the object that `gc_ref` points to.
238    fn header(&self, gc_ref: &VMGcRef) -> Result<&VMGcHeader>;
239
240    /// Get the header of the object that `gc_ref` points to.
241    fn header_mut(&mut self, gc_ref: &VMGcRef) -> Result<&mut VMGcHeader>;
242
243    /// Get the size (in bytes) of the object referenced by `gc_ref`.
244    ///
245    /// # Errors
246    ///
247    /// Returns an error on out of bounds or if the `gc_ref` is an `i31ref`.
248    fn object_size(&self, gc_ref: &VMGcRef) -> Result<usize>;
249
250    /// Allocate a raw, uninitialized GC-managed object with the given header
251    /// and layout.
252    ///
253    /// The object's fields and elements are left uninitialized. It is the
254    /// caller's responsibility to initialize them before exposing the struct to
255    /// Wasm or triggering a GC.
256    ///
257    /// The header's described type and layout must match *for this
258    /// collector*. That is, if this collector adds an extra header word to all
259    /// objects, the given layout must already include space for that header
260    /// word. Therefore, this method is effectively only usable with layouts
261    /// derived from a `Gc{Struct,Array}Layout` returned by this collector.
262    ///
263    /// Failure to uphold any of the above is memory safe, but may result in
264    /// general failures such as panics or incorrect results.
265    ///
266    /// Return values:
267    ///
268    /// * `Ok(Ok(_))`: The allocation was successful.
269    ///
270    /// * `Ok(Err(n))`: There is currently not enough available space for this
271    ///   allocation of size `n`. The caller should either grow the heap or run
272    ///   a collection to reclaim space, and then try allocating again.
273    ///
274    /// * `Err(_)`: The collector cannot satisfy this allocation request, and
275    ///   would not be able to even after the caller were to trigger a
276    ///   collection. This could be because, for example, the requested
277    ///   alignment is larger than this collector's implementation limit.
278    fn alloc_raw(&mut self, header: VMGcHeader, layout: Layout) -> Result<Result<VMGcRef, u64>>;
279
280    /// Allocate a GC-managed struct of the given type and layout.
281    ///
282    /// The struct's fields are left uninitialized. It is the caller's
283    /// responsibility to initialize them before exposing the struct to Wasm or
284    /// triggering a GC.
285    ///
286    /// The `ty` and `layout` must match.
287    ///
288    /// Failure to do either of the above is memory safe, but may result in
289    /// general failures such as panics or incorrect results.
290    ///
291    /// Return values:
292    ///
293    /// * `Ok(Ok(_))`: The allocation was successful.
294    ///
295    /// * `Ok(Err(n))`: There is currently not enough available space for this
296    ///   allocation of size `n`. The caller should either grow the heap or run
297    ///   a collection to reclaim space, and then try allocating again.
298    ///
299    /// * `Err(_)`: The collector cannot satisfy this allocation request, and
300    ///   would not be able to even after the caller were to trigger a
301    ///   collection. This could be because, for example, the requested
302    ///   allocation is larger than this collector's implementation limit for
303    ///   object size.
304    fn alloc_uninit_struct_or_exn(
305        &mut self,
306        ty: VMSharedTypeIndex,
307        layout: &GcStructLayout,
308    ) -> Result<Result<VMGcRef, u64>>;
309
310    /// Deallocate an uninitialized, GC-managed struct or exception.
311    ///
312    /// This is useful for if initialization of the struct's fields fails, so
313    /// that the struct's allocation can be eagerly reclaimed, and so that the
314    /// collector doesn't attempt to treat any of the uninitialized fields as
315    /// valid GC references, or something like that.
316    fn dealloc_uninit_struct_or_exn(&mut self, structref: VMGcRef) -> Result<()>;
317
318    /// * `Ok(Ok(_))`: The allocation was successful.
319    ///
320    /// * `Ok(Err(n))`: There is currently not enough available space for this
321    ///   allocation of size `n`. The caller should either grow the heap or run
322    ///   a collection to reclaim space, and then try allocating again.
323    ///
324    /// * `Err(_)`: The collector cannot satisfy this allocation request, and
325    ///   would not be able to even after the caller were to trigger a
326    ///   collection. This could be because, for example, the requested
327    ///   allocation is larger than this collector's implementation limit for
328    ///   object size.
329    fn alloc_uninit_array(
330        &mut self,
331        ty: VMSharedTypeIndex,
332        len: u32,
333        layout: &GcArrayLayout,
334    ) -> Result<Result<VMArrayRef, u64>>;
335
336    /// Deallocate an uninitialized, GC-managed array.
337    ///
338    /// This is useful for if initialization of the array's fields fails, so
339    /// that the array's allocation can be eagerly reclaimed, and so that the
340    /// collector doesn't attempt to treat any of the uninitialized fields as
341    /// valid GC references, or something like that.
342    fn dealloc_uninit_array(&mut self, arrayref: VMArrayRef) -> Result<()>;
343
344    /// Get the length of the given array.
345    ///
346    /// Returns an error on out-of-bounds accesses.
347    ///
348    /// The given `arrayref` should be valid and of the given size. Failure to
349    /// do so is memory safe, but may result in general failures such as panics
350    /// or incorrect results.
351    fn array_len(&self, arrayref: &VMArrayRef) -> Result<u32>;
352
353    ////////////////////////////////////////////////////////////////////////////
354    // Garbage Collection Methods
355
356    /// Get the total number of bytes currently allocated (live or
357    /// dead-but-not-collected) in this heap.
358    ///
359    /// This is distinct from the heap capacity.
360    fn allocated_bytes(&self) -> usize;
361
362    /// Whether a GC should be performed before the next heap growth.
363    ///
364    /// Some collectors (e.g. the copying collector) need to perform a GC before
365    /// growing the heap in certain states, to ensure that the semi-spaces remain
366    /// properly balanced.
367    ///
368    /// Defaults to `false`.
369    fn needs_gc_before_next_growth(&self) -> bool {
370        false
371    }
372
373    /// Start a new garbage collection process.
374    ///
375    /// The given `roots` are GC roots and should not be collected (nor anything
376    /// transitively reachable from them).
377    ///
378    /// Upon reclaiming an `externref`, its associated entry in the
379    /// `host_data_table` is removed.
380    ///
381    /// Callers should pass valid GC roots that belongs to this heap, and the
382    /// host data table associated with this heap's `externref`s. Failure to do
383    /// so is memory safe, but may result in general failures such as panics or
384    /// incorrect results.
385    ///
386    /// This method should panic if we are in a no-GC scope.
387    fn gc<'a, 'b>(
388        &'a mut self,
389        roots: GcRootsIter<'a>,
390        trace_state: &'a mut GcStoreTraceState<'b>,
391    ) -> Box<dyn GarbageCollection + 'a>
392    where
393        'b: 'a;
394
395    ////////////////////////////////////////////////////////////////////////////
396    // JIT-Code Interaction Methods
397
398    /// Get the pointer that will be stored in the `VMContext::gc_heap_data`
399    /// field and be accessible from JIT code via collaboration with the
400    /// corresponding `GcCompiler` trait.
401    ///
402    /// # Safety
403    ///
404    /// The returned pointer, if any, must remain valid as long as `self` is not
405    /// dropped.
406    unsafe fn vmctx_gc_heap_data(&self) -> NonNull<u8>;
407
408    ////////////////////////////////////////////////////////////////////////////
409    // Accessors for the raw bytes of the GC heap
410
411    /// Take the underlying memory storage out of this GC heap.
412    ///
413    /// # Panics
414    ///
415    /// If this GC heap is used while the memory is taken then a panic will
416    /// occur. This will also panic if the memory is already taken.
417    fn take_memory(&mut self) -> crate::vm::Memory;
418
419    /// Replace this GC heap's underlying memory storage.
420    ///
421    /// # Safety
422    ///
423    /// The `memory` must have been taken via `take_memory` and the GC heap must
424    /// not have been used at all since the memory was taken. The memory must be
425    /// the same size or larger than it was.
426    unsafe fn replace_memory(&mut self, memory: crate::vm::Memory, delta_bytes_grown: u64);
427
428    /// Get a raw `VMMemoryDefinition` for this heap's underlying memory storage.
429    ///
430    /// If/when exposing this `VMMemoryDefinition` to Wasm, it is your
431    /// responsibility to ensure that you do not do that in such a way as to
432    /// violate Rust's borrowing rules (e.g. make sure there is no active
433    /// `heap_slice_mut()` call at the same time) and that if this GC heap is
434    /// resized (and its base potentially moves) then that Wasm gets a new,
435    /// updated `VMMemoryDefinition` record.
436    fn vmmemory(&self) -> VMMemoryDefinition;
437
438    /// Get a slice of the raw bytes of the GC heap.
439    #[inline]
440    fn heap_slice(&self) -> &[u8] {
441        let vmmemory = self.vmmemory();
442        let ptr = vmmemory.base.as_ptr().cast_const();
443        let len = vmmemory.current_length();
444        unsafe { slice::from_raw_parts(ptr, len) }
445    }
446
447    /// Get a mutable slice of the raw bytes of the GC heap.
448    #[inline]
449    fn heap_slice_mut(&mut self) -> &mut [u8] {
450        let vmmemory = self.vmmemory();
451        let ptr = vmmemory.base.as_ptr();
452        let len = vmmemory.current_length();
453        unsafe { slice::from_raw_parts_mut(ptr, len) }
454    }
455
456    ////////////////////////////////////////////////////////////////////////////
457    // Provided helper methods.
458
459    /// Index into this heap and get a shared reference to the `T` that `gc_ref`
460    /// points to.
461    ///
462    /// # Errors
463    ///
464    /// Returns an error on out of bounds or if the `gc_ref` is an `i31ref`.
465    #[inline]
466    fn index<T>(&self, gc_ref: &TypedGcRef<T>) -> Result<&T>
467    where
468        Self: Sized,
469        T: GcHeapObject,
470    {
471        assert!(!mem::needs_drop::<T>());
472        let gc_ref = gc_ref.as_untyped();
473        let start = gc_ref.heap_index()?.get();
474        let start = usize::try_from(start)?;
475        let len = mem::size_of::<T>();
476        let slice = match self.heap_slice().get(start..).and_then(|s| s.get(..len)) {
477            Some(slice) => slice,
478            None => bail_bug!("gc object out-of-bounds"),
479        };
480        if slice.as_ptr().addr() % mem::align_of::<T>() != 0 {
481            bail_bug!("gc object and/or heap misaligned");
482        }
483        Ok(unsafe { &*(slice.as_ptr().cast::<T>()) })
484    }
485
486    /// Index into this heap and get an exclusive reference to the `T` that
487    /// `gc_ref` points to.
488    ///
489    /// # Errors
490    ///
491    /// Returns an error on out of bounds or if the `gc_ref` is an `i31ref`.
492    #[inline]
493    fn index_mut<T>(&mut self, gc_ref: &TypedGcRef<T>) -> Result<&mut T>
494    where
495        Self: Sized,
496        T: GcHeapObject,
497    {
498        assert!(!mem::needs_drop::<T>());
499        let gc_ref = gc_ref.as_untyped();
500        let start = gc_ref.heap_index()?.get();
501        let start = usize::try_from(start)?;
502        let len = mem::size_of::<T>();
503        let slice = match self
504            .heap_slice_mut()
505            .get_mut(start..)
506            .and_then(|s| s.get_mut(..len))
507        {
508            Some(slice) => slice,
509            None => bail_bug!("gc object out-of-bounds"),
510        };
511        assert!(slice.as_ptr().addr() % mem::align_of::<T>() == 0);
512        Ok(unsafe { &mut *(slice.as_mut_ptr().cast::<T>()) })
513    }
514
515    /// Get the range of bytes that the given object occupies in the heap.
516    ///
517    /// # Errors
518    ///
519    /// Returns an error on out of bounds or if the `gc_ref` is an `i31ref`.
520    fn object_range(&self, gc_ref: &VMGcRef) -> Result<Range<usize>> {
521        let start = gc_ref.heap_index()?.get();
522        let start = usize::try_from(start)?;
523        let size = self.object_size(gc_ref)?;
524        let end = match start.checked_add(size) {
525            Some(end) => end,
526            None => bail_bug!("object size overflow"),
527        };
528        Ok(start..end)
529    }
530
531    /// Get a mutable borrow of the given object's data.
532    ///
533    /// # Errors
534    ///
535    /// Returns an error on out-of-bounds accesses or if the `gc_ref` is an
536    /// `i31ref`.
537    fn gc_object_data(&self, gc_ref: &VMGcRef) -> Result<&VMGcObjectData> {
538        let range = self.object_range(gc_ref)?;
539        let data = match self.heap_slice().get(range) {
540            Some(data) => data,
541            None => bail_bug!("gc object out of bounds"),
542        };
543        Ok(data.into())
544    }
545
546    /// Get a mutable borrow of the given object's data.
547    ///
548    /// # Errors
549    ///
550    /// Returns an error on out-of-bounds accesses or if the `gc_ref` is an
551    /// `i31ref`.
552    fn gc_object_data_mut(&mut self, gc_ref: &VMGcRef) -> Result<&mut VMGcObjectData> {
553        let range = self.object_range(gc_ref)?;
554        let data = match self.heap_slice_mut().get_mut(range) {
555            Some(data) => data,
556            None => bail_bug!("gc object out of bounds"),
557        };
558        Ok(data.into())
559    }
560}
561
562/// A list of GC roots.
563///
564/// This is effectively a builder for a `GcRootsIter` that will be given to a GC
565/// heap when it is time to perform garbage collection.
566#[derive(Default)]
567pub struct GcRootsList(Vec<RawGcRoot>);
568
569// Ideally these `*mut`s would be `&mut`s and we wouldn't need as much of this
570// machinery around `GcRootsList`, `RawGcRoot`, `GcRoot`, and `GcRootIter` but
571// if we try that then we run into two different kinds of lifetime issues:
572//
573// 1. When collecting the various roots from a `&mut StoreOpaque`, we borrow
574//    from `self` to push new GC roots onto the roots list. But then we want to
575//    call helper methods like `self.for_each_global(...)`, but we can't because
576//    there are active borrows of `self` preventing it.
577//
578// 2. We want to reuse the roots list and its backing storage across GCs, rather
579//    than reallocate on every GC. But the only place for the roots list to live
580//    such that it is easily reusable across GCs is in the store itself. But the
581//    contents of the roots list (when it is non-empty, during GCs) borrow from
582//    the store, which creates self-references.
583#[derive(Clone, Copy, Debug)]
584#[cfg_attr(
585    not(feature = "gc"),
586    expect(
587        dead_code,
588        reason = "not worth it at this time to #[cfg] away these variants",
589    )
590)]
591enum RawGcRoot {
592    Stack(SendSyncPtr<u32>),
593    VMGcRef(SendSyncPtr<VMGcRef>),
594    ValRaw(SendSyncPtr<ValRaw>),
595}
596
597#[cfg(feature = "gc")]
598impl GcRootsList {
599    /// Add a GC root that is inside a Wasm stack frame to this list.
600    ///
601    /// # Safety
602    ///
603    /// The pointer must be to a valid stack-map slot on the Wasm stack and must
604    /// remain valid while registered within this `GcRootsList`.
605    #[inline]
606    pub unsafe fn add_wasm_stack_root(&mut self, ptr_to_root: SendSyncPtr<u32>) {
607        unsafe {
608            log::trace!(
609                "Adding Wasm stack root: {:#p} -> {:#p}",
610                ptr_to_root,
611                VMGcRef::from_raw_u32(*ptr_to_root.as_ref()).unwrap()
612            );
613            debug_assert!(VMGcRef::from_raw_u32(*ptr_to_root.as_ref()).is_some());
614        }
615        self.0.push(RawGcRoot::Stack(ptr_to_root));
616    }
617
618    /// Add a GC root to this list.
619    ///
620    /// # Safety
621    ///
622    /// The pointer must be to a valid `VMGcRef` and must remain valid while
623    /// registered within this `GcRootsList`.
624    #[inline]
625    pub unsafe fn add_vmgcref_root(&mut self, ptr_to_root: SendSyncPtr<VMGcRef>, why: &str) {
626        unsafe {
627            log::trace!(
628                "Adding VMGcRef root: {why}: {:#p}",
629                ptr_to_root.as_ref().unchecked_copy()
630            );
631        }
632        self.0.push(RawGcRoot::VMGcRef(ptr_to_root))
633    }
634
635    /// Add a GC root to this list.
636    ///
637    /// # Safety
638    ///
639    /// The pointer must be to a valid `ValRaw` that is a GC reference and must
640    /// remain valid while registered within this `GcRootsList`.
641    #[inline]
642    pub unsafe fn add_val_raw_root(&mut self, ptr_to_root: SendSyncPtr<ValRaw>, why: &str) {
643        unsafe {
644            log::trace!(
645                "Adding ValRaw root: {why}: {:#x}",
646                ptr_to_root.as_ref().get_anyref()
647            );
648        }
649        self.0.push(RawGcRoot::ValRaw(ptr_to_root))
650    }
651
652    /// Get an iterator over all roots in this list.
653    ///
654    /// # Safety
655    ///
656    /// Callers must ensure that all the pointers to GC roots that have been
657    /// added to this list are valid for the duration of the `'a` lifetime.
658    #[inline]
659    pub unsafe fn iter<'a>(&'a mut self) -> GcRootsIter<'a> {
660        GcRootsIter {
661            list: self,
662            index: 0,
663        }
664    }
665
666    /// Is this list empty?
667    pub fn is_empty(&self) -> bool {
668        self.0.is_empty()
669    }
670
671    /// Clear this GC roots list.
672    #[inline]
673    pub fn clear(&mut self) {
674        self.0.clear();
675    }
676}
677
678/// An iterator over all the roots in a `GcRootsList`.
679pub struct GcRootsIter<'a> {
680    list: &'a mut GcRootsList,
681    index: usize,
682}
683
684impl<'a> Iterator for GcRootsIter<'a> {
685    type Item = GcRoot<'a>;
686
687    #[inline]
688    fn next(&mut self) -> Option<Self::Item> {
689        let root = GcRoot {
690            raw: self.list.0.get(self.index).copied()?,
691            _phantom: marker::PhantomData,
692        };
693        self.index += 1;
694        Some(root)
695    }
696}
697
698/// A GC root.
699///
700/// This is, effectively, a mutable reference to a `VMGcRef`.
701///
702/// Collector implementations should update the `VMGcRef` if they move the
703/// `VMGcRef`'s referent during the course of a GC.
704#[derive(Debug)]
705pub struct GcRoot<'a> {
706    raw: RawGcRoot,
707    _phantom: marker::PhantomData<&'a mut VMGcRef>,
708}
709
710impl GcRoot<'_> {
711    /// Is this root from inside a Wasm stack frame?
712    #[inline]
713    pub fn is_on_wasm_stack(&self) -> bool {
714        matches!(self.raw, RawGcRoot::Stack(_))
715    }
716
717    /// Get this GC root.
718    ///
719    /// Does NOT run GC barriers.
720    #[inline]
721    pub fn get(&self) -> Result<VMGcRef> {
722        match self.raw {
723            RawGcRoot::VMGcRef(ptr) => Ok(unsafe { ptr::read(ptr.as_ptr()) }),
724            RawGcRoot::Stack(ptr) => unsafe {
725                let raw: u32 = ptr::read(ptr.as_ptr());
726                match VMGcRef::from_raw_u32(raw) {
727                    Some(r) => Ok(r),
728                    None => bail_bug!("stacked contained null gcref"),
729                }
730            },
731            RawGcRoot::ValRaw(ptr) => unsafe {
732                let val: ValRaw = ptr::read(ptr.as_ptr());
733                match val.get_vmgcref() {
734                    Some(r) => Ok(r),
735                    None => bail_bug!("val contained null gcref"),
736                }
737            },
738        }
739    }
740
741    /// Set this GC root.
742    ///
743    /// Does NOT run GC barriers.
744    ///
745    /// Collector implementations should use this method to update GC root
746    /// pointers after the collector moves the GC object that the root is
747    /// referencing.
748    pub fn set(&mut self, new_ref: VMGcRef) {
749        match self.raw {
750            RawGcRoot::VMGcRef(ptr) => unsafe {
751                ptr::write(ptr.as_ptr(), new_ref);
752            },
753            RawGcRoot::Stack(ptr) => unsafe {
754                ptr::write(ptr.as_ptr(), new_ref.as_raw_u32());
755            },
756            RawGcRoot::ValRaw(ptr) => unsafe {
757                let val = ValRaw::vmgcref(Some(new_ref));
758                ptr::write(ptr.as_ptr(), val);
759            },
760        }
761    }
762}
763
764/// A garbage collection process.
765///
766/// Implementations define the `collect_increment` method, and then consumers
767/// can either use
768///
769/// * `GarbageCollection::collect` for synchronous code, or
770///
771/// * `collect_async(Box<dyn GarbageCollection>)` for async code.
772///
773/// When using fuel and/or epochs, consumers can also use `collect_increment`
774/// directly and choose to abandon further execution in this GC's heap's whole
775/// store if the GC is taking too long to complete.
776pub trait GarbageCollection: Send + Sync {
777    /// Perform an incremental slice of this garbage collection process.
778    ///
779    /// Upon completion of the slice, a `GcProgress` is returned which informs
780    /// the caller whether to continue driving this GC process forward and
781    /// executing more slices (`GcProgress::Continue`) or whether the GC process
782    /// has finished (`GcProgress::Complete`).
783    ///
784    /// The mutator does *not* run in between increments. This method exists
785    /// solely to allow cooperative yielding
786    fn collect_increment(&mut self) -> Result<GcProgress>;
787
788    /// Run this GC process to completion.
789    ///
790    /// Keeps calling `collect_increment` in a loop until the GC process is
791    /// complete.
792    fn collect(&mut self) -> Result<()> {
793        loop {
794            match self.collect_increment()? {
795                GcProgress::Continue => continue,
796                GcProgress::Complete => return Ok(()),
797            }
798        }
799    }
800}
801
802/// The result of doing an incremental amount of GC.
803pub enum GcProgress {
804    /// There is still more work to do.
805    Continue,
806    /// The GC is complete.
807    Complete,
808}
809
810/// Asynchronously run the given garbage collection process to completion,
811/// cooperatively yielding back to the event loop after each increment of work.
812pub async fn collect_async(
813    mut collection: Box<dyn GarbageCollection + '_>,
814    asyncness: Asyncness,
815    yield_fn: impl AsyncFn(),
816) -> Result<()> {
817    #[cfg(not(feature = "async"))]
818    {
819        _ = yield_fn;
820    }
821
822    loop {
823        match collection.collect_increment()? {
824            GcProgress::Continue => {
825                if asyncness != Asyncness::No {
826                    #[cfg(feature = "async")]
827                    yield_fn().await
828                }
829            }
830            GcProgress::Complete => return Ok(()),
831        }
832    }
833}
834
835#[cfg(all(test, feature = "async"))]
836mod collect_async_tests {
837    use super::*;
838
839    #[test]
840    fn is_send_and_sync() {
841        fn _assert_send_sync<T: Send + Sync>(_: T) {}
842
843        fn _foo(collection: Box<dyn GarbageCollection>) {
844            _assert_send_sync(collect_async(collection, Asyncness::Yes, async || ()));
845        }
846    }
847}