[Feat] (MG_Remote, P11 B2): T0 is the default - an unset MOBILEGL_IPC_ADOPT_TIER asks T0 (ask 0x80) and falls back to T2 quietly (MGLOG_D, still counted: client T0Fallbacks, server T0Refusals), while an explicit 0 keeps every named line; revision 5's schema comment and pin fixed up

This commit is contained in:
2026-09-29 20:15:29 -04:00
parent e44d6ac7da
commit b32140e614
11 changed files with 173 additions and 65 deletions
+10 -7
View File
@@ -500,6 +500,9 @@ namespace MobileGL::MG_Config {
Offscreen = 0, // a window surface is the client's own window, as before (the default)
Server = 1, // a window surface is the SERVER's window: WindowKind::ServerOwned
};
// IpcTable::AdoptTier when MOBILEGL_IPC_ADOPT_TIER is unset (or unparseable): T0 by default,
// with QUIET fallbacks. Not a tier a user can type - the knob admits 0..2.
inline constexpr Uint32 kAdoptTierUnset = 3;
struct IpcTable {
// MOBILEGL_IPC_SERVER_PATH: where to find libMobileGLServer. P6 consumes it; P5
// lands the parse because t1's ctest ENVIRONMENT blocks and add_trace_replay_test's
@@ -626,13 +629,13 @@ namespace MobileGL::MG_Config {
// arm and the red-once of the no-reply gate (FencePollScenario).
Uint32 PollEscalate = 64;
// MOBILEGL_IPC_ADOPT_TIER, THE ZERO-COPY SWITCH (ruling ID-P11-14, CONTRACT-P11 B2):
// 2 = emulate (T2: the client keeps the shadow and pushes), the default and today's
// behaviour byte for byte; 0 = T0 (the client allocates each adopted store as an
// AHardwareBuffer and the server imports it); 1 = T1, closed - refused by name, then T2.
// A T0 this session cannot use (a stream plane, the server's allow switch, no
// AHardwareBuffer, a failed server POST) is T2 plus one named line, never a Fatal
// (MG_Remote/Transport/AdoptTier.h).
Uint32 AdoptTier = 2;
// UNSET (kAdoptTierUnset, the default since 2026-09-29, the user's decision) = T0 where the
// session can run it, T2 QUIETLY where it cannot (MGLOG_D only, still counted);
// 0 = T0 asked by name - a T0 this session cannot use (a stream plane, the server's allow
// switch, no AHardwareBuffer, a failed server POST) is T2 plus one named line;
// 1 = T1, closed - refused by name, then T2; 2 = emulate (T2: the client keeps the shadow
// and pushes). Never a Fatal (MG_Remote/Transport/AdoptTier.h).
Uint32 AdoptTier = kAdoptTierUnset;
// MOBILEGL_IPC_ALLOW_ADOPT_T0 (server): 1 (default) = grant a client's T0 ask when this
// 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.
+7 -5
View File
@@ -433,9 +433,10 @@ namespace MobileGL::MG_ConfigLoader {
// torn read rather than a divergence. The batch is therefore off whenever the
// shadow comparer is armed.
if (MG_Config::Features.PipeVerify) ipc.BatchWaits = 0;
// 2 (T2) is the default; 0 asks for T0 and 1 is refused by name - both settled at the
// handshake from the data plane (P11 A1/B2, MG_Remote/Transport/AdoptTier.h).
ipc.AdoptTier = QueryEnvUint32("MOBILEGL_IPC_ADOPT_TIER", 2, 0, 2);
// UNSET is T0 with quiet fallbacks (the default since 2026-09-29); 0 asks for T0 by name, 1 is
// refused by name, 2 is T2 - settled at the handshake from the data plane (P11 A1/B2,
// MG_Remote/Transport/AdoptTier.h). An unparseable value is warned about and reads as unset.
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);
@@ -532,10 +533,11 @@ 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=%u allow-t0=%u verb-barrier=%u run-ahead=%u present-credit=%u control-timeout=%ums "
"adopt-tier=%s allow-t0=%u verb-barrier=%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, ipc.WireDeferredMb, ipc.SpinUs, ipc.EventWaitMs,
ipc.PersistentBlockKb, ipc.CreateWindow, ipc.PboReadbackSync, ipc.PollEscalate, ipc.AdoptTier,
ipc.PersistentBlockKb, ipc.CreateWindow, ipc.PboReadbackSync, ipc.PollEscalate,
ipc.AdoptTier == 0 ? "0" : 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),
+36 -10
View File
@@ -823,7 +823,10 @@ namespace MobileGL::MG_Remote::Client {
return result;
}
ResetT0(false);
m_adoptT0Asked = adoptAsk == 0;
m_adoptT0Asked = Transport::AdoptAskIsT0(adoptAsk);
m_adoptT0Quiet = Transport::AdoptTierIsDefault();
if (terms->dataPlane() == ::MobileGL::Wire::DataPlane::Stream && Transport::AdoptTierWantsT0())
m_t0Fallbacks = 1; // a stream carries T2 only (the settle said so, by name or at D)
// The four SegmentRefs are what a spawn client MAPS (P6). Under inproc the mapping
// already exists, so what they are good for here is the cross-check that the two
// sides agree about the geometry at all - which is the assertion that would
@@ -1127,7 +1130,10 @@ namespace MobileGL::MG_Remote::Client {
return result;
}
ResetT0(false);
m_adoptT0Asked = adoptAsk == 0;
m_adoptT0Asked = Transport::AdoptAskIsT0(adoptAsk);
m_adoptT0Quiet = Transport::AdoptTierIsDefault();
if (stream && Transport::AdoptTierWantsT0())
m_t0Fallbacks = 1; // a stream carries T2 only (the settle said so, by name or at D)
if (stream) {
// PH-7 (4). A Stream Welcome without a nonce of exactly the minted width names no
// data connection this client could open; refused rather than guessed at.
@@ -2728,6 +2734,13 @@ namespace MobileGL::MG_Remote::Client {
"AHardwareBuffers the server imports");
} else {
m_adoptT0Decision = 0;
++m_t0Fallbacks;
if (m_adoptT0Quiet) {
// T0 BY DEFAULT (knob unset) FALLS BACK QUIETLY - counted, said at MGLOG_D.
MGLOG_D("MG_Remote client: T0 by default is not available (no kCapAdoptT0; the server's log "
"says why at MGLOG_D); the session runs T2");
return false;
}
MGLOG_W("MG_Remote client: Refuse{AdoptT0Unavailable, \"no kCapAdoptT0\"} - "
"MOBILEGL_IPC_ADOPT_TIER=0 asked for T0 and the server did not publish kCapAdoptT0 (its "
"log names why: its allow switch, no AHardwareBuffer, or its POST self-test); the "
@@ -2853,21 +2866,34 @@ namespace MobileGL::MG_Remote::Client {
}
void ClientSession::ResetT0(Bool releaseStores) {
if (m_adoptT0Asked) {
MGLOG_I("MG_Remote client: T0 session totals - %s; stores imported %llu, bytes %llu, declined %llu, "
"released %llu, held at teardown %zu",
m_adoptT0Decision == 1 ? "ran T0" : m_adoptT0Decision == 0 ? "fell back to T2" : "never decided",
static_cast<unsigned long long>(m_t0Imported), static_cast<unsigned long long>(m_t0Bytes),
static_cast<unsigned long long>(m_t0Declined), static_cast<unsigned long long>(m_t0Released),
m_t0Stores.size());
if (m_adoptT0Asked || m_t0Fallbacks != 0) {
// A default (unset-knob) session that did not run T0 says its totals at MGLOG_D.
const bool quiet = m_adoptT0Quiet && m_adoptT0Decision != 1;
const char* ran =
m_adoptT0Decision == 1 ? "ran T0" : m_adoptT0Decision == 0 || m_t0Fallbacks ? "fell back to T2" : "never decided";
#define MGL_T0_CLIENT_TOTALS(LOG) \
LOG("MG_Remote client: T0 session totals - %s%s; stores imported %llu, bytes %llu, declined %llu, " \
"fallbacks %llu, released %llu, held at teardown %zu", \
ran, m_adoptT0Quiet ? " (default)" : "", static_cast<unsigned long long>(m_t0Imported), \
static_cast<unsigned long long>(m_t0Bytes), static_cast<unsigned long long>(m_t0Declined), \
static_cast<unsigned long long>(m_t0Fallbacks), static_cast<unsigned long long>(m_t0Released), \
m_t0Stores.size())
if (quiet) {
MGL_T0_CLIENT_TOTALS(MGLOG_D);
} else {
MGL_T0_CLIENT_TOTALS(MGLOG_I);
}
#undef MGL_T0_CLIENT_TOTALS
(void)ran;
}
if (releaseStores) {
for (auto& [key, store] : m_t0Stores) Transport::AdoptT0::ReleaseHeld(store);
}
m_t0Stores.clear();
m_adoptT0Asked = false;
m_adoptT0Quiet = false;
m_adoptT0Decision = -1;
m_t0Imported = m_t0Bytes = m_t0Declined = m_t0Released = 0;
m_t0Imported = m_t0Bytes = m_t0Declined = m_t0Released = m_t0Fallbacks = 0;
m_prePublish = nullptr;
}
@@ -252,6 +252,10 @@ namespace MobileGL::MG_Remote::Client {
Uint64 T0StoresImported() const { return m_t0Imported; }
Uint64 T0BytesImported() const { return m_t0Bytes; }
Uint64 T0Declined() const { return m_t0Declined; }
// 1 when this session's knob asked for T0 (0, or unset) and it runs T2 instead: a stream
// data plane, or no kCapAdoptT0 at the first map_persistent. Counted whether the fallback
// was named (knob 0) or quiet (unset, the default).
Uint64 T0Fallbacks() const { return m_t0Fallbacks; }
// Test seam: 0 = queue each Offer before its record (the product order); > 0 = queue it
// that many ms AFTER the record is published, from another thread; < 0 = never queue it.
// What the host tests use to prove a late Offer still lands and a lost one is a named
@@ -595,12 +599,14 @@ namespace MobileGL::MG_Remote::Client {
// the counters; and the one-shot hook EmitAndWaitTails runs between EncodeRecord (the
// seq exists) and PublishAndNotify (the record is visible) - where the Offer is queued.
Bool m_adoptT0Asked = false;
Bool m_adoptT0Quiet = false; // the knob is unset: fallbacks at MGLOG_D
Int32 m_adoptT0Decision = -1;
std::unordered_map<Uint64, Transport::AdoptT0::HeldStore> m_t0Stores;
Uint64 m_t0Imported = 0;
Uint64 m_t0Bytes = 0;
Uint64 m_t0Declined = 0;
Uint64 m_t0Released = 0;
Uint64 m_t0Fallbacks = 0;
std::function<void(Uint64)> m_prePublish;
void ResetT0(Bool releaseStores);
};
@@ -1247,6 +1247,10 @@ namespace MobileGL::MG_Remote::Client {
Bool AdoptTierIsEmulate() {
const Uint32 tier = MG_Config::Ipc.AdoptTier;
if (tier == 2) return true;
// P11 B2: UNSET (T0 by default) reaching the T2 path - settled or not - is T2, quietly: a
// session-free caller (a unit case, a harness) runs the P5 default it always ran, and a
// settled session already counted its fallback at the handshake or its first map_persistent.
if (tier == MG_Config::kAdoptTierUnset) return true;
// P11 A1 (CONTRACT-P11 §1): THE VERDICT IS THE HANDSHAKE'S, NOT THIS CALL'S. Both roles
// settle the tier from the knob and the data plane before the first record
// (Transport/AdoptTier.cpp): a stream refuses T0/T1 by name and runs T2, a client over
+3 -2
View File
@@ -71,8 +71,9 @@ table LinkTerms {
eventWindowBytes: ulong;
// P11 B2 (MG_Remote/CONTRACT-P11.md B2), appended. In the Hello: the persistent-map adoption
// tier the client ASKS for - 0 = T0 (it allocates each adopted store as an AHardwareBuffer and
// hands it over the aux socket), 2 = T2 (emulate: shadow + push). 1 (T1) is closed and never
// sent; a stream plane always asks 2. In the Welcome: the server's echo of the ask it
// hands it over the aux socket), 0x80 = T0 by DEFAULT (the client's knob is unset: the same T0,
// but the server names a refusal only at debug level), 2 = T2 (emulate: shadow + push). 1 (T1)
// is closed and never sent; a stream plane always asks 2. In the Welcome: the server's echo of the ask it
// recorded. It is not the tier: T0 needs the server's POST self-test, which runs at the first
// native bind (the GPU context does not exist yet), and is published as kCapAdoptT0 in the
// CapsSnapshot that bind republishes. Absent (an older peer) reads 2.
+50 -21
View File
@@ -686,16 +686,20 @@ namespace MobileGL::MG_Remote::Server {
// for, before a byte of Welcome. A stream refuses T0/T1 by name and serves T2; over
// shared segments the client settles it (Transport/AdoptTier.h). 0/1 never die here.
(void)Transport::SettleAdoptTierAtHandshake(stream, Transport::AdoptTierSide::Server);
// ---- P11 B2: the client's ask (LinkTerms.adoptTier). 2 or, over shared segments, 0;
// anything else is not an ask this revision knows. Recorded, echoed in the Welcome, and
// granted or refused by name at the first native bind (SettleAdoptT0AtBind).
if (terms->adoptTier() != 2 && !(terms->adoptTier() == 0 && !stream))
// ---- P11 B2: the client's ask (LinkTerms.adoptTier). 2 or, over shared segments, 0 (T0
// by name) or kAdoptAskT0Default (T0 by default: refusals quiet); anything else is not an
// ask this revision knows. Recorded, echoed in the Welcome, and granted or refused at the
// first native bind (SettleAdoptT0AtBind).
if (terms->adoptTier() != 2 && !(Transport::AdoptAskIsT0(terms->adoptTier()) && !stream))
return RefuseHandshake(transport, ::MobileGL::Wire::RefuseCode::LinkTerms,
"adoptTier is neither 2 nor, over shared segments, 0", 2, terms->adoptTier());
m_adoptT0Asked = terms->adoptTier() == 0;
"adoptTier is neither 2 nor, over shared segments, a T0 ask (0 or 0x80)", 2,
terms->adoptTier());
m_adoptAsk = terms->adoptTier();
m_adoptT0Asked = Transport::AdoptAskIsT0(m_adoptAsk);
m_adoptT0Quiet = m_adoptAsk == Transport::kAdoptAskT0Default;
m_adoptT0Settled = false;
m_adoptT0 = false;
m_t0Stores = m_t0Bytes = m_t0Declined = 0;
m_t0Stores = m_t0Bytes = m_t0Declined = m_t0Refusals = 0;
m_adoptInbox.Clear();
m_adoptInbox.Attach(&transport);
@@ -794,7 +798,7 @@ namespace MobileGL::MG_Remote::Server {
}
auto negotiated = ::MobileGL::Wire::CreateLinkTerms(builder, terms->dataPlane(),
::MobileGL::Wire::WireForm::StructImage, maxReply, cmdWindow, stageWindow, eventWindow,
static_cast<Uint8>(m_adoptT0Asked ? 0 : 2));
static_cast<Uint8>(m_adoptT0Asked ? m_adoptAsk : 2));
auto welcome = ::MobileGL::Wire::CreateWelcome(
builder, MOBILEGL_PROTOCOL_ABI_MAJOR, MOBILEGL_PROTOCOL_ABI_MINOR,
// CONTRACT-P6 4.3: THE SERVER'S OWN PID, not an echo of the client's.
@@ -1030,17 +1034,29 @@ namespace MobileGL::MG_Remote::Server {
// P11 B2: the T0 arm's proof for this session, then the inbox's leftovers (an Offer whose
// record never came; its client has released the store on the session's end).
if (m_accepted && m_adoptT0Asked) {
MGLOG_I("MG_Remote server: T0 session totals - %s; stores imported %llu, bytes %llu, declined %llu, "
"stale offers %llu, malformed %llu, unclaimed %zu",
m_adoptT0 ? "ran T0" : m_adoptT0Settled ? "not granted (T2)" : "no native bind (T2)",
static_cast<unsigned long long>(m_t0Stores), static_cast<unsigned long long>(m_t0Bytes),
static_cast<unsigned long long>(m_t0Declined),
static_cast<unsigned long long>(m_adoptInbox.StaleOffers()),
static_cast<unsigned long long>(m_adoptInbox.MalformedOffers()), m_adoptInbox.Kept());
// A default ask that ended on T2 says its totals at MGLOG_D, like its refusal.
const bool quiet = m_adoptT0Quiet && !m_adoptT0;
const char* ran = m_adoptT0 ? "ran T0" : m_adoptT0Settled ? "not granted (T2)" : "no native bind (T2)";
#define MGL_T0_SERVER_TOTALS(LOG) \
LOG("MG_Remote server: T0 session totals - %s%s; stores imported %llu, bytes %llu, declined %llu, " \
"refused %llu, stale offers %llu, malformed %llu, unclaimed %zu", \
ran, m_adoptT0Quiet ? " (default ask)" : "", static_cast<unsigned long long>(m_t0Stores), \
static_cast<unsigned long long>(m_t0Bytes), static_cast<unsigned long long>(m_t0Declined), \
static_cast<unsigned long long>(m_t0Refusals), \
static_cast<unsigned long long>(m_adoptInbox.StaleOffers()), \
static_cast<unsigned long long>(m_adoptInbox.MalformedOffers()), m_adoptInbox.Kept())
if (quiet) {
MGL_T0_SERVER_TOTALS(MGLOG_D);
} else {
MGL_T0_SERVER_TOTALS(MGLOG_I);
}
#undef MGL_T0_SERVER_TOTALS
(void)ran;
}
m_adoptInbox.Clear();
m_adoptInbox.Attach(nullptr);
m_adoptT0Asked = m_adoptT0Settled = m_adoptT0 = false;
m_adoptT0Asked = m_adoptT0Settled = m_adoptT0 = m_adoptT0Quiet = false;
m_adoptAsk = 2;
m_consumer.Detach();
*m_commands = Transport::RingConsumer();
*m_events = Transport::EventRingProducer();
@@ -1094,19 +1110,32 @@ namespace MobileGL::MG_Remote::Server {
post.passed = ops->SelfTestExternal(detail, sizeof(detail));
post.detail = detail;
post.ran = true;
MGLOG_I("MG_Remote server: T0 POST (%s, sustained-lock pattern) %s - %s", backendName,
post.passed ? "PASSED" : "FAILED", post.detail.c_str());
if (m_adoptT0Quiet) {
MGLOG_D("MG_Remote server: T0 POST (%s, sustained-lock pattern) %s - %s", backendName,
post.passed ? "PASSED" : "FAILED", post.detail.c_str());
} else {
MGLOG_I("MG_Remote server: T0 POST (%s, sustained-lock pattern) %s - %s", backendName,
post.passed ? "PASSED" : "FAILED", post.detail.c_str());
}
}
if (!post.passed) why = std::string("the ") + backendName + " POST self-test failed: " + post.detail;
}
}
if (!why.empty()) {
++m_t0Refusals;
const char* word = MG_Config::Ipc.AllowAdoptT0 == 0 ? "disallowed"
: !Transport::AdoptT0::PlatformHasAhb() ? "no AHardwareBuffer" : "POST";
if (m_adoptT0Quiet) {
// THE DEFAULT ASK (MOBILEGL_IPC_ADOPT_TIER unset) FALLS BACK QUIETLY: every host
// session would otherwise carry this line. Counted (T0Refusals, the totals line).
MGLOG_D("MG_Remote server: T0 by default not granted (%s) - %s; the session runs T2",
word, why.c_str());
return;
}
MGLOG_W("MG_Remote server: Refuse{AdoptT0Unavailable, \"%s\"} - the client asked T0 "
"(MOBILEGL_IPC_ADOPT_TIER=0) and %s; kCapAdoptT0 is not published and the session "
"runs T2 (emulate)",
MG_Config::Ipc.AllowAdoptT0 == 0 ? "disallowed"
: !Transport::AdoptT0::PlatformHasAhb() ? "no AHardwareBuffer" : "POST",
why.c_str());
word, why.c_str());
return;
}
m_adoptT0 = true;
@@ -315,6 +315,11 @@ namespace MobileGL::MG_Remote::Server {
Uint64 T0StoresImported() const { return m_t0Stores; }
Uint64 T0BytesImported() const { return m_t0Bytes; }
Uint64 T0Declined() const { return m_t0Declined; }
// 1 when this session asked T0 and was not granted it (allow switch, no AHardwareBuffer,
// POST): counted whether the refusal was named (knob 0) or quiet (the default ask).
Uint64 T0Refusals() const { return m_t0Refusals; }
// The ask was the client's DEFAULT (kAdoptAskT0Default): a refusal is said at MGLOG_D.
Bool AdoptT0Quiet() const { return m_adoptT0Quiet; }
AdoptInbox& T0Inbox() { return m_adoptInbox; }
private:
@@ -355,12 +360,15 @@ namespace MobileGL::MG_Remote::Server {
// P11 B2 (T0). Set by Accept from the Hello, by SettleAdoptT0AtBind on the apply thread;
// the inbox and the counters are the apply thread's.
Bool m_adoptT0Asked = false;
Bool m_adoptT0Quiet = false;
Uint8 m_adoptAsk = 2;
Bool m_adoptT0Settled = false;
Bool m_adoptT0 = false;
AdoptInbox m_adoptInbox;
Uint64 m_t0Stores = 0;
Uint64 m_t0Bytes = 0;
Uint64 m_t0Declined = 0;
Uint64 m_t0Refusals = 0;
};
// Leak-at-exit like every other MG_Remote singleton (ID-8): no frontend destructor may
+25 -11
View File
@@ -22,15 +22,20 @@ namespace MobileGL::MG_Remote::Transport {
const char* SideName(AdoptTierSide side) { return side == AdoptTierSide::Client ? "client" : "server"; }
} // namespace
bool AdoptTierIsDefault() { return MG_Config::Ipc.AdoptTier == MG_Config::kAdoptTierUnset; }
bool AdoptTierWantsT0() { return MG_Config::Ipc.AdoptTier == 0 || AdoptTierIsDefault(); }
std::uint32_t AdoptTierAskFor(bool streamDataPlane) {
return (MG_Config::Ipc.AdoptTier == 0 && !streamDataPlane) ? 0u : 2u;
if (streamDataPlane || !AdoptTierWantsT0()) return 2u;
return AdoptTierIsDefault() ? kAdoptAskT0Default : 0u;
}
std::uint32_t SettleAdoptTierAtHandshake(bool streamDataPlane, AdoptTierSide side) {
const std::uint32_t knob = MG_Config::Ipc.AdoptTier;
if (knob > 2) {
// Unreachable through ConfigLoader (it admits 0..2); a direct assignment is the only
// way here, and "T<n>" of a 7 would be a lie.
if (knob > 2 && knob != MG_Config::kAdoptTierUnset) {
// Unreachable through ConfigLoader (it admits 0..2 or leaves the knob unset); a direct
// assignment is the only way here, and "T<n>" of a 7 would be a lie.
SessionFail(MGFatalFamily::UnimplementedAdoptTier,
"MGPipe: Fatal{UnimplementedAdoptTier, \"%u\"} - MOBILEGL_IPC_ADOPT_TIER=%u is not an "
"adoption tier; the values are 0 (T0), 1 (closed) and 2 (emulate, the default)",
@@ -39,9 +44,17 @@ namespace MobileGL::MG_Remote::Transport {
g_settled.store(true, std::memory_order_release);
if (knob == 2) return 2;
if (streamDataPlane) {
// A1, UNCHANGED: ONE LINE PER SESSION, on the side that holds the knob. The tcp lane's
// server is a shared fixture that never sees the client's environment, so each side
// says it for itself; neither side dies, because T2 is the only tier a stream carries.
// A1, UNCHANGED FOR A NAMED KNOB: ONE LINE PER SESSION, on the side that holds the knob.
// The tcp lane's server is a shared fixture that never sees the client's environment,
// so each side says it for itself; neither side dies, because T2 is the only tier a
// stream carries. AN UNSET KNOB (T0 by default) SAYS IT AT MGLOG_D: every tcp session
// would otherwise carry the line. The client counts it (ClientSession::T0Fallbacks).
if (AdoptTierIsDefault()) {
MGLOG_D("MG_Remote %s: T0 by default is not available on a stream data plane (tcp): the "
"session runs T2 (MOBILEGL_IPC_ADOPT_TIER unset; set 0 to have this named)",
SideName(side));
return 2;
}
MGLOG_W("MG_Remote %s: Refuse{AdoptTierOnStream, \"T%u\"} - MOBILEGL_IPC_ADOPT_TIER=%u names a "
"shared-mapping adoption tier and this session's data plane is a stream (tcp): the two "
"sides share no memory to map, so the session runs T2 (emulate: the client keeps the "
@@ -61,10 +74,11 @@ namespace MobileGL::MG_Remote::Transport {
"could run it); the session runs T2 (emulate). Set 0 for T0 or 2 for T2");
return 2;
}
// knob 0 over shared segments: asked in the Hello. Nothing is said yet - the answer is the
// server's, at its first native bind, and a T0 this session cannot use is named once, at
// the first map_persistent (ClientSession::AdoptT0).
return 0;
// knob 0 (or unset) over shared segments: asked in the Hello. Nothing is said yet - the
// answer is the server's, at its first native bind, and a T0 this session cannot use is
// named once (knob 0) or said at MGLOG_D (unset), at the first map_persistent
// (ClientSession::AdoptT0).
return AdoptTierAskFor(false);
}
bool AdoptTierSettledAtHandshake() { return g_settled.load(std::memory_order_acquire); }
+23 -8
View File
@@ -13,9 +13,11 @@
// and the server imports it; T1 = the server exports an opaque fd (CLOSED, ID-P11-1); T2 = the
// resource owner declines and the client keeps the shadow and pushes.
//
// MOBILEGL_IPC_ADOPT_TIER IS THE SWITCH (ruling ID-P11-14): 2, the default, is today's T2 byte
// for byte; 0 asks for T0; 1 is refused by name and runs T2. T0 that this session cannot use is
// never a Fatal - it is T2 plus one named line:
// MOBILEGL_IPC_ADOPT_TIER IS THE SWITCH (ruling ID-P11-14). UNSET - the default since 2026-09-29,
// the user's decision - asks for T0 and falls back to T2 QUIETLY (MGLOG_D only, still counted),
// so a tcp session or a host session is not one line longer; 0 asks for T0 BY NAME (the table
// below); 1 is refused by name and runs T2; 2 is T2 byte for byte. T0 that this session cannot use
// is never a Fatal - it is T2 plus one named line (knob 0) or one D line (unset):
//
// data plane side knob 0 knob 1
// Stream (tcp) either `Refuse{AdoptTierOnStream, "T0"}`, T2 `Refuse{AdoptTierOnStream, "T1"}`, T2
@@ -40,15 +42,28 @@ namespace MobileGL::MG_Remote::Transport {
enum class AdoptTierSide : std::uint8_t { Client, Server };
// THE HELLO'S T0 ASK WHEN THE KNOB IS UNSET (LinkTerms.adoptTier, protocol.fbs): T0 as asked
// by 0, except that the server names a refusal only at MGLOG_D. The ask travels because only
// the server knows why it did not grant T0 (its allow switch, its platform, its POST).
inline constexpr std::uint32_t kAdoptAskT0Default = 0x80;
// An ask that is a T0 ask (0 by name, kAdoptAskT0Default by default).
inline bool AdoptAskIsT0(std::uint32_t ask) { return ask == 0 || ask == kAdoptAskT0Default; }
// This process's knob asks for T0 (0, or unset).
bool AdoptTierWantsT0();
// This process's knob is unset: T0 fallbacks are quiet.
bool AdoptTierIsDefault();
// The tier the client's Hello asks for (LinkTerms.adoptTier): 0 when the knob says 0 and the
// plane it proposes is shared segments, else 2. Pure - the lines are the settle's.
// plane it proposes is shared segments, kAdoptAskT0Default when the knob is unset on shared
// segments, else 2. Pure - the lines are the settle's.
std::uint32_t AdoptTierAskFor(bool streamDataPlane);
// Settles this side's handshake answer from MOBILEGL_IPC_ADOPT_TIER and the negotiated data
// plane and returns the tier this side asks (client) or serves by its own knob (server): 0
// only for a client asking T0 over shared segments, 2 otherwise. Logs at most one line per
// call (A1's stream refusal, the T1 refusal). Never aborts for 0/1; a knob above 2 (only a
// direct assignment can make one) is still Fatal{UnimplementedAdoptTier}.
// plane and returns the ask this side made (client) or serves by its own knob (server):
// AdoptTierAskFor's value for a client asking T0 over shared segments, 2 otherwise. Logs at
// most one line per call (A1's stream refusal - at MGLOG_D when the knob is unset - and the
// T1 refusal). Never aborts for 0/1/unset; any other value (only a direct assignment can
// make one) is still Fatal{UnimplementedAdoptTier}.
std::uint32_t SettleAdoptTierAtHandshake(bool streamDataPlane, AdoptTierSide side);
// True once a handshake in this process has settled the tier. The at-use check trusts a
+1 -1
View File
@@ -3,5 +3,5 @@
"2": "b30928e6fc93ddc015fd86f634a6820f775a6f2f6de5a848d273981e706b2597",
"3": "da09f0acb48cf245703f28556ef5a38bc9e25241ebbf7b6baa9ab1765b7ddd50",
"4": "6357c3a07ae258d2825dc42bfd610712b15a38a3beb7a96b5f56ea2988705e7f",
"5": "f83f4c5ba6d15fac9c9f00c12b246c5f706f10c80719accd4ce116cb1a090c10"
"5": "4632750fd68b889cb6df709df9d150f9910468c0c530399de01bef87ba793904"
}