From f91d0f2c4b9e85f64f17a998b3a88eb527b36ba9 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Tue, 22 Sep 2026 22:15:52 -0400 Subject: [PATCH] [MG_Test] (P7): X2 - InitialCapsStartup pairs its TCP sockets by DataBind nonce, not by accept order - the fixture rendezvoused with ConnectTo/AcceptPair over loopback TCP, which pairs the two connections by the order the listener accepts them; under host load the kernel delivers two back-to-back loopback connects to the accept queue out of connect order (measured 5 of 3000 with 20 CPU hogs), the peer's control socket became the client's data socket, the first control frame hit the StreamLink reader and the process died of Fatal{ProtocolCorruption, "StreamLink"} invalid data frame header - 2 of 200 and 2 of 500 runs of ANullInitialSnapshot..., 5 of 500 of DelayedTcpSnapshot..., every one in 3 of the last 4 integration gates on the loaded host - the flake is in the TEST: PH-7 (4) already took by-order pairing out of the product on TCP ("never paired by arrival order"); the fixture now does what ServerMain's ListenerSource does - ConnectControl is accepted before ConnectDataConnection is opened, and the data connection names itself with a DataBind nonce that the peer decodes and matches in constant time - 0 of 500 for both cases under the same load afterwards; AHealthyPeer... shares the fixture and is covered by the same change; no product bytes touched --- .../MG_Test/Wire/InitialCapsStartupTest.cpp | 48 +++++++++++++++---- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/MobileGL/MG_Test/Wire/InitialCapsStartupTest.cpp b/MobileGL/MG_Test/Wire/InitialCapsStartupTest.cpp index 2dac0bee..7bfeaeb6 100644 --- a/MobileGL/MG_Test/Wire/InitialCapsStartupTest.cpp +++ b/MobileGL/MG_Test/Wire/InitialCapsStartupTest.cpp @@ -3,6 +3,8 @@ // The peer publishes real CapsSnapshot bytes; no private run-ahead flag is set. #include #include +#include +#include #include #include #include @@ -10,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -57,25 +60,52 @@ namespace { ::close(reserve); int listener = -1; ASSERT_EQ(Transport::SocketTransport::Listen(endpoint, &listener), MOBILEGL_OK); - MobileGLResult accepted = MOBILEGL_ERR_TIMEOUT; - std::thread accepting( - [&] { accepted = Transport::SocketTransport::AcceptPair(listener, 2000, controlPeer); }); - const auto connected = Transport::SocketTransport::ConnectTo(endpoint, 2000, controlClient); - accepting.join(); - ::close(listener); + + // X2 (P7): THE PRODUCT'S TCP RENDEZVOUS, NOT ConnectTo/AcceptPair. Two loopback TCP + // connections opened back to back reach the listener's accept queue out of connect + // order under load (measured: 5 of 3000 with 20 CPU hogs on the host), and AcceptPair + // pairs them BY ORDER - the peer's control socket was then the client's data socket, + // the first CapsSnapshot landed on the StreamLink reader and the process died of + // Fatal{ProtocolCorruption, "StreamLink"} invalid data frame header. PH-7 (4) took + // exactly this pairing out of the product on TCP (SocketTransport.h: "never paired + // by arrival order"); the fixture now does what ServerMain's ListenerSource does - + // the control connection is accepted before the data connection is opened, and the + // data connection names itself with a DataBind nonce rather than by arrival. + const auto connected = Transport::SocketTransport::ConnectControl(endpoint, 2000, controlClient); + int peerControlFd = -1; + const auto accepted = Transport::SocketTransport::AcceptOne(listener, 2000, &peerControlFd); + if (accepted == MOBILEGL_OK) + controlPeer = std::make_unique(peerControlFd, -1, + Transport::TransportRole::Server); ASSERT_EQ(connected, MOBILEGL_OK); ASSERT_EQ(accepted, MOBILEGL_OK); ASSERT_TRUE(controlClient->IsTcp()); ASSERT_TRUE(controlPeer->IsTcp()); + Uint8 nonce[Transport::kDataNonceBytes]; + ASSERT_EQ(Transport::SocketTransport::MintNonce(nonce, sizeof nonce), MOBILEGL_OK); + const auto bind = EncodeDataBind(nonce, sizeof nonce); + int clientDataFd = -1, peerDataFd = -1; + ASSERT_EQ(Transport::SocketTransport::ConnectDataConnection( + endpoint, 2000, MobileGLByteSpan{bind.data(), bind.size()}, &clientDataFd), + MOBILEGL_OK); + const auto dataAccepted = Transport::SocketTransport::AcceptOne(listener, 2000, &peerDataFd); + ::close(listener); + ASSERT_EQ(dataAccepted, MOBILEGL_OK); + std::vector first; + ASSERT_EQ(Transport::SocketTransport::ReceiveOneFrame(peerDataFd, 2000, 1024, &first), MOBILEGL_OK); + Uint8 presented[Transport::kDataNonceBytes] = {}; + ASSERT_TRUE(DecodeDataBind(first, presented)); + ASSERT_TRUE(Transport::ConstantTimeNonceMatch(nonce, presented)); + Transport::SessionSegmentSizes sizes; sizes.CmdRingBytes = 4096; sizes.StageBytes = 4096; sizes.EventRingBytes = 4096; sizes.ReplyBytes = 4096; - ASSERT_EQ(client.AttachStreamLink(controlClient->TakeDataFd(), sizes), MOBILEGL_OK); - ASSERT_EQ(Transport::CreateStreamLink(controlPeer->TakeDataFd(), sizes, - Transport::TransportRoleTag::ServerConsumer, dataPeer), + ASSERT_EQ(client.AttachStreamLink(clientDataFd, sizes), MOBILEGL_OK); + ASSERT_EQ(Transport::CreateStreamLink(peerDataFd, sizes, Transport::TransportRoleTag::ServerConsumer, + dataPeer), MOBILEGL_OK); dataPeer->InitializeEndpoints(); client.*StartupPeerMember(StartupControlTag{}) = controlClient.get();