E5: hazard-audit round-2 fixes (re-register set hygiene, SUMMARY mutex ref)

1. DEFENSIVE (backend_metal.mm, coli_metal_register): re-registering a live
   base overwrote s.buf and resset_add'ed the new wrapper without removing
   the OLD one from the residency set -- ARC drops our reference but the set
   retains the object and keeps its pages resident: a leak and a
   set/g_slabs divergence. The found branch now stashes the replaced
   wrapper under g_slab_mtx and, after dropping the lock (round-1 invariant:
   no Metal call under the slab lock), resset_remove(old)s it before
   resset_add(b); identical old==b early-outs both set operations.
   Invariant defended, stated in the comment: residency-set membership
   mirrors g_slabs exactly. No in-tree caller re-registers a live base
   today (audited) -- closed defensively.

2. DOC (SUMMARY.md): the moe_submit lifecycle bullet still said
   resset_flush commits "under g_slab_mtx" -- stale text from before the
   round-1 mutex split; the code takes g_resset_mtx and never touches the
   slab lock there. Parenthetical corrected; register bullet updated to
   describe the re-register hygiene from item 1.
This commit is contained in:
monotophic
2026-07-18 15:57:12 -04:00
committed by Kyle Kelley
parent e011092ce1
commit 8efa9ec6c3
2 changed files with 15 additions and 4 deletions
+9 -2
View File
@@ -399,13 +399,20 @@ extern "C" void coli_metal_register(void *base, size_t len) {
id<MTLBuffer> b = [g_dev newBufferWithBytesNoCopy:base length:len
options:g_res_opts deallocator:nil];
if (!b) return;
id<MTLBuffer> old = nil; // E5: replaced wrapper on re-register of a live base (defensive)
{
std::lock_guard<std::mutex> lk(g_slab_mtx); // called from parallel expert_load threads
bool found = false;
for (auto &s : g_slabs) if (s.base == base) { s.len = len; s.buf = b; found = true; break; }
for (auto &s : g_slabs) if (s.base == base) { old = s.buf; s.len = len; s.buf = b; found = true; break; }
if (!found) g_slabs.push_back({base, len, b});
}
resset_add(b); // E5: outside g_slab_mtx (no Metal call under the slab lock); still before return
// E5, outside g_slab_mtx (no Metal call under the slab lock), before returning. Invariant
// defended: set membership mirrors g_slabs exactly -- a re-register of a live base must
// drop the replaced wrapper from the set (ARC releases our reference, but the set retains
// it and keeps its pages resident forever) before adding the new one. No in-tree caller
// re-registers a live base today; defensive.
if (old && old != b) resset_remove(old);
if (old != b) resset_add(b);
}
extern "C" void coli_metal_unregister(void *base) {
id<MTLBuffer> b = nil;