[MG_Pipe, MG_Remote, MG_Backend, MG_Test] (Disaggregated): P5e ID-128 - admission gains the escalation disjunct

- The integrator's census over the merged tree left two markers nothing admitted, both
  DirectGLES.Split.ClientVertexArrayScenario: GetBoundVertexArray@DrawArrays. Disjunct 1 fails
  (draw_vbo is kWaitNone) and disjunct 2 fails (that row's retiring phase names P5e, correctly,
  for the ORDINARY draw path vi and mv retired). But these two entries pull the pair on a record
  the client IS parked behind: MGPipeBarriered escalates a draw carrying client vertex arrays
  (ID-83), ID-82 puts client arrays outside P5e entirely, and the read site
  (SyncClientSideVertexArraysForDrawArrays) aborts by its own name if it is ever applied
  unbarriered. The pull is legal; no static table could see it.
- Third disjunct: the record was barriered BY ESCALATION, i.e. wireSaysBarriered(op, payload,
  applierState) && MGPipeWaitClassFor(op) == kWaitNone. Both halves were already computed on
  every record - ApplyOne computes the predicate unconditionally for ID-103's reason - so this is
  one more bit stamped beside the barriered stamp, not new plumbing.
- It is a SEPARATE flag rather than a refinement of the barriered stamp, because the two answer
  different questions: the stamp answers "is the client parked" (the allocator and registry
  guards ask that), this answers "and for a reason the static table cannot see" (only the strict
  knob asks). Its default is false - the answer that admits nothing - which is the opposite of
  the stamp's default and deliberately so.
- The marker says WHICH disjunct: ADMITTED when the generated table answered, ADMITTED-ESCALATED
  when only the record did. That keeps the lane's comparison against --print-admitted exact
  instead of widening the static list with every pair that could ever escalate - and the tag sits
  in the slot the grammar already has for the reason, so Fatal{UnmigratedPipeInput filters are
  untouched.
- Escalation also happens to be the truer justification for GetTransformFeedbackProgram@DrawArrays,
  which ID-125 admits by phase. Both now hold; ID-125's disjunct is kept, as ruled.
- Red-once: AnEscalatedRecordsPullIsAdmittedAndSaysWhy. Dropping `&& !escalated` from the strict
  arm kills it - "FieldOwnershipTest.cpp:938: Failure / Actual: false / signal 6" with
  'Fatal{UnmigratedPipeInput, "GetBoundVertexArray@DrawArrays"} [BARRIER-PULLED,
  MOBILEGL_IPC_STRICT_ERRORS=1, retires in P5e (Espryt unbarriered), P7 (Magma)]'. Its control is
  StrictErrorsTurnsABarrierPulledReadIntoANamedAbort, which drives the identical pull with the
  flag at its default and must keep aborting.
This commit is contained in:
2026-09-18 12:19:14 -04:00
parent faf19a6aaa
commit a0836568ac
5 changed files with 124 additions and 4 deletions
@@ -94,11 +94,16 @@ namespace {
MG_Config::Ipc.StrictErrors = false;
MGPipeServerClearVerbBoundary();
MGPipeResetResidualPullCountForTesting();
// P5e (gl), ID-128: the escalation flag is per-thread state PipeApplier::ApplyOne
// stamps on every record, so a case that set it would otherwise hand its answer to
// the next one - and the arm it selects is "admitted", so the leak is silent.
MGPipeApplierSetCurrentRecordBarrieredByEscalation(false);
#endif
}
void TearDown() override {
#if MOBILEGL_BUILD_DISAGGREGATED
MGPipeServerClearVerbBoundary();
MGPipeApplierSetCurrentRecordBarrieredByEscalation(false);
MG_Config::Ipc.StrictErrors = false;
#endif
MG_State::pGLContext = Move(m_previous);
@@ -898,6 +903,49 @@ TEST_F(FieldOwnershipTest, TheSameReadWithoutStrictErrorsSurvivesAndIsCounted) {
EXPECT_EQ(r.Log.find("Fatal{"), std::string::npos) << r.Log;
}
// P5e (gl) RED-ONCE, ID-128: THE THIRD DISJUNCT, AND IT IS A RUNTIME FACT ABOUT THE RECORD.
//
// GetBoundVertexArray@DrawArrays is this phase's debt on an unbarriered verb, so the static
// table rejects it and strict aborts - that is the case two blocks up, and it must keep doing
// that for the ORDINARY draw path vi and mv retired. But the two
// DirectGLES.Split.ClientVertexArrayScenario entries pull the same pair on a record the client
// IS parked behind: MGPipeBarriered escalates a draw carrying client vertex arrays (ID-83), and
// ID-82 puts client arrays outside P5e entirely - under run-ahead the client refuses them by
// name and staging is P8's. The read site (SyncClientSideVertexArraysForDrawArrays) aborts with
// Fatal{RoleViolation, "MGPipeSlots"} if it is ever applied unbarriered, so the pull is legal.
//
// ONE PAIR, TWO VERDICTS, DECIDED BY THE RECORD AND NOT BY THE TABLE. That is why the flag is
// stamped in ApplyOne beside the barriered stamp rather than folded into the generated table:
// no table indexed by (field, verb) can tell these two entries apart.
//
// THE RED: drop `&& !escalated` from CountBarrierPull's strict arm and this case dies of
// SIGABRT on Fatal{UnmigratedPipeInput, "GetBoundVertexArray@DrawArrays"}.
//
// THE CONTROL IS THE CASE TWO BLOCKS UP, NOT A SECOND CHILD HERE.
// StrictErrorsTurnsABarrierPulledReadIntoANamedAbort drives the IDENTICAL pull - same field,
// same verb, same knob - with the escalation flag at its `false` default, and asserts the abort.
// Reading the log delta of two forks in one case is what a shared log file cannot support.
TEST_F(FieldOwnershipTest, AnEscalatedRecordsPullIsAdmittedAndSaysWhy) {
const ChildResult escalated = RunInChild([] {
MG_Config::Ipc.StrictErrors = true;
// What PipeApplier::ApplyOne stamps for a draw whose payload carries kDrawClientArrays:
// MGPipeBarriered says barriered, the static wait class of draw_vbo says kWaitNone.
MGPipeApplierSetCurrentRecordBarrieredByEscalation(true);
MGPipeServerStampVerbBoundary(MGPipeVerb::DrawArrays);
(void)gPipeInputs.GetBoundVertexArray();
if (MGPipeResidualPullCount() != 1) ::_exit(7);
});
ASSERT_TRUE(ExitedWith(escalated, 0)) << DescribeStatus(escalated) << "\n" << escalated.Log;
EXPECT_EQ(escalated.Log.find("Fatal{UnmigratedPipeInput"), std::string::npos) << escalated.Log;
// THE MARKER SAYS WHICH DISJUNCT, and that is what keeps the lane's comparison exact: a pair
// admitted by the table is checkable against `--print-admitted`, one admitted by escalation
// is not in any static list and must not widen one.
EXPECT_NE(escalated.Log.find("Admitted{UnmigratedPipeInput, \"GetBoundVertexArray@DrawArrays\"}"
" [BARRIER-PULLED, ADMITTED-ESCALATED, retires in "),
std::string::npos)
<< escalated.Log;
}
// THE SEVEN STICKY FORWARDS ARE NOT EXEMPT FROM THE DETECTOR, which is what this case has
// always been for - it was written as "strict PROMOTES them to a Fatal" because before ID-125
// every barrier-pulled read under strict aborted.