Downstream: zig-utils/zig-js#128 Related: #4, #26
Problem
The optional owned-slab classifiers can expose a newly reserved slot before zig-gc initializes and publishes its header. In zig-js's parallel mid-script collector, a conservative stack word can then classify that issued slot and read Header.magic, size, and marked while the allocating mutator is still zeroing/initializing the same memory.
Fresh main TSan evidence reports all three manifestations in one exact mid-GC run:
headerForInteriorAddressreading header magic;headerForInteriorAddressreading payload size;claimMarktouching the mark bit;
while the peer stack is Allocator.allocBytesAligned -> Heap.allocateCellSlab -> Heap.create.
Design
Add optional binding publication callbacks around owned cell headers:
- after zig-gc initializes every header field, publish the allocation to the binding before it becomes classifiable;
- before clearing magic/returning a dead slot, unpublish it so classifiers stop returning the address;
- bindings without the hooks retain current behavior;
- hook synchronization supplies the happens-before edge from header initialization to classification.
Acceptance
- Generic tests prove an owned allocation is unclassifiable before header publication, classifiable afterward, and unclassifiable before reclamation.
- Preserve create/createBatch ordering, born-grey safety, nursery accounting, OOM behavior, and bindings without hooks.
- Pass full zig-gc normal and TSan suites.
- Downstream zig-js adds exact per-slot publication metadata and passes focused normal/TSan mid-GC tests.
- Fresh full-main
tsan-threadfuzz-midgcis suppression-free.