mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-11 13:48:30 +09:00
[Fix] (MG_Backend, MG_Impl): give the MGPipe input blocks leak-at-exit storage - gPipeInputs and the two verify blocks held the last SharedPtr to a deleted-but-bound vertex array, so __run_exit_handlers ran ~VertexArrayObject and ~BufferObject into a pipe and a backend that were already torn down
This commit is contained in:
@@ -688,7 +688,22 @@ namespace MobileGL::MG_Pipe {
|
|||||||
|
|
||||||
// The single global the backends read through MGB_CTX (ARCHITECTURE.md 9.2). An inline
|
// The single global the backends read through MGB_CTX (ARCHITECTURE.md 9.2). An inline
|
||||||
// variable: no .cpp is needed for the definition.
|
// variable: no .cpp is needed for the definition.
|
||||||
inline PipeInputs gPipeInputs{};
|
//
|
||||||
|
// LEAK-AT-EXIT STORAGE, and it is the same rule Init.cpp and GlobalObjects.cpp state for
|
||||||
|
// pGLContext and pActiveBackendObject: "a process that exits without eglTerminate simply
|
||||||
|
// leaks the global singletons to the OS instead of running destructors during static
|
||||||
|
// teardown". This block breaks that rule if it is a value, because its O-class members
|
||||||
|
// are SharedPtrs to FRONTEND objects: a VertexArrayObject that the application deleted
|
||||||
|
// while it was bound has its last reference here, and destroying this block from
|
||||||
|
// __run_exit_handlers therefore runs ~VertexArrayObject -> ~BufferObject at exit. Those
|
||||||
|
// destructors are not exit-safe and cannot be made so - they reach the client's slot
|
||||||
|
// allocator, the resource tracker, the vertex-input emitter, the applier AND, through
|
||||||
|
// MGPipeApplyResourceDestroy, the backend's own twin tables, deferred-release queue,
|
||||||
|
// buffer pool and driver entry points, every one of which is either already destroyed or
|
||||||
|
// about to be. So the reference is never dropped: nothing here can start such a chain.
|
||||||
|
// A live context releases these SharedPtrs the ordinary way, at the fill point.
|
||||||
|
// (P3a; the exit-time heap corruption this closes is p3a-results/exit-order-v1.md.)
|
||||||
|
inline PipeInputs& gPipeInputs = *new PipeInputs();
|
||||||
|
|
||||||
// Every field has storage or is forwarded, and nothing else.
|
// Every field has storage or is forwarded, and nothing else.
|
||||||
#define MGP_INPUT_COUNT_ONE(Field, Member) +1
|
#define MGP_INPUT_COUNT_ONE(Field, Member) +1
|
||||||
|
|||||||
@@ -363,8 +363,14 @@ namespace MobileGL::MG_Pipe {
|
|||||||
// COMPARE-AT-READ in every accessor (the stored value against a fresh read of the
|
// COMPARE-AT-READ in every accessor (the stored value against a fresh read of the
|
||||||
// live context at the moment the backend reads it - the arm that is real in P1: it
|
// live context at the moment the backend reads it - the arm that is real in P1: it
|
||||||
// catches a value that changed between the verb boundary and the read).
|
// catches a value that changed between the verb boundary and the read).
|
||||||
PipeInputs g_snapshot{}; // the second arm
|
// LEAK-AT-EXIT STORAGE, for gPipeInputs' reason (MG_Backend/MGPipe/PipeInputs.h): a
|
||||||
PipeInputs g_readScratch{}; // where the compare-at-read re-read lands
|
// PipeInputs holds SharedPtrs to frontend objects in its O class, and these two are
|
||||||
|
// filled from the live context, so either can hold the LAST reference to a
|
||||||
|
// VertexArrayObject or a ProgramObject. Destroying them from __run_exit_handlers
|
||||||
|
// would run ~VertexArrayObject / ~BufferObject at exit, into a pipe and a backend
|
||||||
|
// that are already being torn down. References so the ~50 uses below need no edit.
|
||||||
|
PipeInputs& g_snapshot = *new PipeInputs(); // the second arm
|
||||||
|
PipeInputs& g_readScratch = *new PipeInputs(); // where the compare-at-read re-read lands
|
||||||
|
|
||||||
// The read hook arms at the first fill (ArmVerify below), so it cannot see a read
|
// The read hook arms at the first fill (ArmVerify below), so it cannot see a read
|
||||||
// made before that. That window is covered by the poison instead: MGP_INPUT_CHECK
|
// made before that. That window is covered by the poison instead: MGP_INPUT_CHECK
|
||||||
@@ -1163,7 +1169,12 @@ namespace MobileGL::MG_Pipe {
|
|||||||
// Widening the probe to all 29 would re-implement the derivation to check it.
|
// Widening the probe to all 29 would re-implement the derivation to check it.
|
||||||
Bool ApplierDerivesRenderStateFields() {
|
Bool ApplierDerivesRenderStateFields() {
|
||||||
static const Bool answer = [] {
|
static const Bool answer = [] {
|
||||||
static PipeInputs probe;
|
// Leak-at-exit, for gPipeInputs' reason: a PipeInputs is never destroyed by
|
||||||
|
// an exit handler. This one only ever carries render state, but the rule is
|
||||||
|
// stated over the TYPE rather than over each instance's current contents -
|
||||||
|
// an instance that grows an O-class write later must not become the next
|
||||||
|
// exit-time chain starter.
|
||||||
|
static PipeInputs& probe = *new PipeInputs();
|
||||||
constexpr Uint32 kSentinel = 0x5a5a5a5au;
|
constexpr Uint32 kSentinel = 0x5a5a5a5au;
|
||||||
MGPipeFillAccess::RenderStateOf(probe).ClearStencil = kSentinel;
|
MGPipeFillAccess::RenderStateOf(probe).ClearStencil = kSentinel;
|
||||||
MGPipeFillAccess::ClearStencilOf(probe) = 0u;
|
MGPipeFillAccess::ClearStencilOf(probe) = 0u;
|
||||||
|
|||||||
Reference in New Issue
Block a user