From 081854653a2355c1c7973e79631ec1b980287d43 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Tue, 6 Oct 2026 05:29:21 -0400 Subject: [PATCH] [Feat] (P13 W4): MG_Config::DataArmIsRecord and the W4-W6 MOBILEGL_PIPE_DATA_ARM knob --- MobileGL/Config.h | 23 +++++++++++++++ MobileGL/ConfigLoader.cpp | 29 +++++++++++++++++++ .../Harness/SplitRuntimePeek.cpp | 1 + .../Harness/SplitRuntimePeek.h | 3 ++ 4 files changed, 56 insertions(+) diff --git a/MobileGL/Config.h b/MobileGL/Config.h index 8fee0a33..a3cd05ad 100644 --- a/MobileGL/Config.h +++ b/MobileGL/Config.h @@ -735,10 +735,33 @@ namespace MobileGL::MG_Config { inline Bool ServerOwnedWindowSurfaces() { return Transport == TransportMode::Spawn && Ipc.Surface == IpcSurface::Server; } + + // P13 W4: THE DATA-ARM QUESTION - do the backends read RECORDS (the applier's twins, the staged + // stores, the verb's own handles) rather than frontend objects? Under a wire the answer has + // always been yes; P13 makes it yes for monolith too ("monolith is the push variant"), one + // record family at a time. Each family's arm sites ask THIS rather than `Transport`, which + // keeps answering the questions that really are about the transport (lifecycle, roles, whose + // caps, whether a store copies). + // + // MOBILEGL_PIPE_DATA_ARM=record (default) | frontend picks monolith's arm for the families + // already switched. It exists for W4-W6 only - bisecting a monolith red to the switch, and + // the per-thread CPU record (ID-P13-2) - and goes in W6 with the frontend arms. Latched once + // by MG_ConfigLoader::Init(): the two arms keep their twins in different places. + extern Bool MonolithTakesRecordArm; + inline Bool DataArmIsRecord() { return Transport != TransportMode::Monolith || MonolithTakesRecordArm; } + // ...and the record arm runs in this process over stores that ALIAS the frontend's shadows + // (monolith) instead of copying them (every wire). + inline Bool RecordArmAliasesFrontend() { + return Transport == TransportMode::Monolith && MonolithTakesRecordArm; + } #else // The whole point: in a build without MG_Remote this folds at compile time, so // `if (MG_Config::Transport != MG_Config::TransportMode::Monolith)` in Init.cpp is a // discarded statement and the pull build gains no symbol, no branch and no byte. inline constexpr TransportMode Transport = TransportMode::Monolith; + // P13 W4: a build without MG_Remote stays on the frontend arm until W5 moves the record arm + // out of the disaggregated gating (ID-P13-1 / A2). + inline constexpr Bool DataArmIsRecord() { return false; } + inline constexpr Bool RecordArmAliasesFrontend() { return false; } #endif } // namespace MobileGL::MG_Config diff --git a/MobileGL/ConfigLoader.cpp b/MobileGL/ConfigLoader.cpp index 50c15271..6539acbf 100644 --- a/MobileGL/ConfigLoader.cpp +++ b/MobileGL/ConfigLoader.cpp @@ -38,6 +38,7 @@ namespace MobileGL::MG_Config { String TransportEndpoint; Bool SplitTransportRequestedByConfig = false; IpcTable Ipc; + Bool MonolithTakesRecordArm = true; #endif } // namespace MobileGL::MG_Config @@ -455,6 +456,33 @@ namespace MobileGL::MG_ConfigLoader { // rather than silent, because the failure this avoids is a P6 lane that set // MOBILEGL_TRANSPORT=spawn, fell back to monolith, and went green on the wrong arm. // The mode is left at Monolith so nothing half-initializes. + // P13 W4 (Config.h DataArmIsRecord). Read after InitTransport: `frontend` names monolith's + // arm only - a wire has no frontend objects on its server half to read, so there it is + // ignored by name rather than half-honoured. + inline void InitDataArm() { + String value; + QueryEnvVariable("MOBILEGL_PIPE_DATA_ARM", value, "record"); + String lowered = value; + std::transform(lowered.begin(), lowered.end(), lowered.begin(), + [](unsigned char c) { return static_cast(std::tolower(c)); }); + MG_Config::MonolithTakesRecordArm = true; + if (lowered.empty() || lowered == "record") return; + if (lowered == "frontend") { + if (MG_Config::Transport != MG_Config::TransportMode::Monolith) { + MGLOG_W("Config: MOBILEGL_PIPE_DATA_ARM=frontend is ignored under a transport - the " + "record arm is the only arm a wire has"); + return; + } + MG_Config::MonolithTakesRecordArm = false; + MGLOG_I("Config: MOBILEGL_PIPE_DATA_ARM=frontend - monolith reads frontend objects in " + "the record families switched so far"); + return; + } + MGLOG_W("Config: Ignoring invalid env variable MOBILEGL_PIPE_DATA_ARM='%s'; expected " + "record|frontend, using record", + value.c_str()); + } + inline void InitTransport() { String value; QueryEnvVariable("MOBILEGL_TRANSPORT", value, "monolith"); @@ -702,6 +730,7 @@ namespace MobileGL::MG_ConfigLoader { // After InitFeatures, so the one line InitIpc logs is the last word on this run's // configuration, and before the accepted-env map is destroyed just below. InitTransport(); + InitDataArm(); InitIpc(); #endif diff --git a/MobileGL/MG_IntegrationTest/Harness/SplitRuntimePeek.cpp b/MobileGL/MG_IntegrationTest/Harness/SplitRuntimePeek.cpp index ca3b6248..67d659e9 100644 --- a/MobileGL/MG_IntegrationTest/Harness/SplitRuntimePeek.cpp +++ b/MobileGL/MG_IntegrationTest/Harness/SplitRuntimePeek.cpp @@ -128,6 +128,7 @@ namespace MGITest { default: state.transportName = "non-monolith"; break; } state.transportResolved = MobileGL::MG_Config::Transport != TransportMode::Monolith; + state.dataArmIsRecord = MobileGL::MG_Config::DataArmIsRecord(); // Active() is c0's one deliberately non-aborting accessor: "does a session exist" has a // legitimate no. Everything below it is only reached through a live session, so nothing diff --git a/MobileGL/MG_IntegrationTest/Harness/SplitRuntimePeek.h b/MobileGL/MG_IntegrationTest/Harness/SplitRuntimePeek.h index 7550b922..6e874ef1 100644 --- a/MobileGL/MG_IntegrationTest/Harness/SplitRuntimePeek.h +++ b/MobileGL/MG_IntegrationTest/Harness/SplitRuntimePeek.h @@ -63,6 +63,9 @@ namespace MGITest { bool transportResolved = false; // The resolved transport, for a message: "monolith", "inproc", "spawn", "unix", "pipe". std::string transportName = "monolith"; + // P13 W4: MG_Config::DataArmIsRecord() - false in a build without the peek (and without + // MG_Remote), which stays on the frontend arm until W5. + bool dataArmIsRecord = false; // ClientSession::Active() != nullptr. bool sessionActive = false; // ImplementedVerbCount(), out of kRemoteEmitSlotCount (71).