From a6028ff38a8146bf818ffcafb3c5472660bd49ea Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Fri, 9 Oct 2026 21:14:43 -0400 Subject: [PATCH] [Fix] (TLS): drop the context reference before clearing the slot at thread exit --- MobileGL/MG_State/GLState/Core.cpp | 11 +++++++---- MobileGL/MG_State/GLState/Core.h | 6 +++--- MobileGL/MG_Test/SanityTest.cpp | 2 +- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/MobileGL/MG_State/GLState/Core.cpp b/MobileGL/MG_State/GLState/Core.cpp index 9195b464..aab477e3 100644 --- a/MobileGL/MG_State/GLState/Core.cpp +++ b/MobileGL/MG_State/GLState/Core.cpp @@ -1658,12 +1658,15 @@ namespace MobileGL::MG_State { constinit thread_local CurrentGLContextSlot pGLContext; // The reference that keeps the calling thread's pGLContext alive (Core.h). Registered for - // destruction on the thread's first assignment; at thread exit it clears the slot first, so - // whatever the context's destructor reaches sees no current context rather than one that is - // being destroyed. + // destruction on the thread's first assignment. At thread exit it drops the reference first, + // with the slot still naming the context - what destroying the SharedPtr this used to be did + // - and then clears the slot, so no pointer outlives the context. struct CurrentGLContextOwner { SharedPtr context; - ~CurrentGLContextOwner() { pGLContext.m_context = nullptr; } + ~CurrentGLContextOwner() { + context.reset(); + pGLContext.m_context = nullptr; + } }; namespace { thread_local CurrentGLContextOwner tCurrentGLContextOwner; diff --git a/MobileGL/MG_State/GLState/Core.h b/MobileGL/MG_State/GLState/Core.h index 23429bc3..2786fdce 100644 --- a/MobileGL/MG_State/GLState/Core.h +++ b/MobileGL/MG_State/GLState/Core.h @@ -790,9 +790,9 @@ namespace MobileGL { // every API level. This object is trivially destructible and constant-initialized, so a // read is one TLS address computation and a load. The SharedPtr that keeps the context // alive lives in a separate thread_local in Core.cpp, written only by the assignments - // below (eglMakeCurrent and the release edge, MG_State::Init(), tests); its destructor - // clears this pointer before it drops the reference, so a thread's exit leaves no - // dangling pointer behind. + // below (eglMakeCurrent and the release edge, MG_State::Init(), tests); at thread exit its + // destructor drops the reference and then clears this pointer, so no dangling pointer is + // left behind. // // Assignment, reset() and the SharedPtr conversions touch the owner and belong to the // CALLING THREAD only: never assign through a reference to another thread's slot. diff --git a/MobileGL/MG_Test/SanityTest.cpp b/MobileGL/MG_Test/SanityTest.cpp index 5752cc1d..2d806cab 100644 --- a/MobileGL/MG_Test/SanityTest.cpp +++ b/MobileGL/MG_Test/SanityTest.cpp @@ -234,7 +234,7 @@ namespace { // restores the process-wide state even when a gtest assertion unwinds the test body. struct ScopedDirectGLESTextureBindings { ScopedDirectGLESTextureBindings(): - previousContext(MobileGL::Move(MobileGL::MG_State::pGLContext)), + previousContext(MobileGL::MG_State::pGLContext.Take()), previousFunctions(MobileGL::MG_Backend::DirectGLES::g_GLESFuncs), previousActiveUnit(MobileGL::MG_Backend::DirectGLES::TextureImpl::g_activeTextureUnit), previousCache(MobileGL::MG_Backend::DirectGLES::TextureImpl::g_boundTexturesCache),