[Fix, Test] (Magma, MG_Test): cover the {slot, gen} generation in a unit test that forces a real slot reuse, and stop claiming the ABA lanes do

- 55d2af9b claimed - in its message, in MagmaPipeArms.h, in VertexInputStateFactory.cpp and
  in MG_IntegrationTest/CMakeLists.txt - that the AbaControlHandles lane defeats the
  GENERATION in {slot, gen}. It does not, and no lane of that shape can. Magma's mint has no
  death notification (nothing in MG_Backend/DirectVulkan consumes NotifyStateObjectDestroyed)
  and returns a slot only through OnFrameBoundary's age sweep, kSweepInterval 256 /
  kRetireAgeBoundaries 1024; HandleRecycleScenario issues five frame boundaries, so the
  replacement VAO acquires against an empty free list and gets a BRAND-NEW slot at Gen 1
  (measured: redVao slot=2 gen=1, greenVao slot=3 gen=1). The knob-off FRESH verdict there is
  decided by the SLOT alone, and deleting ++m_entries[index].Gen leaves all 32 HandleRecycle
  entries green - re-measured this round.
- What the lane does defeat is the object identity that SELECTS the slot, which IS the key the
  handle arm ships, and that is what the three code sites now say. The two requirements are
  mutually exclusive for the pixel-visible memo: a genuine slot reuse needs >= 1024 idle
  boundaries after the dead object's last draw, which necessarily puts the two draws in
  different frames, and ResolvedVertexBindings - the only memo carrying a GPU slice rather
  than a layout - declines across frames by design.
- So the generation is covered where it IS expressible. MG_Test/Pipe/MagmaPipeIdentityTest.cpp
  drives the mint's real retire -> reuse (1280 boundaries, with a keep-alive object holding the
  first allocatable slot so the reuse is not the slot the control aliases onto) and asserts
  four things: the retired slot comes back with Gen+1; with the knob OFF a memo stamped at
  {slot, gen=N} is NOT served at {slot, gen=N+1}; with the knob ON it IS, out of one uncleared
  and unclaimed entry; and a live object keeps its slot, its generation and its memo across two
  sweeps, so the generation cannot be "fixed" by bumping it on every acquisition.
- The claim rule itself moves into MagmaPipeArms.h as MagmaPipeClaimSlotMemos so the suite
  exercises production code rather than a copy of it. VertexInputStateFactory::MemosFor is now
  one call to it and is otherwise unchanged, on both the knob-on and the knob-off path.
- Load-bearing, measured: with ++m_entries[index].Gen commented out, ctest -L unit in
  build-push goes 1563/1566 - three of the four new cases red, one of them naming the inherited
  0xDEAD payload out of the same slot - while ctest -R HandleRecycle stays 32/32. Restored, all
  four pass in build-push and build-verify and skip visibly in the pull build, so the ctest name
  sets stay identical (G2).
This commit is contained in:
2026-09-08 01:08:51 -04:00
parent c73ae7d443
commit a9778eaabe
5 changed files with 326 additions and 26 deletions
+13 -5
View File
@@ -829,13 +829,21 @@ gtest_discover_tests(MobileGLIntegrationTest
# table, and LookupVaoDrawMemo); it steers nothing on DirectGLES, and a lane that configured it
# there would be a permanent skip claiming to be a control.
#
# It gets TWO lanes, because there are two arms and the control has to cover the one P2 SHIPS.
# It gets TWO lanes, because there are two arms and the control has to reach the one P2 SHIPS.
# `AbaControl` is D18's lane verbatim (MOBILEGL_PIPE_PUSH=0, the pre-handle arm) and defeats the
# lifetime-id/address guards; `AbaControlHandles` runs the handle arm (MOBILEGL_PIPE_LEGACY_MEMOS=0,
# the default push mask) and defeats the {slot, gen} GENERATION, which is what makes the re-keyed
# memos ABA-safe. With only the first lane the control says nothing at all about the re-key: the
# handle arm is not executed under MOBILEGL_PIPE_PUSH=0, so every guard it would have to defeat is
# in another branch.
# the default push mask) and defeats the object identity that SELECTS THE SLOT - the key the handle
# arm ships. With only the first lane the control says nothing at all about the re-key: the handle
# arm is not executed under MOBILEGL_PIPE_PUSH=0, so every guard it would have to defeat is in
# another branch.
#
# NEITHER lane exercises the GENERATION half of {slot, gen}, and no lane of this shape can. Magma's
# mint has no death notification and returns a slot only through its age sweep (256/1024 boundaries,
# MagmaPipeArms.h), so the five frame boundaries this scenario issues always hand the replacement a
# brand-new slot at Gen 1; a real reuse needs >= 1024 idle boundaries, which puts the two draws in
# different frames - where the only pixel-visible memo declines by design. The generation is covered
# by the unit suite MG_Test/Pipe/MagmaPipeIdentityTest.cpp instead, which drives a real
# retire -> reuse; MagmaPipeAbaControlDefeatsIdentity carries the measurement.
#
# The two PUSH-ONLY knobs of those arms are set only in a push build, and the lane NAMES are
# unaffected by that (an ENVIRONMENT property is not part of a test's name, so G2 still sees the