Parent: #140 Tracked by: #134 Related: #325, #324 Dependency: zig-utils/zig-gc#34
Problem
The object-cell finalizer currently invokes CApiObjectOwner.finishOnce directly from Binding.finalize. In parallel collection, zig-gc runs that finalizer while holding alloc_lock. A class finalizer is embedder code and may release handles, allocate, or re-enter the VM, so direct invocation can deadlock or observe a half-restored collector state.
Scope
- Queue C-API class-owner finalization without allocating from the sweep path.
- Drain exact-once callbacks from Binding.afterSweep, after collector locks are released and publication is restored by zig-utils/zig-gc#34.
- Drain teardown callbacks only after heap deinit and after the Context has made the heap unavailable.
- Preserve class refcounts, prototype ownership, cross-context rejection, no-GIL ordering, and current arena-mode teardown behavior.
- Keep external buffer/string release records separate unless one generic intrusive owner queue measurably simplifies both without weakening type contracts.
Acceptance
- Focused tests prove ordinary GC and context teardown invoke once, duplicate references do not duplicate callbacks, and construction failure releases correctly.
- A callback re-enters representative C API operations without deadlock and observes a fully restored heap state.
- Parallel/no-GIL and TSan coverage is green.
- One full CI matrix is accepted and parent trackers are updated.