ReviewOS

zig-utils/zig-js

GC: add one ownership-aware heap across sibling realms

#415
Closed chris opened this 9 days ago ·
9 days ago

Parent: #414 GC parent: #144 Roadmap: #134

Build the engine-core foundation for multiple Context realms to allocate cells into one precise heap without pretending every cell belongs to the primary realm.

Acceptance:

  • Introduce a heap-owned realm registry with atomic add/retire and one explicit heap owner.
  • Trace and relocate roots for every live realm while excluding a retired realm only after its execution has quiesced.
  • Route allocation-failure recovery, weak work, post-sweep callbacks, finalization accounting, and embedder-owner callbacks to the cell or realm that owns them.
  • Keep shared Symbol identity and VM-wide state separate from realm-local globals, intrinsics, modules, jobs, exceptions, and protected handles.
  • Define safe realm destruction ordering: stop jobs/threads, retire roots, collect cells, then release non-cell realm storage; the heap owner outlives all siblings.
  • Add focused multi-realm precise-GC, weak/finalization, relocation, retained-handle, and TSan tests.
  • Preserve standalone precise-GC and arena context-group behavior until #416 migrates the private lifecycle.

Child issue map

  • #420 — register and trace live precise sibling realms
  • #421 — route shared-heap cells to their allocating realm
  • #419 — retire and destroy precise sibling realms safely.
9 days ago

Initial code audit identifies the ownership seams that must change together:

  • gc.Binding is currently one *Context; root tracing, allocation recovery, weak queues, post-sweep callbacks, finalizer accounting, backing allocation, and relocation all route through that one realm.
  • gc.setActiveHeap also derives managed-string, object-side-store, and generator backing ownership from heap.ctx.context, so sharing only the heap pointer would silently charge/finalize sibling allocations through the primary.
  • Context.createSharedArenaRealm explicitly rejects GC primaries, and ordinary destroy() owns/deinitializes the whole GcState; a sibling needs a distinct retire/collect/non-cell-storage path.
  • A root-list-only patch would therefore be unsound. The foundation needs an explicit heap runtime owner plus allocation-realm identity, per-realm callback/weak queues, and a cell-to-realm finalization rule before #416 can migrate the ABI.

This is why #415 remains a separate engine-core dependency instead of being hidden inside the ABI shim.

9 days ago

All child issues (#420, #421, #419) and parent acceptance items are complete. #416 now adopts the engine-core path at the private ABI boundary.

Sign in to comment on this issue.