From 35dddc0c890aae840d9ef5ae3573b837d1e359c8 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Mon, 28 Sep 2026 21:54:08 -0400 Subject: [PATCH] [Fix] (MG_Remote): the socket-io stats are LinkMetrics free functions, not StreamLink members 6bccf98d7 put TakeSendStats/TakeReadStats on the concrete StreamLink class, so ClientSession.cpp and ServerLoop.cpp had to include - which the P6.5 seam gate (scripts/ci/link_seam_purity.py, "concrete link included above Transport") rejects, and the MGPipe generators and hygiene gates job has been red since. The counters stay in StreamLink.cpp beside the recv/sendmsg loops that feed them; only the struct and the two Take functions move, to LinkMetrics.h as LinkIoStats + LinkMetricsTakeReadStats/TakeSendStats. The readers drop the concrete include; the hot loops are untouched. --- MobileGL/MG_Remote/Client/ClientSession.cpp | 3 +-- MobileGL/MG_Remote/Server/ServerLoop.cpp | 4 +-- MobileGL/MG_Remote/Transport/LinkMetrics.h | 28 ++++++++++++++++++++- MobileGL/MG_Remote/Transport/StreamLink.cpp | 11 ++++---- MobileGL/MG_Remote/Transport/StreamLink.h | 21 +++------------- 5 files changed, 40 insertions(+), 27 deletions(-) diff --git a/MobileGL/MG_Remote/Client/ClientSession.cpp b/MobileGL/MG_Remote/Client/ClientSession.cpp index 2506e8de..85e55a30 100644 --- a/MobileGL/MG_Remote/Client/ClientSession.cpp +++ b/MobileGL/MG_Remote/Client/ClientSession.cpp @@ -13,7 +13,6 @@ #include #include #include -#include #include "../CapsCodec.h" #include "../Protocol/generated/protocol_generated.h" @@ -2300,7 +2299,7 @@ namespace MobileGL::MG_Remote::Client { const double creditMs = static_cast(std::chrono::duration_cast( std::chrono::steady_clock::now() - paceStarted).count()) / 1e6; - const auto io = Transport::StreamLink::TakeSendStats(); + const auto io = Transport::LinkMetricsTakeSendStats(); MGLOG_I("P65ClientPace present=%llu credit_wait_ms=%.1f | io: %llu B in %llu sendmsg " "send_ms=%.1f total_ms=%.1f", static_cast(serial), creditMs, diff --git a/MobileGL/MG_Remote/Server/ServerLoop.cpp b/MobileGL/MG_Remote/Server/ServerLoop.cpp index 3b75967f..93b7549f 100644 --- a/MobileGL/MG_Remote/Server/ServerLoop.cpp +++ b/MobileGL/MG_Remote/Server/ServerLoop.cpp @@ -10,7 +10,7 @@ #include "ServerLoop.h" #include -#include +#include #include #include @@ -796,7 +796,7 @@ namespace MobileGL::MG_Remote::Server { // recv - blocking included - so a frame that is 90% read_ms is a frame the // reader spent WAITING for bytes, and one that is mostly recv calls with // little read_ms is a reader that is copying. - const auto read = Transport::StreamLink::TakeReadStats(); + const auto read = Transport::LinkMetricsTakeReadStats(); MGLOG_I("P65ServerFrame frame=%llu records=%llu apply_ms=%.1f wall_ms=%.1f " "outside_ms=%.1f | read: %llu B in %llu recv(%llu reads) " "recv_ms=%.1f total_ms=%.1f (%.0f%% blocked, %.1f MB/s)", diff --git a/MobileGL/MG_Remote/Transport/LinkMetrics.h b/MobileGL/MG_Remote/Transport/LinkMetrics.h index a629c844..573b5505 100644 --- a/MobileGL/MG_Remote/Transport/LinkMetrics.h +++ b/MobileGL/MG_Remote/Transport/LinkMetrics.h @@ -1,4 +1,6 @@ -// P6.5 transport measurements. Called only by the single client producer. +// P6.5 transport measurements. The Window state is written only by the single client +// producer; the socket-io counters drained by LinkMetricsTakeReadStats/TakeSendStats are +// atomic and cross-thread (io thread writes, apply/client thread reads) - see below. #pragma once #include @@ -41,4 +43,28 @@ namespace MobileGL::MG_Remote::Transport { std::uint64_t LinkMetricsRecordsFor(std::uint32_t op); void LinkMetricsPresent(); void LinkMetricsServerPresent(std::uint64_t serial); + + // P65READ: THE SERVER'S SOCKET-READ PATH, ACCOUNTED WHERE IT CAN BE SEEN. The counters are + // published from the io thread's recv loop and read by the APPLY thread, because the io + // thread's own MGLOG_ lines never reach the phone's forwarded log (only mgl-srv-apply and + // mgl-display-ser do - measured, not assumed). Taking them RESETS them, so one call is one + // frame's worth of the stage nothing else measures: the frame's time is not the client's GL + // thread (13%) and not PipeApplier::ApplyOne (11%), and this is what sits between them. + // + // Free functions, NOT StreamLink members: the P6.5 seam gate (scripts/ci/link_seam_purity.py) + // rejects a concrete-link include outside Transport, and the readers of these numbers live in + // ClientSession/ServerLoop. The counters themselves stay in StreamLink.cpp beside the recv / + // sendmsg loops that feed them. + struct LinkIoStats { + std::uint64_t bytes = 0; // payload bytes taken off the socket + std::uint64_t calls = 0; // recv() calls it took + std::uint64_t reads = 0; // Link::Read() calls (one per header, envelope or chunk) + std::uint64_t nsInRecv = 0; // wall time inside recv - blocking included + std::uint64_t nsTotal = 0; // wall time inside Read + }; + LinkIoStats LinkMetricsTakeReadStats(); + // The client's half: bytes handed to the io thread's sendmsg, the calls it took, and the + // wall time inside sendmsg (blocking included). nsInRecv carries the send time; reads is + // unused on this side. + LinkIoStats LinkMetricsTakeSendStats(); } diff --git a/MobileGL/MG_Remote/Transport/StreamLink.cpp b/MobileGL/MG_Remote/Transport/StreamLink.cpp index c0bc9699..866d8ab5 100644 --- a/MobileGL/MG_Remote/Transport/StreamLink.cpp +++ b/MobileGL/MG_Remote/Transport/StreamLink.cpp @@ -5,6 +5,7 @@ #include "Doorbell.h" #include "ReplySlot.h" #include "Framing.h" +#include "LinkMetrics.h" // P12: the declared-answer store's crossing log. Same include as LinkMetrics.cpp's, rather than // , because this file is transport-layer and pulls nothing else from the tree. #include @@ -28,7 +29,7 @@ namespace MobileGL::MG_Remote::Transport { // P65Read's counters: written by the server's io thread, drained by its apply thread (see - // StreamLink::TakeReadStats). + // LinkMetricsTakeReadStats). static std::atomic gReadBytes{0}, gReadCalls{0}, gReadReads{0}; static std::atomic gReadNsInRecv{0}, gReadNsTotal{0}; // P65CLIENTPACE's send half: the io thread's side of the same question. A sendmsg that blocks @@ -479,8 +480,8 @@ namespace MobileGL::MG_Remote::Transport { } }; - StreamLink::ReadStats StreamLink::TakeSendStats() { - ReadStats out; + LinkIoStats LinkMetricsTakeSendStats() { + LinkIoStats out; out.bytes = gSendBytes.exchange(0, std::memory_order_relaxed); out.calls = gSendCalls.exchange(0, std::memory_order_relaxed); out.nsInRecv = gSendNsInSend.exchange(0, std::memory_order_relaxed); @@ -488,8 +489,8 @@ namespace MobileGL::MG_Remote::Transport { return out; } - StreamLink::ReadStats StreamLink::TakeReadStats() { - ReadStats out; + LinkIoStats LinkMetricsTakeReadStats() { + LinkIoStats out; out.bytes = gReadBytes.exchange(0, std::memory_order_relaxed); out.calls = gReadCalls.exchange(0, std::memory_order_relaxed); out.reads = gReadReads.exchange(0, std::memory_order_relaxed); diff --git a/MobileGL/MG_Remote/Transport/StreamLink.h b/MobileGL/MG_Remote/Transport/StreamLink.h index c6118db1..8fba8885 100644 --- a/MobileGL/MG_Remote/Transport/StreamLink.h +++ b/MobileGL/MG_Remote/Transport/StreamLink.h @@ -11,23 +11,10 @@ namespace MobileGL::MG_Remote::Transport { public: StreamLink(); ~StreamLink() override; - // P65READ: THE SERVER'S SOCKET-READ PATH, ACCOUNTED WHERE IT CAN BE SEEN. The counters are - // published from the io thread's recv loop and read by the APPLY thread, because the io - // thread's own MGLOG_ lines never reach the phone's forwarded log (only mgl-srv-apply and - // mgl-display-ser do - measured, not assumed). Taking them RESETS them, so one call is one - // frame's worth of the stage nothing else measures: the frame's time is not the client's GL - // thread (13%) and not PipeApplier::ApplyOne (11%), and this is what sits between them. - struct ReadStats { - std::uint64_t bytes = 0; // payload bytes taken off the socket - std::uint64_t calls = 0; // recv() calls it took - std::uint64_t reads = 0; // Link::Read() calls (one per header, envelope or chunk) - std::uint64_t nsInRecv = 0; // wall time inside recv - blocking included - std::uint64_t nsTotal = 0; // wall time inside Read - }; - static ReadStats TakeReadStats(); - // The client's half: bytes handed to the io thread's sendmsg, the calls it took, and the - // wall time inside sendmsg (blocking included). nsInRecv carries the send time. - static ReadStats TakeSendStats(); + // The P65READ/P65CLIENTPACE socket-io stats moved to LinkMetrics.h + // (LinkMetricsTakeReadStats/TakeSendStats): the seam gate rejects this concrete + // header outside Transport, and the readers are ClientSession/ServerLoop. The + // counters themselves are still in StreamLink.cpp beside the io loops. MobileGLResult Attach(int dataFd, SessionSegments& mirrors, TransportRoleTag role); MobileGLResult AttachOwned(int dataFd, const struct SessionSegmentSizes& sizes, TransportRoleTag role); // PH-7 (4), ID-P7-3. The SERVER's half of a nonce-bound data connection, in two steps.