Files
MobileGL/MobileGL/MG_Remote/Transport/InProcessTransport.h
T
swung0x48 c1a7ffac94 [Fix] (MG_Remote, Transport): give descriptor offers their own condition variable, cap the ring at what a 32-bit size field can describe, and keep the frontend umbrella out of Framing.h
- InProcessChannel::Direction served two different predicates from one
  condition_variable signalled with notify_one, so a SendFrame wakeup could be
  delivered to a thread blocked in ReceiveFd, which re-tests its own predicate
  and goes back to sleep - leaving a queued message undelivered until some
  unrelated later event. ITransport narrows the contract to one dedicated
  reader thread, but that is a comment, not a mechanism, and the first caller
  that splits its reader should not have to discover this. Offers now have
  their own fdCv, and Close() notifies both.

- RingProducer/RingConsumer accepted any power-of-two capacity while
  RingRecordHeader::size is 32-bit by wire contract (plan section 8.1 ->
  PLAN.md section 6.3: 8-byte RecHeader). At 4 GiB or more a record's size -
  or a wrap filler's, which is sized by the distance to the boundary - would be
  truncated on the way in, and the consumer would then bounds-check the
  truncated value against the real one. kMaxRingCapacity rejects that at
  construction, the same class of guard as the power-of-two and
  smaller-than-a-header checks beside it. Unreachable today (SEG_CMD 8 MiB,
  SEG_STAGE 32 MiB), which is the point of catching it now.

- Framing.h included MG_Util/Debug/Log.h, which includes Includes.h, the GL
  frontend's umbrella header: 661 headers by `clang++ -H`. It is the one header
  under Transport/ that broke the rule ITransport.h states for this layer
  ("nothing about a byte pipe needs the GL frontend's umbrella header"), which
  matters when the server-side binary links this and when the include-graph
  purity gate of plan section 10.3 (gate A, asserted on -H output rather than
  on symbols) lands. Its three error paths now call WireLogError, declared in a
  new dependency-free WireLog.h whose .cpp owns the umbrella. Framing.h is down
  to 134 headers, none of them MG_State, Includes.h or Log.h.

- Two documentation corrections. ITransport::Shutdown documented a one-sided
  "releases the endpoint" while InProcessTransport::Shutdown closes both
  directions - which is what closing a socket does, so the spawn transport will
  behave the same way; the interface now says whole-connection teardown, and
  keeps the promise that queued messages stay readable until drained.
  InProcessTransport.h cited a CMake option
  MOBILEGL_BUILD_DISAGGREGATED_INPROC that grep finds nowhere: plan appendix B
  reserves it for the role-isolation shim, this skeleton does not add it, and
  the delivery mode is a runtime choice (MOBILEGL_TRANSPORT), not a build one.

- Evidence: both configurations reconfigured and rebuilt; nm --defined-only on
  the OFF build still reports 0 MG_Remote symbols and the ldd dependency set is
  byte-identical to the OFF link (plan section 10.3, the two surviving
  byte-level equalities). Negative controls: removing the capacity ceiling
  makes RingTest.RejectsACapacityTheRecordHeaderCannotDescribe fail on both
  roles; collapsing fdCv back into cv makes
  InProcessTransportTest.AFrameWakeupIsNotEatenByAWaiterOnDescriptors fail at
  3950ms against its 2000ms bound.
2026-09-05 20:16:50 -04:00

78 lines
3.3 KiB
C++

// MobileGL - MobileGL/MG_Remote/Transport/InProcessTransport.h
// Copyright (c) 2025-2026 MobileGL-Dev
// Licensed under the GNU Lesser General Public License v3.0:
// https://www.gnu.org/licenses/gpl-3.0.txt
// https://www.gnu.org/licenses/lgpl-3.0.txt
// SPDX-License-Identifier: LGPL-3.0-only
// End of Source File Header
// The `inproc` transport: two in-memory message queues and a pair of condvar
// doorbells, one connected endpoint at each end.
//
// It is not a test double. `inproc` is a delivery mode of its own - the server
// side is the monolith's own render thread, which is the single largest CPU
// lever this project has, and it is also the CI form of the split build. What
// it does NOT exercise is serialization of the byte stream, so the framing
// codec is covered separately by FramingTest.
//
// It is built by MOBILEGL_BUILD_DISAGGREGATED, the one option this skeleton
// adds, and selected at RUNTIME (plan appendix B: MOBILEGL_TRANSPORT =
// monolith / inproc / spawn / ...). The plan also reserves a separate
// MOBILEGL_BUILD_DISAGGREGATED_INPROC option for the role-isolation shim that
// a single-process CI build will need; that option does not exist yet, and
// nothing here depends on it.
//
// Messages are queued whole, so no framing bytes are involved; the size cap is
// still enforced so that a payload which would be illegal on a socket is
// illegal here too and does not pass CI only to fail after the switch to
// `spawn`.
//
// Descriptor passing is a plain dup(): both ends are the same process, so
// there is nothing to transfer, but the API stays identical so callers can be
// written once.
#pragma once
#include "Doorbell.h"
#include "ITransport.h"
#include <memory>
namespace MobileGL::MG_Remote::Transport {
class InProcessChannel;
class InProcessTransport final : public ITransport {
public:
~InProcessTransport() override;
// Creates one connected pair. Endpoint 0 is the client, endpoint 1 the
// server; both share one channel and either may be destroyed first.
static void CreatePair(std::unique_ptr<InProcessTransport>& outClient,
std::unique_ptr<InProcessTransport>& outServer);
MobileGLResult SendFrame(MobileGLByteSpan bytes) override;
MobileGLResult ReceiveFrame(MobileGLMutableByteSpan buffer, std::uint64_t* outSize,
std::uint32_t timeoutMs) override;
std::uint64_t PeekFrameSize() override;
MobileGLResult ShareFd(int fd, MobileGLByteSpan sideband) override;
MobileGLResult ReceiveFd(int* outFd, MobileGLMutableByteSpan sideband,
std::uint64_t* outSidebandSize, std::uint32_t timeoutMs) override;
void Shutdown() override;
TransportRole Role() const override { return TransportRole::InProcess; }
// The wake channel for the SEG_CMD/SEG_STAGE rings living beside this
// transport: ring the peer's bell after publishing a watermark (only
// when its park flag is set - see NotifyIfParked), park on your own.
Doorbell& PeerDoorbell();
Doorbell& SelfDoorbell();
private:
InProcessTransport(std::shared_ptr<InProcessChannel> channel, int endpoint);
std::shared_ptr<InProcessChannel> m_channel;
int m_endpoint = 0;
};
} // namespace MobileGL::MG_Remote::Transport