mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
[MG_Remote, MG_Test] (P7): end a forfeited session whose peer keeps talking on control
This commit is contained in:
@@ -94,9 +94,10 @@ namespace MobileGL::MG_Remote::Client {
|
||||
|
||||
// PH-6's fuzz arm 3 ONLY (EventForfeitPeerTest): the control stream, so a peer that has
|
||||
// stopped draining can still put on it the frames no ClientSession path would send at that
|
||||
// moment - a malformed one, a surface op queued behind a wait it did not drain for, or a
|
||||
// half-close. Every production path that writes control holds m_remoteControlMutex and
|
||||
// drains first; a caller of this does neither, which is the point. Null unless spawned.
|
||||
// moment - a malformed one, a surface op queued behind a wait it did not drain for, a
|
||||
// LogFlush every 100 ms, or a half-close. Every production path that writes control holds
|
||||
// m_remoteControlMutex and drains first; a caller of this does neither, which is the
|
||||
// point. Null unless spawned.
|
||||
Transport::SocketTransport* ControlSocketForTest() { return m_socketTransport.get(); }
|
||||
|
||||
// CONTRACT-P6 4.3: THE PID THE SERVER STATED IN ITS Welcome, which is not the same fact
|
||||
|
||||
@@ -369,18 +369,24 @@ void SendLogAck(void* pointer) {
|
||||
// later client Busy. Now each quiet interval asks whether the apply thread is still there;
|
||||
// when it is not (forfeit, or a dead data bell), the session takes the ordinary exit below:
|
||||
// Stop, Close, _exit(0). The interval is the supervisor's own accept poll.
|
||||
//
|
||||
// ASKED AT THE TOP OF EVERY TURN, NOT ONLY WHEN A WAIT TIMES OUT (PH-6 fix round). A peer that
|
||||
// forfeited the reverse channel but keeps TALKING - a LogFlush or a surface op more often than
|
||||
// every 250 ms, each of which this loop answers - never let ReceiveFrame time out, so the
|
||||
// question was never asked and the session, with the supervisor's one slot, lived as long as
|
||||
// the peer kept chattering.
|
||||
constexpr std::uint32_t kControlPollMs = 250;
|
||||
for (;;) {
|
||||
std::uint64_t size = 0;
|
||||
const auto result = control->ReceiveFrame({buffer.data(), buffer.size()}, &size, kControlPollMs);
|
||||
if (result == MOBILEGL_ERR_TIMEOUT) {
|
||||
if (loop.Running()) continue;
|
||||
if (!loop.Running()) {
|
||||
WireLogError("MG_Remote server: pid=%d the apply thread has stopped (%s, %llu event(s) dropped); "
|
||||
"ending the session",
|
||||
selfPid, session.ReverseChannelForfeited() ? "ReverseChannelForfeit" : "its bell died",
|
||||
static_cast<unsigned long long>(session.ForfeitDrops()));
|
||||
break;
|
||||
}
|
||||
std::uint64_t size = 0;
|
||||
const auto result = control->ReceiveFrame({buffer.data(), buffer.size()}, &size, kControlPollMs);
|
||||
if (result == MOBILEGL_ERR_TIMEOUT) continue;
|
||||
if (result == MOBILEGL_ERR_BUFFER_TOO_SMALL) { buffer.resize(static_cast<std::size_t>(size)); continue; }
|
||||
if (result != MOBILEGL_OK) {
|
||||
// EOF or a transport error: the peer is gone. Said to the session BEFORE loop.Stop()
|
||||
|
||||
@@ -29,7 +29,9 @@
|
||||
// sends). Each plane runs in its own ctest entry under its own lane label.
|
||||
//
|
||||
// THE SCENARIOS (all but the first are the PH-6 fix round's):
|
||||
// * the peer stops draining and stays - NotDraining after the knob;
|
||||
// * the peer stops draining and stays - NotDraining after the knob - and keeps TALKING on
|
||||
// control (a LogFlush every 100 ms), so the session must end without the control wait ever
|
||||
// timing out;
|
||||
// * the peer is KILLED while the server waits, with a knob longer than ServerLoop::Stop()'s
|
||||
// 5000 ms join - PeerGone well inside the knob, exit 0 (whichever of the data bell's death and
|
||||
// the control EOF's stop reaches the waiting apply thread first);
|
||||
@@ -52,6 +54,7 @@
|
||||
|
||||
#include <MG_Remote/Client/ClientSession.h>
|
||||
#include <MG_Remote/Protocol/SurfaceOpCodec.h>
|
||||
#include <MG_Remote/Protocol/generated/protocol_generated.h>
|
||||
#include <MG_Remote/Server/ServerLoop.h>
|
||||
#include <MG_Remote/Server/ServerSpawn.h>
|
||||
#include <MG_Remote/Server/SurfaceControlFrame.h>
|
||||
@@ -80,6 +83,7 @@
|
||||
#include <csignal>
|
||||
#include <fcntl.h>
|
||||
#include <netinet/in.h>
|
||||
#include <poll.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
@@ -364,6 +368,20 @@ namespace {
|
||||
control->SendFrame(MobileGLByteSpan{builder.GetBufferPointer(), builder.GetSize()}) == MOBILEGL_OK;
|
||||
}
|
||||
|
||||
// A LogFlush that asks for an acknowledgement (ack=false is the REQUEST; the server's answer
|
||||
// carries ack=true), exactly the frame ClientSession::SyncPeerLog sends - minus the drain that
|
||||
// SyncPeerLog does first.
|
||||
bool SendLogFlush(Client::ClientSession& client, std::uint64_t seq) {
|
||||
flatbuffers::FlatBufferBuilder builder(64);
|
||||
auto flush = ::MobileGL::Wire::CreateLogFlush(builder, seq, false);
|
||||
auto envelope =
|
||||
::MobileGL::Wire::CreateCtrlEnvelope(builder, ::MobileGL::Wire::CtrlMsg::LogFlush, flush.Union());
|
||||
::MobileGL::Wire::FinishCtrlEnvelopeBuffer(builder, envelope);
|
||||
auto* control = client.ControlSocketForTest();
|
||||
return control != nullptr &&
|
||||
control->SendFrame(MobileGLByteSpan{builder.GetBufferPointer(), builder.GetSize()}) == MOBILEGL_OK;
|
||||
}
|
||||
|
||||
// Four bytes: too short to carry a CtrlEnvelope, which ServerMain answers by ending the session
|
||||
// (P7 wave 0's named refusal) - a control stream that ENDS while the peer is still there.
|
||||
bool SendMalformedControlFrame(Client::ClientSession& client) {
|
||||
@@ -402,6 +420,21 @@ namespace {
|
||||
}
|
||||
if (!sent) ::_exit(kRawFrame);
|
||||
}
|
||||
if (scenario == Scenario::StopsDraining) {
|
||||
// SILENT ON SEG_EVENT, NOT ON CONTROL. A LogFlush every 100 ms, each of which the server
|
||||
// answers (and this peer never reads): a peer that talks keeps the server's control
|
||||
// wait from ever timing out, so the session may only end because its control loop asks
|
||||
// about the apply thread on every turn and not only on a quiet one. Until the server has
|
||||
// gone (a send fails) or the parent releases this peer.
|
||||
std::uint64_t seq = 0x7f000000ull;
|
||||
bool talking = true;
|
||||
for (;;) {
|
||||
struct pollfd wait{release, POLLIN, 0};
|
||||
if (::poll(&wait, 1, 100) != 0) break;
|
||||
if (talking) talking = SendLogFlush(client, ++seq);
|
||||
}
|
||||
::_exit(kChildOk);
|
||||
}
|
||||
char go = 0;
|
||||
(void)::read(release, &go, 1);
|
||||
::_exit(kChildOk);
|
||||
|
||||
Reference in New Issue
Block a user