mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
[Refactor, Test] (docs/ci, docs/Disaggregated, MG_Backend/MGPipe, MG_Remote/Client, MG_Remote/Transport, MG_Test/Wire, MobileGL, scripts/data, tools/trace_replay): delete the MOBILEGL_IPC_VERB_BARRIER knob
- retires RemoteRunAhead.ClearingTheVerbBarrierDisarmsRunAheadEvenWithTheCapBit
This commit is contained in:
+1
-9
@@ -696,12 +696,6 @@ namespace MobileGL::MG_Config {
|
||||
// platform and the backend's POST self-test allow it; 0 = never grant it (the session
|
||||
// runs T2 and says so). Read by the server role only; kept through ServerSpawn.
|
||||
Uint32 AllowAdoptT0 = 1;
|
||||
// MOBILEGL_IPC_VERB_BARRIER: 1 = the client blocks at every verb boundary until
|
||||
// appliedSeq reaches its emitSeq (R-1). 0 is the negative control: it is EXPECTED to
|
||||
// be red, because 31 of the 63 PipeInputs fields are still pulled from a live
|
||||
// GLContext by the client's residual fill and a free-running queue lets the server
|
||||
// read a FUTURE value of them.
|
||||
Uint32 VerbBarrier = 1;
|
||||
// MOBILEGL_IPC_RUN_AHEAD (P5e, docs/archive/contracts/CONTRACT-P5E.md §1): 1 = after publishing an
|
||||
// UNBARRIERED record the client returns immediately instead of waiting for its apply.
|
||||
// It is one half of a conjunction and never a switch on its own - the client arms
|
||||
@@ -712,9 +706,7 @@ namespace MobileGL::MG_Config {
|
||||
// 0 IS THE A/B CONTROL AND NOT A NEGATIVE ONE: the server code, the fill decision and
|
||||
// every record are identical on both arms, and the only difference is whether the
|
||||
// client waits. That is what makes "is the picture the same" a question about the wait
|
||||
// rule alone. MOBILEGL_IPC_VERB_BARRIER=0 keeps its own meaning and stays the
|
||||
// lockstep arm's negative control; under run-ahead it is the one that must go red, at
|
||||
// the first barriered row's stale pull.
|
||||
// rule alone.
|
||||
//
|
||||
// Forced to 0 by MOBILEGL_PIPE_VERIFY, beside BatchWaits: the comparator needs a
|
||||
// client-filled gPipeInputs block for every verb, and run-ahead is precisely the
|
||||
|
||||
@@ -609,7 +609,6 @@ namespace MobileGL::MG_ConfigLoader {
|
||||
ipc.AdoptTier = QueryEnvUint32("MOBILEGL_IPC_ADOPT_TIER", MG_Config::kAdoptTierUnset, 0, 2);
|
||||
// P11 B2: the server's own T0 allow switch (Config.h).
|
||||
ipc.AllowAdoptT0 = QueryEnvUint32("MOBILEGL_IPC_ALLOW_ADOPT_T0", 1, 0, 1);
|
||||
ipc.VerbBarrier = QueryEnvUint32("MOBILEGL_IPC_VERB_BARRIER", 1, 0, 1);
|
||||
// P5e (docs/archive/contracts/CONTRACT-P5E.md §1). The wait rule's A/B, parsed here like every
|
||||
// other IPC knob and armed only where the server publishes kCapRunAheadApply.
|
||||
ipc.RunAhead = QueryEnvUint32("MOBILEGL_IPC_RUN_AHEAD", 1, 0, 1);
|
||||
@@ -716,7 +715,7 @@ namespace MobileGL::MG_ConfigLoader {
|
||||
// log that does not name it makes every run ambiguous about its own configuration.
|
||||
MGLOG_I("Config: IPC ring=%uMiB stage=%uMiB wire-deferred=%uMiB spin=%uus event-wait=%ums "
|
||||
"persistent-block=%uKiB create-window=%u pbo-readback-sync=%u poll-escalate=%u "
|
||||
"adopt-tier=%s allow-t0=%u verb-barrier=%u run-ahead=%u present-credit=%u control-timeout=%ums "
|
||||
"adopt-tier=%s allow-t0=%u run-ahead=%u present-credit=%u control-timeout=%ums "
|
||||
"cold-start=%ums strict=%d audit=%d role-split-state=%d affinity='%s' surface=%s",
|
||||
ipc.RingMb, ipc.StageMb, MG_Config::RecordArm.WireDeferredMb, ipc.SpinUs, ipc.EventWaitMs,
|
||||
MG_Config::RecordArm.PersistentBlockKb, ipc.CreateWindow, ipc.PboReadbackSync, ipc.PollEscalate,
|
||||
@@ -724,15 +723,9 @@ namespace MobileGL::MG_ConfigLoader {
|
||||
: ipc.AdoptTier == 1 ? "1"
|
||||
: ipc.AdoptTier == 2 ? "2"
|
||||
: "unset(T0)",
|
||||
ipc.AllowAdoptT0, ipc.VerbBarrier, ipc.RunAhead, ipc.PresentCredit, ipc.ControlTimeoutMs,
|
||||
ipc.ColdStartMs, static_cast<int>(ipc.StrictErrors), static_cast<int>(ipc.Audit),
|
||||
static_cast<int>(ipc.RoleSplitState), ipc.ServerAffinity.c_str(),
|
||||
MG_Config::ServerOwnedWindowSurfaces() ? "server" : "offscreen");
|
||||
if (ipc.VerbBarrier == 0) {
|
||||
MGLOG_W("Config: MOBILEGL_IPC_VERB_BARRIER=0 is the R-1 NEGATIVE CONTROL and is "
|
||||
"expected to fail: the client still pulls 31 of 63 PipeInputs fields from a "
|
||||
"live GLContext, so an unbarriered queue lets the server read future values");
|
||||
}
|
||||
ipc.AllowAdoptT0, ipc.RunAhead, ipc.PresentCredit, ipc.ControlTimeoutMs, ipc.ColdStartMs,
|
||||
static_cast<int>(ipc.StrictErrors), static_cast<int>(ipc.Audit), static_cast<int>(ipc.RoleSplitState),
|
||||
ipc.ServerAffinity.c_str(), MG_Config::ServerOwnedWindowSurfaces() ? "server" : "offscreen");
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -965,9 +965,7 @@ namespace MobileGL::MG_Pipe {
|
||||
// PLAIN, NOT ATOMIC, AND THAT IS THE SAME RULING TABLE 3 MAKES FOR gPipeInputS ITSELF
|
||||
// (CONTRACT-P5.md section 4): the verb barrier leaves at most one of {GL thread, apply
|
||||
// thread} runnable, so there is one writer at any instant. This counter, m_serverStampedVerb
|
||||
// and FilledGen[] all rest on that and on nothing else - so MOBILEGL_IPC_VERB_BARRIER=0,
|
||||
// R-1's negative control, is a data race on all three as well as the correctness failure it
|
||||
// is there to show. It is expected to be red; it is not expected to be meaningful.
|
||||
// and FilledGen[] all rest on that and on nothing else.
|
||||
Uint64 MGPipeResidualPullCount();
|
||||
void MGPipeResetResidualPullCountForTesting();
|
||||
|
||||
|
||||
@@ -236,14 +236,6 @@ namespace MobileGL::MG_Remote::Client {
|
||||
#endif
|
||||
}
|
||||
|
||||
Bool VerbBarrierFromConfig() {
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
return MG_Config::Ipc.VerbBarrier != 0;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
// ---- c1: the barrier's two flags ------------------------------------------------
|
||||
//
|
||||
// thread_local for the client's own "am I waiting", a shared atomic for "is the apply
|
||||
@@ -1810,14 +1802,6 @@ namespace MobileGL::MG_Remote::Client {
|
||||
return MOBILEGL_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
m_barrierArmed = VerbBarrierFromConfig();
|
||||
if (!m_barrierArmed) {
|
||||
MGLOG_W("MG_Remote client: MOBILEGL_IPC_VERB_BARRIER=0 - this is R-1's NEGATIVE "
|
||||
"CONTROL and is EXPECTED to be red. 31 of the 63 PipeInputs fields are still "
|
||||
"pulled from a live GLContext by the client's residual fill, so a free-running "
|
||||
"queue lets the server read a FUTURE value of them");
|
||||
}
|
||||
|
||||
// ---- 6. the client's segment table. IT MUST NOT INSTALL THE PROCESS RESOLVER: there is
|
||||
// exactly one gMGPipeSegmentResolver per process, the SERVER role owns it (table 3), and
|
||||
// the client never resolves a span at all - it only ever writes Ptr = nullptr (R-2's
|
||||
@@ -1989,9 +1973,8 @@ namespace MobileGL::MG_Remote::Client {
|
||||
// permits submittedSeq to be published lazily and RingProducer::Publish encourages batching
|
||||
// the publish, so the shared watermark is allowed to lag the emitter - and a drain
|
||||
// that waited for `appliedSeq >= submittedSeq` would then under-wait and free an
|
||||
// emitter-owned var-tail while a record still names it. With the verb barrier armed
|
||||
// the two are equal; under MOBILEGL_IPC_VERB_BARRIER=0, R-1's negative control that
|
||||
// the phase has to run once, they are not.
|
||||
// emitter-owned var-tail while a record still names it. Run-ahead and the batched
|
||||
// waits return before the apply, so the two may differ when Stop() runs.
|
||||
const Uint64 submitted = m_producer.LastPublishedSeq();
|
||||
m_producer.PublishAndNotify(submitted);
|
||||
if (submitted != 0 &&
|
||||
@@ -2338,14 +2321,6 @@ namespace MobileGL::MG_Remote::Client {
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_barrierArmed && !ownsReplySlot) {
|
||||
// R-1's NEGATIVE CONTROL ARM, and the only thing it turns off is the barrier. A
|
||||
// reply-slot row still waits: the answer is not derivable here and R-5 forbids
|
||||
// inventing one, so MOBILEGL_IPC_VERB_BARRIER=0 makes the queue free-running, not
|
||||
// the client clairvoyant.
|
||||
return seq;
|
||||
}
|
||||
|
||||
++m_recordWaits;
|
||||
if (MG_Config::Ipc.WaitStats && RunAheadArmed()) ++g_waitCensus.recordWaits[static_cast<SizeT>(op)];
|
||||
const BarrierWaitScope waiting;
|
||||
@@ -2441,10 +2416,6 @@ namespace MobileGL::MG_Remote::Client {
|
||||
return seq;
|
||||
}
|
||||
|
||||
Bool ClientSession::BarrierArmed() const {
|
||||
return m_barrierArmed;
|
||||
}
|
||||
|
||||
// ---- P5e (ra): the run-ahead latch and the waits it leaves behind ---------------------
|
||||
|
||||
Bool ClientSession::RunAheadArmed() const {
|
||||
@@ -2465,8 +2436,8 @@ namespace MobileGL::MG_Remote::Client {
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
// THE CONJUNCTION, IN THE CONTRACT'S OWN ORDER (§1). The caps read is last because it
|
||||
// is the only one that costs a mirror load, and because it is the only one whose answer
|
||||
// can change: the other two are process configuration.
|
||||
const Bool armed = m_barrierArmed && MG_Config::Ipc.RunAhead != 0 && Caps().HasCap(MG_Pipe::kCapRunAheadApply);
|
||||
// can change: the other one is process configuration.
|
||||
const Bool armed = MG_Config::Ipc.RunAhead != 0 && Caps().HasCap(MG_Pipe::kCapRunAheadApply);
|
||||
if (!m_runAheadLatched) {
|
||||
m_runAheadLatched = true;
|
||||
m_runAheadArmed = armed;
|
||||
@@ -2475,7 +2446,7 @@ namespace MobileGL::MG_Remote::Client {
|
||||
"kCapRunAheadApply, so an unbarriered record is published and not "
|
||||
"waited for (CONTRACT-P5E §2.3), paced by a present credit of %u",
|
||||
MG_Config::Ipc.PresentCredit);
|
||||
} else if (MG_Config::Ipc.RunAhead != 0 && m_barrierArmed) {
|
||||
} else if (MG_Config::Ipc.RunAhead != 0) {
|
||||
// THE KNOB IS NOT A SWITCH ON ITS OWN, and an operator who set it has to be
|
||||
// told that, or an A/B arm that measured nothing looks like an A/B arm that
|
||||
// measured no difference (Config.h's own argument for parsing it everywhere).
|
||||
@@ -2747,10 +2718,6 @@ namespace MobileGL::MG_Remote::Client {
|
||||
if (MG_Config::Ipc.BatchWaits != 0) return;
|
||||
if (Server::ServerLoop::OnApplyThread()) return; // the applier owns the block inside a verb
|
||||
if (!ClientSessionInstance().Started()) return; // the bring-up window pre-dates the roles
|
||||
// MOBILEGL_IPC_VERB_BARRIER=0 is R-1's NEGATIVE CONTROL: the single-writer rule is off
|
||||
// by the operator's own hand there, and EmitAndWait's Fatal{BarrierViolation} owns the
|
||||
// red. Firing here instead would pre-empt the control's evidence line.
|
||||
if (!ClientSessionInstance().BarrierArmed()) return;
|
||||
if (!ApplyThreadIsInsideApplier()) return;
|
||||
// LAST, because it is the only thread_local left in the chain: an emutls call per
|
||||
// access in a shared library, and it must stay thread-local (see InBarrierWait's note
|
||||
|
||||
@@ -193,7 +193,7 @@ namespace MobileGL::MG_Remote::Client {
|
||||
Wire::PipeWireEncoder& Encoder();
|
||||
CapsMirror& Caps();
|
||||
|
||||
// Emit one record and, if the barrier is armed, wait for it. `replyOut`/`replyBytes`
|
||||
// Emit one record and wait for it unless the wait rule lets it go. `replyOut`/`replyBytes`
|
||||
// name where a kReplySlot answer lands; pass {nullptr, 0} for a call that has none.
|
||||
// Returns the record's seq, which is also its reply-slot id.
|
||||
//
|
||||
@@ -280,18 +280,13 @@ namespace MobileGL::MG_Remote::Client {
|
||||
// !willReadReply, and !willReadReply on a row with no reply slot, are Fatal by name: the
|
||||
// first would wait for an answer never sent, the second discards one never posted.
|
||||
|
||||
// MOBILEGL_IPC_VERB_BARRIER. False is the R-1 negative control and is EXPECTED to be
|
||||
// red; it must be run once and the way it goes red recorded.
|
||||
Bool BarrierArmed() const;
|
||||
|
||||
// ---- P5e (ra): the wait rule (CONTRACT-P5E §1, §2.3, §2.4, §2.5) -------------------
|
||||
|
||||
// A CONJUNCTION OF THREE, LATCHED ONCE: `m_barrierArmed && Ipc.RunAhead &&
|
||||
// Caps().HasCap(kCapRunAheadApply)`. The third conjunct is the server's own statement
|
||||
// that it applies an unbarriered record without reading client memory, so run-ahead is
|
||||
// never something the client can turn on by itself - on Magma, and on Espryt until the
|
||||
// integration commit publishes bit 10, this answers false and EmitAndWaitTails runs
|
||||
// today's lockstep path byte for byte.
|
||||
// A CONJUNCTION OF TWO, LATCHED ONCE: `Ipc.RunAhead && Caps().HasCap(kCapRunAheadApply)`.
|
||||
// The second conjunct is the server's own statement that it applies an unbarriered record
|
||||
// without reading client memory, so run-ahead is never something the client can turn on
|
||||
// by itself - on Magma, and on Espryt until the integration commit publishes bit 10,
|
||||
// this answers false and EmitAndWaitTails runs today's lockstep path byte for byte.
|
||||
//
|
||||
// LATCHED AT THE FIRST CAPS ADOPTION AFTER Start, AND A LATER SNAPSHOT MAY ONLY TURN IT
|
||||
// OFF. A caps re-run cannot promote a lockstep server to a run-ahead one mid-frame:
|
||||
@@ -540,7 +535,6 @@ namespace MobileGL::MG_Remote::Client {
|
||||
|
||||
Wire::PipeWireEncoder m_encoder;
|
||||
Wire::SegmentTable m_segments;
|
||||
Bool m_barrierArmed = true;
|
||||
// P5e (ra): the latch of RunAheadArmed() and the fact that it has been taken once.
|
||||
Bool m_runAheadArmed = false;
|
||||
Bool m_runAheadLatched = false;
|
||||
|
||||
@@ -267,9 +267,8 @@ namespace MobileGL::MG_Remote::Transport {
|
||||
// encourages batching the publish, so the shared watermark may lag the
|
||||
// emitter - and a drain that waits for `appliedSeq >= submittedSeq` would
|
||||
// then under-wait and free an emitter's var-tail while a record still
|
||||
// names it. With the verb barrier armed the two are equal; with
|
||||
// MOBILEGL_IPC_VERB_BARRIER=0, R-1's negative control which the phase has
|
||||
// to run once, they are not.
|
||||
// names it. Run-ahead and the batched waits publish without waiting, so
|
||||
// at teardown the two may differ.
|
||||
std::uint64_t LastPublishedSeq() const { return m_lastPublishedSeq; }
|
||||
|
||||
// The verb barrier's wait, AND the reply's wait: they are the same wait
|
||||
|
||||
@@ -54,7 +54,6 @@ namespace {
|
||||
::alarm(10);
|
||||
MG_Config::Transport = MG_Config::TransportMode::Spawn;
|
||||
MG_Config::Ipc.RunAhead = 1;
|
||||
MG_Config::Ipc.VerbBarrier = 1;
|
||||
int reserve = ::socket(AF_INET, SOCK_STREAM, 0);
|
||||
ASSERT_GE(reserve, 0);
|
||||
sockaddr_in address{};
|
||||
|
||||
@@ -44,9 +44,6 @@ void CheckE1Boundary(Bool serverCap, MGPWireOp op, Bool expectWait, const char*
|
||||
E1Emit emit = E1Emit::Rule) {
|
||||
const auto child = RunInChild([=] {
|
||||
::alarm(15);
|
||||
const char* barrier = std::getenv("MOBILEGL_IPC_VERB_BARRIER");
|
||||
if (barrier && std::strcmp(barrier, "0") && std::strcmp(barrier, "1")) ::_exit(80);
|
||||
MG_Config::Ipc.VerbBarrier = barrier ? static_cast<Uint32>(barrier[0] - '0') : 1;
|
||||
MG_Config::Ipc.BatchWaits = 1;
|
||||
MG_Config::Ipc.RunAhead = 1;
|
||||
MG_Config::Ipc.SpinUs = 0;
|
||||
@@ -84,9 +81,9 @@ void CheckE1Boundary(Bool serverCap, MGPWireOp op, Bool expectWait, const char*
|
||||
if (session.WaitForApplied(seq, 2000) != Transport::SessionWait::Reached) ::_exit(85);
|
||||
if (seq <= before || peer.seen.load() != 1 || session.Control()->Progress.appliedSeq.load() < seq) ::_exit(86);
|
||||
const Bool correct = expectWait ? parked && appliedBeforeReturn : !parked && !appliedBeforeReturn;
|
||||
std::printf("E1 observation: op=%s cap=%u barrier=%u parked=%u applied_before_return=%u emitted=1 peer_seen=1\n",
|
||||
std::printf("E1 observation: op=%s cap=%u emit=%s parked=%u applied_before_return=%u emitted=1 peer_seen=1\n",
|
||||
op == MGPWireOp::ApplierReset ? "ApplierReset" : "Clear",
|
||||
static_cast<unsigned>(serverCap), MG_Config::Ipc.VerbBarrier,
|
||||
static_cast<unsigned>(serverCap), emit == E1Emit::PublishOnly ? "publish-only" : "rule",
|
||||
static_cast<unsigned>(parked), static_cast<unsigned>(appliedBeforeReturn));
|
||||
std::fflush(stdout);
|
||||
session.Stop();
|
||||
|
||||
@@ -2730,29 +2730,6 @@ TEST(RemoteRunAhead, TheKnobAloneDoesNotArmRunAheadWithoutTheServersCapBit) {
|
||||
ExpectChildSuccess(child);
|
||||
}
|
||||
|
||||
// §1 again, from the other side: MOBILEGL_IPC_VERB_BARRIER=0 is the LOCKSTEP arm's negative
|
||||
// control and it disarms run-ahead outright, because the barrier is the first term of the
|
||||
// conjunction. Under run-ahead its documented red is the first barriered row's stale pull -
|
||||
// that one is a scenario, not a unit case; what is pinned here is that the two controls do not
|
||||
// silently compose into a third mode nobody designed.
|
||||
TEST(RemoteRunAhead, ClearingTheVerbBarrierDisarmsRunAheadEvenWithTheCapBit) {
|
||||
const auto child = RunInChild([] {
|
||||
MG_Config::Ipc.RunAhead = 1;
|
||||
MG_Config::Ipc.VerbBarrier = 0;
|
||||
::alarm(15);
|
||||
MG_Config::Transport = MG_Config::TransportMode::InProcess;
|
||||
Srv::ServerSessionInstance().SetCapabilityBits(static_cast<Uint64>(MG_Pipe::kCapRunAheadApply));
|
||||
Srv::ServerSessionInstance().SetConsumedSubsystems(kMGPipeSubsystemsMigratedAtP4a);
|
||||
Srv::ServerSessionInstance().SetBackend(ControlCapsPeer());
|
||||
if (ClientSessionInstance().Start(MG_Config::TransportMode::InProcess, {}) != MOBILEGL_OK) {
|
||||
::_exit(81);
|
||||
}
|
||||
if (ClientSessionInstance().RunAheadArmed()) ::_exit(101);
|
||||
ClientSessionInstance().Stop();
|
||||
});
|
||||
ExpectChildSuccess(child);
|
||||
}
|
||||
|
||||
// §2.4's RED-ONCE, BY COUNT. With a credit of 1 the client may have one present in flight, so
|
||||
// the second and third presents each pay the credit before they encode - `credit-waits` is 2
|
||||
// after three presents and the present-ack watermark has reached 2. Delete the
|
||||
|
||||
@@ -1244,9 +1244,8 @@ TEST(SessionTest, ABorrowedSlotIsNotHandedBackUntilItIsReleased) {
|
||||
// q-1. Teardown's drain keys on the PRODUCER's own last-published seq, not on
|
||||
// RingControl::submittedSeq: Ring.h's header comment permits that watermark to be published lazily, so a
|
||||
// drain that trusted it would under-wait and free an emitter-owned var-tail while a record
|
||||
// still names it. With the verb barrier armed the two are equal; under
|
||||
// MOBILEGL_IPC_VERB_BARRIER=0 - R-1's negative control, which the phase must run once - they
|
||||
// are not.
|
||||
// still names it. Run-ahead and the batched waits publish without waiting, so at teardown the
|
||||
// two may differ.
|
||||
TEST(SessionTest, TheProducerRemembersWhatItPublishedEvenIfTheWatermarkLags) {
|
||||
SessionFixture session;
|
||||
ASSERT_TRUE(session.Build(TestSizes()));
|
||||
|
||||
@@ -57,7 +57,6 @@ namespace {
|
||||
::alarm(5);
|
||||
MG_Config::Transport = MG_Config::TransportMode::Spawn;
|
||||
MG_Config::Ipc.RunAhead = 1;
|
||||
MG_Config::Ipc.VerbBarrier = 1;
|
||||
MG_Config::Ipc.BatchWaits = 1;
|
||||
MG_Config::Ipc.PresentCredit = 3;
|
||||
Transport::SessionSegmentSizes sizes;
|
||||
|
||||
@@ -64,7 +64,6 @@ CMake:`MOBILEGL_BUILD_DISAGGREGATED`(OFF)、`MOBILEGL_BUILD_DISAGGREGATED_
|
||||
| `MOBILEGL_IPC_SURFACE` | `offscreen` | `server` = 使用 server 自有窗口,client 无头(P12) |
|
||||
| `MOBILEGL_IPC_RING_MB` / `MOBILEGL_IPC_STAGE_MB` | 8 / 32 | `SEG_CMD` / `SEG_STAGE`;stage 的 1/4 是内容分块预算 |
|
||||
| `MOBILEGL_IPC_RUN_AHEAD` | 1 | P5e run-ahead;只是合取式的一半(server 须发布 `kCapRunAheadApply`);`0` 是 A/B 对照 |
|
||||
| `MOBILEGL_IPC_VERB_BARRIER` | 1 | `0` 只作阴性对照,且同时关掉 run-ahead |
|
||||
| `MOBILEGL_FRAMES_IN_FLIGHT` | 3 | 1..64,两个后端共用(旧名 `MOBILEGL_MAGMA_FRAMESINFLIGHT` 为弃用别名):Magma 的帧上下文数;Espryt 在 present S 后等待帧 S+1-N 完成;split 的 present credit 由它导出为 max(1, N-2)(P15) |
|
||||
| `MOBILEGL_IPC_PRESENT_CREDIT` | 由 `MOBILEGL_FRAMES_IN_FLIGHT` 导出(默认 1) | 1..8,§14;显式设置覆盖导出值,仅供实验 |
|
||||
| `MOBILEGL_IPC_POLL_ESCALATE` | 64 | 未报告 fence 的零超时轮询本地答"未完成",同一 fence 连续第 N 次才往返(P10,`CONTRACT-P10.md` §1);`0` = 每次轮询都往返,是 A/B 对照 |
|
||||
|
||||
@@ -14,4 +14,4 @@
|
||||
| 17.4 | lane 隔离(P5) | 每条 split 条目私有日志;E1 与 E3(a) 的对照是常驻用例(`RemoteWaitBoundaryControl.AnUnwaitedPublishIsSeenAsNoWait`、`PersistentCoherentMapScenario.ThePushIsTheOnlyRouteAWriteTakesToTheServer`),不重跑 lane | 同上 |
|
||||
| 17.5 | class-C verb 迁移(P5b) | 迁移顺序由动态普查决定;一次迁移 = 一个发射器 + 一个 sink 体;记录逐字带 GL 调用参数,未测量的形式按名拒绝(规则 D) | [`docs/archive/notes/p5b/README.md`](../../archive/notes/p5b/README.md) |
|
||||
| 17.6 | 共享地址空间访问归零(P5c) | server 端纹理 staged shadow;`SEG_EVENT` 是唯一反向通道;sink / twin 按记录句柄解析;`applier_reset` / `object_death` 控制记录;角色守卫 `Fatal{RoleViolation}` | [`docs/archive/notes/p5c/README.md`](../../archive/notes/p5c/README.md) |
|
||||
| 17.7 | run-ahead(P5e) | 规则 F:unbarriered 记录的 apply 不读任何 client 内存(违反即具名 Fatal,与 strict 开关无关);`gPipeInputs` 是 server 角色内存;`RunAheadArmed()` 在首次 caps 采纳时锁存,只能关不能开;`RUN_AHEAD=0` 是 A/B 对照,`VERB_BARRIER=0` 会连带关掉 run-ahead | [`docs/archive/notes/p5e/README.md`](../../archive/notes/p5e/README.md) |
|
||||
| 17.7 | run-ahead(P5e) | 规则 F:unbarriered 记录的 apply 不读任何 client 内存(违反即具名 Fatal,与 strict 开关无关);`gPipeInputs` 是 server 角色内存;`RunAheadArmed()` 在首次 caps 采纳时锁存,只能关不能开;`RUN_AHEAD=0` 是 A/B 对照 | [`docs/archive/notes/p5e/README.md`](../../archive/notes/p5e/README.md) |
|
||||
|
||||
@@ -57,6 +57,7 @@ them wrong.
|
||||
| 2026-10-18 | `Negative controls (barrier, map push)` (`scripts/ci/split_negative_controls.sh`; skip-census lane `junit-integration-split/control`) | Test / `integration-split` | it reran the split entries with `MOBILEGL_IPC_VERB_BARRIER=0` and `MOBILEGL_IPC_PERSISTENT_BLOCK_KB=0`, two product knobs that exist only for it; what was left without them (the ownership check and the baseline tally) repeats `SplitLogPaths.PrivateAndDistinct` and the lane step's own `junit_tally.py --require-split-ran` | E1: `RemoteWaitBoundaryControl.AnUnwaitedPublishIsSeenAsNoWait` (unit, always on); E3(a): `PersistentCoherentMapScenario.ThePushIsTheOnlyRouteAWriteTakesToTheServer` on the inproc, spawn, tcp and small-ring split entries (always on) |
|
||||
| 2026-10-18 | `scripts/ci/wait_boundary_negative_control.py` | Test / `integration-split` | E1's body inside the step above: the three boundary cases rerun with the barrier off | `RemoteWaitBoundaryControl.AnUnwaitedPublishIsSeenAsNoWait`: the same ApplierReset published with no wait must read parked=0, the observation the owed waits read as parked=1 |
|
||||
| 2026-10-18 | `scripts/ci/control_smoke_test.sh`, `scripts/ci/testdata/stub_ctest.sh`, `scripts/ci/testdata/split_private_log_smoke.sh`, and the `reset` / `results` / `assertion` / `evidence` modes of `split_log_paths.py` | Test / pipe-gates (R-16) | smoke cases and helpers of the step above only | none needed |
|
||||
| 2026-10-18 | unit `RemoteRunAhead.ClearingTheVerbBarrierDisarmsRunAheadEvenWithTheCapBit`, and the `MOBILEGL_IPC_VERB_BARRIER` knob with its arm in the shipped client (`ClientSession`: the free-running `EmitAndWaitTails` return, the run-ahead latch term, the `gPipeInputs` guard's early return, the `verb-barrier=` field of the `Config: IPC` line) | Test / unit (split); product code | `0` was never a product mode, only E1's negative control, retired above; the unit case pinned that the control also disarmed run-ahead | `RemoteWaitBoundaryControl.AnUnwaitedPublishIsSeenAsNoWait`; `MOBILEGL_IPC_RUN_AHEAD=0` stays the lockstep A/B arm |
|
||||
|
||||
## Kept after investigation
|
||||
|
||||
|
||||
@@ -11207,7 +11207,6 @@
|
||||
"RemoteRunAhead.AnEventBurstBehindAnUnwaitedSequenceFlowControlsInsteadOfAborting",
|
||||
"RemoteRunAhead.AnIndexedDrawVerbIsUnbarrieredAndTouchesPipeInputsNotAtAll",
|
||||
"RemoteRunAhead.AnUnbarrieredVerbDoesNotFillPipeInputsAndIsAllowed",
|
||||
"RemoteRunAhead.ClearingTheVerbBarrierDisarmsRunAheadEvenWithTheCapBit",
|
||||
"RemoteRunAhead.OnlyTheTextureHalfOfResourceSubDataWantsItsReply",
|
||||
"RemoteRunAhead.TheKnobAloneDoesNotArmRunAheadWithoutTheServersCapBit",
|
||||
"RemoteWaitBoundaryControl.AnUnwaitedPublishIsSeenAsNoWait",
|
||||
|
||||
@@ -460,11 +460,11 @@ stacks before treating a local result as a statement about CI.
|
||||
|
||||
`run_tcp_matrix.py` uses a CTest JSON catalog plus the **current** `trace_cases.json`.
|
||||
It does not build, install an APK, or launch the remote server. For the formal
|
||||
P6.5 run-ahead matrix, use `--require-run-ahead`. It forces `MOBILEGL_IPC_RUN_AHEAD=1`
|
||||
and `MOBILEGL_IPC_VERB_BARRIER=1`, then requires `run-ahead ARMED` in each private
|
||||
client log and rejects any `running lockstep` / `DISARMED` marker. The checkpoint
|
||||
records `requested_arm` and `actual_arm`; strict resume cannot reuse an earlier
|
||||
lockstep image pass. Start the intended server first, then run serially:
|
||||
P6.5 run-ahead matrix, use `--require-run-ahead`. It forces `MOBILEGL_IPC_RUN_AHEAD=1`,
|
||||
then requires `run-ahead ARMED` in each private client log and rejects any
|
||||
`running lockstep` / `DISARMED` marker. The checkpoint records `requested_arm` and
|
||||
`actual_arm`; strict resume cannot reuse an earlier lockstep image pass. Start the intended
|
||||
server first, then run serially:
|
||||
|
||||
```bash
|
||||
ctest --test-dir /path/to/retrace-build --show-only=json-v1 > /tmp/trace-catalog.json
|
||||
|
||||
@@ -59,7 +59,7 @@ def measure(args, case, credit, work):
|
||||
env = dict(os.environ, MOBILEGL_TRANSPORT='spawn', MOBILEGL_IPC_CONTROL=args.endpoint,
|
||||
MOBILEGL_IPC_DATA='stream', MOBILEGL_IPC_TOKEN=args.token, MOBILEGL_IPC_REQUIRE_SAME_BUILD='1',
|
||||
MOBILEGL_IPC_LOG_FORWARD='1', MOBILEGL_PIPE_STATS='1', MOBILEGL_PIPE_STATS_PERIOD='1',
|
||||
MOBILEGL_IPC_PRESENT_CREDIT=str(credit), MOBILEGL_IPC_RUN_AHEAD='1', MOBILEGL_IPC_VERB_BARRIER='1',
|
||||
MOBILEGL_IPC_PRESENT_CREDIT=str(credit), MOBILEGL_IPC_RUN_AHEAD='1',
|
||||
MOBILEGL_IPC_BATCH_WAITS='1', MOBILEGL_BACKEND_TYPE='DirectGLES',
|
||||
MOBILEGL_IPC_STRICT_ERRORS='0', MOBILEGL_IPC_ROLE_SPLIT_STATE='0', MOBILEGL_IPC_AUDIT='0')
|
||||
for name in ('MOBILEGL_IPC_DIAL', 'MOBILEGL_IPC_SERVER_PATH'):
|
||||
|
||||
@@ -58,7 +58,7 @@ def run(args, case, transport, repeat):
|
||||
out.mkdir(parents=True)
|
||||
env = dict(os.environ, MOBILEGL_TRANSPORT='spawn', MOBILEGL_BACKEND_TYPE='DirectGLES',
|
||||
MOBILEGL_IPC_SERVER_PATH=str(args.server), MOBILEGL_PIPE_STATS='1', MOBILEGL_PIPE_STATS_PERIOD='1',
|
||||
MOBILEGL_IPC_PRESENT_CREDIT='1', MOBILEGL_IPC_RUN_AHEAD='1', MOBILEGL_IPC_VERB_BARRIER='1',
|
||||
MOBILEGL_IPC_PRESENT_CREDIT='1', MOBILEGL_IPC_RUN_AHEAD='1',
|
||||
MOBILEGL_IPC_BATCH_WAITS='1', MOBILEGL_IPC_SPIN_US='50', MOBILEGL_IPC_STRICT_ERRORS='0',
|
||||
MOBILEGL_IPC_ROLE_SPLIT_STATE='0', MOBILEGL_IPC_AUDIT='0',
|
||||
LIBGL_ALWAYS_SOFTWARE='1', EGL_PLATFORM='surfaceless',
|
||||
|
||||
@@ -36,7 +36,7 @@ def run(args):
|
||||
MOBILEGL_IPC_REQUIRE_SAME_BUILD='1', MOBILEGL_IPC_LOG_FORWARD='1',
|
||||
MOBILEGL_BACKEND_TYPE='DirectGLES', MOBILEGL_PIPE_STATS='1',
|
||||
MOBILEGL_PIPE_STATS_PERIOD='1', MOBILEGL_IPC_RUN_AHEAD='1',
|
||||
MOBILEGL_IPC_VERB_BARRIER='1', MOBILEGL_IPC_PRESENT_CREDIT='1',
|
||||
MOBILEGL_IPC_PRESENT_CREDIT='1',
|
||||
MOBILEGL_LOG_FILE_PATH=str(args.out / 'mobilegl.log'), EGL_PLATFORM='surfaceless')
|
||||
for name in ('MOBILEGL_IPC_DIAL', 'MOBILEGL_IPC_SERVER_PATH'):
|
||||
os.environ.pop(name, None)
|
||||
|
||||
@@ -118,7 +118,7 @@ def run(args, transport, case, repeat):
|
||||
env = dict(os.environ, MOBILEGL_TRANSPORT=transport, MOBILEGL_IPC_SERVER_PATH=str(args.server),
|
||||
MOBILEGL_PIPE_STATS='0', MOBILEGL_IPC_PRESENT_CREDIT='1', MOBILEGL_IPC_RUN_AHEAD='1',
|
||||
MOBILEGL_IPC_STRICT_ERRORS='0', MOBILEGL_IPC_ROLE_SPLIT_STATE='0', MOBILEGL_IPC_AUDIT='0',
|
||||
MOBILEGL_IPC_VERB_BARRIER='1', MOBILEGL_IPC_BATCH_WAITS='1', MOBILEGL_IPC_SPIN_US='50',
|
||||
MOBILEGL_IPC_BATCH_WAITS='1', MOBILEGL_IPC_SPIN_US='50',
|
||||
LIBGL_ALWAYS_SOFTWARE='1', EGL_PLATFORM='surfaceless',
|
||||
MESA_GL_VERSION_OVERRIDE='3.3', MESA_GLSL_VERSION_OVERRIDE='330')
|
||||
for key in ('MOBILEGL_IPC_CONTROL', 'MOBILEGL_IPC_DATA', 'MOBILEGL_IPC_TOKEN', 'MOBILEGL_IPC_DIAL',
|
||||
|
||||
@@ -140,7 +140,6 @@ def case_environment(test, base, args, output=None):
|
||||
MOBILEGL_IPC_PRESENT_CREDIT=str(args.credit))
|
||||
if getattr(args, "require_run_ahead", False):
|
||||
env["MOBILEGL_IPC_RUN_AHEAD"] = "1"
|
||||
env["MOBILEGL_IPC_VERB_BARRIER"] = "1"
|
||||
if output is not None:
|
||||
env["MOBILEGL_LOG_FILE_PATH"] = str(output / "output/mobilegl.log")
|
||||
return env
|
||||
@@ -406,7 +405,7 @@ def run_case(plan, args, row):
|
||||
work = Path(row["work"])
|
||||
console = work.parent / "runner.log"
|
||||
env = case_environment(plan["test"], args.base_env, args, work)
|
||||
row["requested_arm"] = {key: env.get(key) for key in ("MOBILEGL_IPC_RUN_AHEAD", "MOBILEGL_IPC_VERB_BARRIER")}
|
||||
row["requested_arm"] = {key: env.get(key) for key in ("MOBILEGL_IPC_RUN_AHEAD",)}
|
||||
props = properties(plan["test"])
|
||||
cwd = props.get("WORKING_DIRECTORY", str(args.source))
|
||||
def wake():
|
||||
@@ -456,7 +455,7 @@ def main(argv=None):
|
||||
parser.add_argument("--credit", type=int, choices=(1, 2, 3), default=2)
|
||||
parser.add_argument("--resume", action="store_true")
|
||||
parser.add_argument("--require-run-ahead", action="store_true",
|
||||
help="require private-client ARMED proof, no lockstep fallback/DISARMED; force RUN_AHEAD=1 and VERB_BARRIER=1")
|
||||
help="require private-client ARMED proof, no lockstep fallback/DISARMED; force RUN_AHEAD=1")
|
||||
parser.add_argument("--idle-seconds", type=float, default=300)
|
||||
parser.add_argument("--max-seconds", type=float, default=7200)
|
||||
parser.add_argument("--wake-adb-serial", help="explicit opt-in: KEYCODE_WAKEUP every 15 seconds while a case runs")
|
||||
|
||||
@@ -37,7 +37,7 @@ if behavior!='no-result':
|
||||
actual=work/'output/actual.png';actual.write_bytes(b'fixture-image')
|
||||
endpoint=os.environ['MOBILEGL_IPC_CONTROL'].removeprefix('tcp://')
|
||||
arm=os.environ.get('CASE_ARM','armed')
|
||||
if arm=='respect-env':arm='armed' if os.environ.get('MOBILEGL_IPC_RUN_AHEAD')=='1' and os.environ.get('MOBILEGL_IPC_VERB_BARRIER')=='1' else 'lockstep'
|
||||
if arm=='respect-env':arm='armed' if os.environ.get('MOBILEGL_IPC_RUN_AHEAD')=='1' else 'lockstep'
|
||||
line={'armed':'run-ahead ARMED\n','lockstep':'running lockstep\n','disarmed':'run-ahead ARMED\nrun-ahead DISARMED\n','server-only':''}[arm]
|
||||
(work/'output/mobilegl.client.log').write_text('control=tcp data=stream server='+endpoint+' pid=123 dial=connect\n'+line)
|
||||
if arm=='server-only':(work/'output/mobilegl.server.log').write_text('run-ahead ARMED\n')
|
||||
@@ -116,11 +116,11 @@ class MatrixTests(unittest.TestCase):
|
||||
test['properties'][0]['value'] += list(entries)
|
||||
self.catalog.write_text(json.dumps(catalog))
|
||||
|
||||
def test_required_arm_forces_both_environment_knobs_and_records_actual_arm(self):
|
||||
self.set_arm('respect-env',['MOBILEGL_IPC_RUN_AHEAD=0','MOBILEGL_IPC_VERB_BARRIER=0'])
|
||||
def test_required_arm_forces_the_run_ahead_knob_and_records_actual_arm(self):
|
||||
self.set_arm('respect-env',['MOBILEGL_IPC_RUN_AHEAD=0'])
|
||||
self.assertEqual(self.run_main('--require-run-ahead'),0)
|
||||
row=self.checkpoint()[0]
|
||||
self.assertEqual(row['requested_arm'],{'MOBILEGL_IPC_RUN_AHEAD':'1','MOBILEGL_IPC_VERB_BARRIER':'1'})
|
||||
self.assertEqual(row['requested_arm'],{'MOBILEGL_IPC_RUN_AHEAD':'1'})
|
||||
self.assertEqual(row['actual_arm'],{'armed':True,'lockstep':False,'disarmed':False})
|
||||
self.assertEqual(self.run_main('--require-run-ahead','--resume'),0)
|
||||
self.assertEqual(len(self.checkpoint()),1)
|
||||
@@ -337,7 +337,7 @@ class MatrixTests(unittest.TestCase):
|
||||
|
||||
def test_directvulkan_run_ahead_proof_and_checkpoint_keys_are_per_backend(self):
|
||||
self.write_inputs(backends=('DirectGLES','DirectVulkan'))
|
||||
self.set_arm('respect-env',['MOBILEGL_IPC_RUN_AHEAD=0','MOBILEGL_IPC_VERB_BARRIER=0'])
|
||||
self.set_arm('respect-env',['MOBILEGL_IPC_RUN_AHEAD=0'])
|
||||
self.assertEqual(self.run_main('--backend','DirectGLES','--backend','DirectVulkan',
|
||||
'--require-run-ahead'),0)
|
||||
rows=self.checkpoint()
|
||||
|
||||
@@ -259,7 +259,7 @@ class RunTraceCaseTests(unittest.TestCase):
|
||||
'a leg whose transport block was skipped must fail the checker')
|
||||
|
||||
# MGTRACE_EXPECT_IPC: the client's IPC line must carry every token, whole.
|
||||
IPC = 'Config: IPC ring=8MiB verb-barrier=1 run-ahead={ra} present-credit=1 strict={st} audit=0 role-split-state={rs} x'
|
||||
IPC = 'Config: IPC ring=8MiB run-ahead={ra} present-credit=1 strict={st} audit=0 role-split-state={rs} x'
|
||||
|
||||
def ipc_logs(self, ra='1', st='0', rs='1', line=True):
|
||||
logs = good_logs('spawn')
|
||||
|
||||
Reference in New Issue
Block a user