///|
// The native heap retains this state. It never retains the heap itself; a Store
// visitor is installed only within the existing execution/collection scope.
priv struct CollectionState {
mut visitor : ((Int32, FixedArray[Int64]) -> FixedArray[Int64])?
mut collecting : Bool
}
///|
// Borrowed native stack request; no pointer or root buffer may escape the call.
#external
priv type CollectionRequest
///|
#borrow(self)
extern "c" fn GCHeapHandle::collection_state(
self : GCHeapHandle,
) -> CollectionState = "wasmoon_gc_heap_collection_state"
///|
#borrow(self)
extern "c" fn GCHeapHandle::set_precise_roots(
self : GCHeapHandle,
precise : Bool,
) -> Unit = "wasmoon_gc_heap_set_precise_roots"
///|
pub fn GCHeapHandle::bind_root_visitor(
self : GCHeapHandle,
visitor : (Int32, FixedArray[Int64]) -> FixedArray[Int64],
) -> Unit {
guard !self.is_null() else { return }
let state = self.collection_state()
state.visitor = Some(visitor)
if !state.collecting {
self.set_precise_roots(true)
}
}
///|
pub fn GCHeapHandle::clear_root_visitor(self : GCHeapHandle) -> Unit {
guard !self.is_null() else { return }
let state = self.collection_state()
state.visitor = None
if !state.collecting {
self.set_precise_roots(false)
}
}
///|
extern "c" fn CollectionRequest::seeds(
self : CollectionRequest,
) -> FixedArray[Int64] = "wasmoon_gc_collection_seeds"
///|
#borrow(roots)
extern "c" fn CollectionRequest::mark(
self : CollectionRequest,
roots : FixedArray[Int64],
) -> Unit = "wasmoon_gc_collection_mark"
///|
extern "c" fn CollectionRequest::mark_conservative(
self : CollectionRequest,
) -> Unit = "wasmoon_gc_collection_mark_conservative"
///|
extern "c" fn CollectionRequest::abort(self : CollectionRequest) -> Unit = "wasmoon_gc_collection_abort"
///|
extern "c" fn CollectionRequest::reset_auxiliary_marks(
self : CollectionRequest,
) -> Unit = "wasmoon_gc_collection_reset_auxiliary_marks"
///|
extern "c" fn CollectionRequest::sweep(self : CollectionRequest) -> Int32 = "wasmoon_gc_collection_sweep"
///|
extern "c" fn CollectionRequest::rollback(self : CollectionRequest) -> Unit = "wasmoon_gc_collection_rollback"
///|
extern "c" fn CollectionRequest::sweep_exceptions(
self : CollectionRequest,
) -> Unit = "wasmoon_gc_collection_sweep_exceptions"
///|
extern "c" fn CollectionRequest::sweep_continuations(
self : CollectionRequest,
) -> Unit = "wasmoon_gc_collection_sweep_continuations"
///|
extern "c" fn CollectionRequest::set_precise_roots(
self : CollectionRequest,
precise : Bool,
) -> Unit = "wasmoon_gc_collection_set_precise_roots"
///|
fn roots_unavailable(roots : FixedArray[Int64]) -> Bool {
roots.length() == 1 && roots[0] == -2L
}
///|
fn CollectionState::collect(
self : CollectionState,
request : CollectionRequest,
rollback : Bool,
) -> Int32 {
guard !self.collecting else { return if rollback { 0 } else { -1 } }
self.collecting = true
let result = trace_and_reclaim(request, self.visitor, rollback)
self.collecting = false
request.set_precise_roots(self.visitor is Some(_))
result
}
///|
// This protocol is non-raising. Keeping its early returns inside one helper
// gives the outer scope one cleanup point without allocating a defer closure.
fn trace_and_reclaim(
request : CollectionRequest,
visitor : ((Int32, FixedArray[Int64]) -> FixedArray[Int64])?,
rollback : Bool,
) -> Int32 {
// Retain one visitor for the entire protocol. Binding changes apply only to
// the next collection; the native mark mode is updated by the outer scope.
match visitor {
Some(visit) => {
let seeds = request.seeds()
let expanded = if roots_unavailable(seeds) {
seeds
} else {
visit(0, seeds)
}
if roots_unavailable(expanded) {
request.abort()
visit(2, []) |> ignore
return if rollback { 0 } else { -1 }
}
request.mark(expanded)
}
None => request.mark_conservative()
}
let collected = if rollback {
request.rollback()
1
} else {
request.sweep()
}
match visitor {
Some(visit) => {
request.sweep_exceptions()
request.sweep_continuations()
visit(1, []) |> ignore
}
None => request.reset_auxiliary_marks()
}
collected
}