[Fix, Test] (MG_Pipe, clientfb, clientsp): retire every emitter's entry at the object's death - the six death helpers freed the slot and told no emitter, so a dead-but-unrecycled texture handle still resolved to the freed ITextureObject* (the allocator's generation moves only at the next hand-out) and the drain list kept the level: glTexImage2D; glDeleteTextures; <any verb> called a virtual on freed memory from the next validate point (final review C-2); the helpers now forward to the texture, renderbuffer, framebuffer, sampler-view and shader-CSO emitters between the wire delete and the free, ResolveTexture refuses a dead slot loudly on IsLive, the sticky-mask producers stamp the generation they write under, and the delete-then-use sequence is pinned for every kind, for a recycled slot, and under MALLOC_PERTURB_ on both backends

This commit is contained in:
2026-09-08 23:59:48 -04:00
parent 4bb5e082d3
commit a7d56a2fe5
10 changed files with 484 additions and 28 deletions
+6 -8
View File
@@ -324,14 +324,12 @@ namespace MobileGL::MG_Pipe {
// The memo's other half, and the bound-mirror clearing beside it.
//
// NO PRODUCTION CALLER TODAY, stated rather than implied: since c0b the death path
// reads the contract's latch and never asks an emitter. It is kept because the memo
// above needs a way to be told, and because everything it clears SELF-HEALS if it is
// not called - the slot's Gen moves on reuse, so `RecordGen == handle.Gen` refuses a
// stale record latch, and the three bound mirrors below hold a handle whose generation
// can never be handed out again, so the next EmitShaderState compares against a
// different handle and re-binds. Clearing them here is the cheaper answer, not the
// load-bearing one.
// THE CALLER IS THE CONTRACT's DEATH HELPER (P4a final review C-2): the death path
// reads the contract's latch for the wire delete and then forwards here, before the
// slot is freed, so a dead handle no longer reads as published in this memo between
// the death and the recycle and the three bound mirrors never name a dead program.
// Gen-keyed, so a late notice for a slot already handed out again clears nothing of
// the successor's.
void NoteRecordDestroyed(MGPipeHandle handle) {
if (MGPipeHandleIsNull(handle)) return;
Vector<Latch>& table = TableOf(handle);