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}