[MG_Backend, MG_State, MG_Pipe, MG_Remote, MG_Impl, MG_IntegrationTest, docs] (P7): magma installs a state-object death table, and the inproc arm stops losing every object_death

CONTRACT-P7 §5.5. DirectVulkan had no StateObjectDeathOps at all, so under a Magma
inproc session NotifyStateObjectDestroyed reached nobody and the server was never told
that a frontend object had died. Package L measured the shape: with the WireTables
install condition made backend-agnostic, CtWireScenario's two death cases PASS on Magma
spawn and tcp - the remote client has no local backend, so the client-side emitter
installed there answers the notice - and FAIL on inproc, where the install condition
deliberately stands aside for "the backend's own dispatcher" and Magma had none.

g_magmaStateObjectDeathOps is that dispatcher. It mirrors g_glesStateObjectDeathOps
(DirectGLES/Managers.cpp:335) with ONE arm rather than two: Espryt's other arm destroys
a backend twin keyed by lifetime id, and Magma keeps no such twin - its two
client-minted kinds are keyed on {slot, gen} by MagmaPipeIdentityTables and the other
six are still reached from their frontend objects - so what is left is exactly the emit
of object_death off the apply thread. Installed from
BackendObject_DirectVulkan::Initialize(), i.e. ServerLoop::CreateBackend, step 1 of
InitServerRoleCommon: the one place BOTH Magma server roles pass through, and before any
client session exists. #if MOBILEGL_BUILD_DISAGGREGATED && MOBILEGL_PIPE_PUSH, so the
pull build compiles nothing new.

The two skip guards in CtWireScenario go with it. Every other "Magma installs none" site
the audit found is comment text whose ARGUMENT survives, and each is reworded rather than
deleted: the table emits the wire record and frees no client slot, so P4a's
backend-neutral NotifyAndFree is still the only thing that returns one, HandleRecycle's
C-1 case still measures the only protection the allocator has, and MagmaPipeArms' age
sweep is still the only thing that retires an identity slot.

The framebuffer death case now reads the client's allocator beside the server's tally.
That correction matters: the plan called this a slot leak provable with PipeSlotPeek, and
it is not - it has been backend-neutral since P4a. What leaked was the SERVER's twin, and
a framebuffer has no wire delete opcode at all, so object_death is its only delivery.

Two installers cannot race although the global is last-writer-wins with no stacking:
Magma's runs only in a process that owns a DirectVulkan backend, and
InstallClientWireTables' arm runs only under Transport == Spawn, which is by construction
the process that owns none.

RED-ONCE: `if (false) InstallStateObjectDeathOps();` reds the two Split cases alone
(`Expected: (afterCounters.deaths) > (deathsBefore), actual: 0 vs 0`, CtWireScenario.cpp:264)
and leaves spawn and tcp green - the exact split of responsibility the comment claims.
Restored: 8/8. Recorded in docs/Disaggregated/notes/p7/magma-c.md.
This commit is contained in:
2026-09-22 16:07:13 -04:00
parent 07079c3c0e
commit dce9953d36
12 changed files with 270 additions and 35 deletions
+15 -7
View File
@@ -757,13 +757,21 @@ namespace MobileGL::MG_Remote::Client {
// That is what "keep inproc's existing backend dispatcher" meant, and it is also
// what keeps this from stomping a backend that installs ops of its own later.
//
// WHAT THIS DOES *NOT* DO IS GIVE MAGMA A DEATH TABLE. Magma installs no
// StateObjectDeathOps at all (wave 2 package C mirrors g_glesStateObjectDeathOps for
// it); until it does, the client-side emitter installed here still asks
// MGPipeSlots().FindByLifetimeId for a handle, gets a null one for an object Magma
// never routed, and emits nothing. The count under Magma may therefore still be 0 -
// but it is now 0 because no object crossed, which the lane can see and attribute,
// instead of 0 because nobody was listening, which it could not.
// MAGMA NOW HAS A DEATH TABLE OF ITS OWN (P7 wave 2 package C, CONTRACT-P7 §5.5:
// DirectVulkan.cpp's g_magmaStateObjectDeathOps), and the two installs do NOT race,
// which is worth stating because the global is a bare last-writer-wins pointer with
// no stacking. They cannot meet: Magma's is installed from
// BackendObject_DirectVulkan::Initialize(), i.e. only in a process that OWNS a
// DirectVulkan backend, and the arm here runs only under `Transport == Spawn`, which
// is by construction the process that owns NO backend at all (InitSplitRoles builds a
// BackendObject_Remote there). So the spawn client keeps this emitter, the inproc and
// server-side roles keep Magma's, and `GetStateObjectDeathOps() == nullptr` below
// keeps meaning what P6.5 meant by it.
//
// MEASURED, both directions (package C's red-once): with Magma's install
// short-circuited, the two CtWireScenario death cases red on the INPROC arm alone
// (`deaths` 0 vs 0) and stay green on spawn and tcp - which is exactly the split of
// responsibility this comment claims.
if (MG_Config::Transport == MG_Config::TransportMode::Spawn &&
MG_State::GLState::GetStateObjectDeathOps() == nullptr) {
MG_State::GLState::SetStateObjectDeathOps(&kClientStateObjectDeathOps);