From fde5fda3b53e614cc94766f982d4eb82529e49d7 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Tue, 8 Sep 2026 08:20:55 -0400 Subject: [PATCH] [Fix] (MG_Impl): never destroy the four remaining MGPipe singletons - the vertex-input emitter is on ~VertexArrayObject own death path since C-1 and answered RecordIsPublished out of a freed latch vector --- MobileGL/MG_Impl/Pipe/CsoCache.h | 7 +++++-- MobileGL/MG_Impl/Pipe/SetHashSuppressor.h | 7 +++++-- MobileGL/MG_Impl/Pipe/Tracker.h | 10 ++++++++-- MobileGL/MG_Impl/Pipe/VertexInputEmit.h | 10 ++++++++-- 4 files changed, 26 insertions(+), 8 deletions(-) diff --git a/MobileGL/MG_Impl/Pipe/CsoCache.h b/MobileGL/MG_Impl/Pipe/CsoCache.h index 2f742745..43a0886c 100644 --- a/MobileGL/MG_Impl/Pipe/CsoCache.h +++ b/MobileGL/MG_Impl/Pipe/CsoCache.h @@ -195,8 +195,11 @@ namespace MobileGL::MG_Pipe { // when the pipeline version moved, and it keeps the eviction order in the same array as // the content - a map would need a second structure to answer "which is oldest". inline MGPipeCsoCache& MGPipeCsoCacheInstance() { - static MGPipeCsoCache cache; - return cache; + // NEVER DESTROYED, for MGPipeTrackerInstance()' reason (MG_Impl/Pipe/Tracker.h): the + // rule covers every MGPipe process singleton, not only the ones on today's death + // paths. + static MGPipeCsoCache* cache = new MGPipeCsoCache(); + return *cache; } } // namespace MobileGL::MG_Pipe #endif // MOBILEGL_PIPE_PUSH diff --git a/MobileGL/MG_Impl/Pipe/SetHashSuppressor.h b/MobileGL/MG_Impl/Pipe/SetHashSuppressor.h index 70881257..e42104e0 100644 --- a/MobileGL/MG_Impl/Pipe/SetHashSuppressor.h +++ b/MobileGL/MG_Impl/Pipe/SetHashSuppressor.h @@ -86,8 +86,11 @@ namespace MobileGL::MG_Pipe { // The monolith's one suppressor, beside the tracker and the CSO cache. inline MGPipeSetHashSuppressor& MGPipeSetHashSuppressorInstance() { - static MGPipeSetHashSuppressor suppressor; - return suppressor; + // NEVER DESTROYED, for MGPipeTrackerInstance()' reason (MG_Impl/Pipe/Tracker.h): the + // rule covers every MGPipe process singleton, not only the ones on today's death + // paths. + static MGPipeSetHashSuppressor* suppressor = new MGPipeSetHashSuppressor(); + return *suppressor; } } // namespace MobileGL::MG_Pipe #endif // MOBILEGL_PIPE_PUSH diff --git a/MobileGL/MG_Impl/Pipe/Tracker.h b/MobileGL/MG_Impl/Pipe/Tracker.h index de62f0c4..15e88c09 100644 --- a/MobileGL/MG_Impl/Pipe/Tracker.h +++ b/MobileGL/MG_Impl/Pipe/Tracker.h @@ -521,8 +521,14 @@ namespace MobileGL::MG_Pipe { // The monolith's one tracker. Under split there is one per client context; the context // identity check inside Update is what makes the single instance safe today. inline MGPipeTracker& MGPipeTrackerInstance() { - static MGPipeTracker tracker; - return tracker; + // NEVER DESTROYED, for MGPipeSlots()' reason (MG_Impl/Pipe/SlotAllocator.cpp). The + // rule is stated over the SET of MGPipe process singletons rather than over the two + // that a frontend destructor reaches today: which of them a destructor reaches is a + // property of the emitters, and the emitters change (C-1 added a second reaching + // path in one commit). One allocation per process, no destructor to lose - this type + // has none - and nothing can then answer a late call out of freed storage. + static MGPipeTracker* tracker = new MGPipeTracker(); + return *tracker; } } // namespace MobileGL::MG_Pipe #endif // MOBILEGL_PIPE_PUSH diff --git a/MobileGL/MG_Impl/Pipe/VertexInputEmit.h b/MobileGL/MG_Impl/Pipe/VertexInputEmit.h index 94ee409b..1b40e47f 100644 --- a/MobileGL/MG_Impl/Pipe/VertexInputEmit.h +++ b/MobileGL/MG_Impl/Pipe/VertexInputEmit.h @@ -431,8 +431,14 @@ namespace MobileGL::MG_Pipe { // The monolith's one vertex-input emitter, beside the tracker, the CSO cache, the // set-hash suppressor and the resource tracker. inline MGPipeVertexInputEmitter& MGPipeVertexInputEmitterInstance() { - static MGPipeVertexInputEmitter emitter; - return emitter; + // NEVER DESTROYED, for MGPipeSlots()' reason (MG_Impl/Pipe/SlotAllocator.cpp), and + // this one is not hypothetical: C-1 put this emitter DIRECTLY on ~VertexArrayObject's + // path - MGPipeEmitVertexElementsDestroyAndFree asks RecordIsPublished(handle) and + // then NoteRecordDestroyed(handle), which read and WRITE m_latch. A destroyed + // emitter answers out of a freed Vector and the write grows it, i.e. an operator + // new + memcpy + operator delete on an already-freed block. + static MGPipeVertexInputEmitter* emitter = new MGPipeVertexInputEmitter(); + return *emitter; } } // namespace MobileGL::MG_Pipe #endif // MOBILEGL_PIPE_PUSH