[Fix] (Pipe, Purity): end a declared verb honestly, and gate the header MG_State now includes

- MGPipeLeaveVerb() bumps the serial and puts the current verb back to none, so a test that
  drives a backend helper directly stops declaring where it says it stops and a later
  unguarded read aborts as "<Field>@<none>" instead of naming an unrelated verb
- check_include_closure.py gains a fourth probe: F2 put MGP_NOTE_MUTATION into frontend
  mutators, so MG_State includes MG_Pipe/PipeMutation.h and that header must never reach
  back into MG_State, MG_Impl or a backend
This commit is contained in:
2026-09-06 06:15:56 -04:00
parent ef6227e19b
commit 62a7786184
4 changed files with 44 additions and 5 deletions
+10
View File
@@ -552,6 +552,16 @@ namespace MobileGL::MG_Pipe {
ctx->RecordError(code, Move(info)); ctx->RecordError(code, Move(info));
} }
void MGPipeLeaveVerb() {
PipeInputs& inputs = gPipeInputs;
#if MOBILEGL_PIPE_POISON
// Same bump the next fill would make, without a verb to fill from: no field is
// stamped, so every stamp this verb made falls behind the serial.
++MGPipeFillAccess::Filled(inputs).CurrentVerbSerial;
#endif
MGPipeFillAccess::SetVerb(inputs, MGPipeVerb::kVerbCount);
}
// ---- the filler ---- // ---- the filler ----
void MGPipeFillForVerb(MGPipeVerb verb) { void MGPipeFillForVerb(MGPipeVerb verb) {
PipeInputs& inputs = gPipeInputs; PipeInputs& inputs = gPipeInputs;
+9
View File
@@ -24,6 +24,15 @@ namespace MobileGL::MG_Pipe {
// runs the entry compare against a second snapshot (P1 brief D8). // runs the entry compare against a second snapshot (P1 brief D8).
void MGPipeFillForVerb(MGPipeVerb verb); void MGPipeFillForVerb(MGPipeVerb verb);
// Ends the verb in flight without starting another: bumps the serial, so every field the
// verb stamped goes stale, and puts the current verb back to "none", so a read made after
// it aborts as Fatal{UnmigratedPipeInput, "<Field>@<none>"} - which is what such a read
// is - instead of naming whichever verb happened to be filled last. Nothing in the GL
// entry points calls this: a real verb is always followed by the next verb's fill. It
// exists for a caller that drives a backend helper directly and wants its declaration to
// stop where it says it stops (MG_Test/ScopedPipeVerb.h).
void MGPipeLeaveVerb();
// PipeFill.cpp. Negative control B (P1 brief D6): the filler withholds the STAMP - never // PipeFill.cpp. Negative control B (P1 brief D6): the filler withholds the STAMP - never
// the value - of `field` at `verb`, so that verb's read of it is // the value - of `field` at `verb`, so that verb's read of it is
// Fatal{UnmigratedPipeInput, "Field@Verb"} while every other verb is unaffected. The // Fatal{UnmigratedPipeInput, "Field@Verb"} while every other verb is unaffected. The
+6 -5
View File
@@ -65,13 +65,14 @@ namespace MobileGL::MG_Test {
} }
#if MOBILEGL_PIPE_PUSH #if MOBILEGL_PIPE_PUSH
~ScopedPipeVerb() { MG_Pipe::MGPipeFillForVerb(kLeaveVerb); } // Leaving bumps the serial and puts the current verb back to "none": every field
// this scope stamped goes stale, and a later test that forgets its own declaration
// aborts with "<Field>@<none>" rather than with the name of a verb it never issued.
// That matters when the suite runs as one process (a developer running the test
// binary directly, rather than one ctest entry per case).
~ScopedPipeVerb() { MG_Pipe::MGPipeLeaveVerb(); }
private: private:
// Leaving is a fill of the narrowest verb class there is: kQuery names one field, so
// the serial bump lands and every field this scope stamped falls behind it. The one
// field is a transform-feedback counter read - no side effect, and nothing to undo.
static constexpr MG_Pipe::MGPipeVerb kLeaveVerb = MG_Pipe::MGPipeVerb::GetGpuTimestampNs;
MG_Pipe::MGPipeVerb m_verb; MG_Pipe::MGPipeVerb m_verb;
#endif #endif
}; };
+19
View File
@@ -106,6 +106,25 @@ PROBES = [
"Why": "P7 ships the reflection artifacts over the wire; the archive header must not " "Why": "P7 ships the reflection artifacts over the wire; the archive header must not "
"depend on the compiler front end that produced them.", "depend on the compiler front end that produced them.",
}, },
{
"Name": "mutation-header",
"Header": "MobileGL/MG_Pipe/PipeMutation.h",
"Tu": "#include <MG_Pipe/PipeMutation.h>\n",
"Forbidden": [
"MobileGL/MG_Impl/",
"MobileGL/MG_State/GLState/Core.h",
"MobileGL/MG_Remote/",
],
"Allow": [],
"TextLimits": {},
"Why": "P1 finding F2 put MGP_NOTE_MUTATION in frontend mutators, so MG_State now "
"includes this header (TextureState.h, SamplerObject.h). It may only DECLARE "
"the notice: reaching MG_Impl would pull the fill implementation into the "
"state machine that calls it, and reaching GLState/Core.h would close the "
"cycle back onto the context. The MGPipeTypes -> BackendObject -> TextureEnum "
"reach it inherits from MGPipe.h is D1's known exception, which is why gate A "
"asserts MGPipeValueTypes.h rather than MGPipeTypes.h.",
},
{ {
"Name": "wire-header", "Name": "wire-header",
"Header": "MobileGL/MG_Remote/Transport/ITransport.h", "Header": "MobileGL/MG_Remote/Transport/ITransport.h",