mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-08 10:33:24 +09:00
[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
This commit is contained in:
@@ -3,6 +3,8 @@
|
||||
// The peer publishes real CapsSnapshot bytes; no private run-ahead flag is set.
|
||||
#include <MG_Remote/Client/ClientSession.h>
|
||||
#include <MG_Remote/CapsCodec.h>
|
||||
#include <MG_Remote/Handshake.h>
|
||||
#include <MG_Remote/Transport/AuthToken.h>
|
||||
#include <MG_Remote/Transport/ControlInbox.h>
|
||||
#include <MG_Remote/Transport/SocketTransport.h>
|
||||
#include <MG_Remote/Protocol/generated/protocol_generated.h>
|
||||
@@ -10,6 +12,7 @@
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
@@ -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<Transport::SocketTransport>(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<Uint8> 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();
|
||||
|
||||
Reference in New Issue
Block a user