mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
0acee60a3be9fe252f995d2f5a29908b151976a2
4
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
0acee60a3b |
[MG_Remote] (P7): the spawn launcher passes MOBILEGL_IPC_WIRE_DEFERRED_MB through to the server child, the one MOBILEGL_IPC_* knob its environment scrub keeps
BuildChildEnv drops every MOBILEGL_IPC_* entry, so the server-owned watermark meant something under inproc and silently nothing under spawn - measured: the spawn server parked 47 MiB against a lane budget of 8 with zero forced syncs, because it had parsed the default out of an environment that no longer carried the variable. The knob names no endpoint, role or segment size, so neither anti-recursion catch (Dial=No, ServerMain's scrub check of TRANSPORT / SERVER_PATH / RING_MB / STAGE_MB) is weakened; it is listed by name, not by widening the prefix. |
||
|
|
7c8120cc49 |
[MG_Remote, android-plugin] (P6): a real phone retraces a real trace across two processes
OpenRA.DirectGLES, transport=spawn, on a Redmi: ssim=1.000000,
mismatchPixels=0, zero Fatal{ lines, three runs for three. The server is a
process the client launched out of the APK's lib/arm64/, and the client's own
log names its pid.
Four things stood between the desktop green and the device green, and every
one of them was invisible on desktop for the same reason: a build tree is not
a deployment, and Linux is not Android.
1. THE SERVER COULD NOT LINK. It links libMobileGL.so and is EXEC'd, not
dlopen'd, so it gets a fresh linker namespace with the DEFAULT search path
- and an APK's lib/<abi>/ is not on it. `CANNOT LINK EXECUTABLE ...:
library "libMobileGL.so" not found: needed by main executable`, before a
single line of MobileGL ran. On desktop CMake had been giving the target an
absolute build-tree RPATH, so the WSL server resolved its dependency
through a path no phone has. $ORIGIN is asked for via BUILD_RPATH (a raw
-Wl,-rpath,$ORIGIN reaches the generator unquoted and the SHELL eats it -
the device linker said `normalize_path - invalid input: "$"`), and because
the NDK drops RPATH from the link anyway, the LAUNCHER states
LD_LIBRARY_PATH: it is the one component that has already resolved where
the image lives, and a pair that is not co-located is not a pair.
2. THE RENDEZVOUS NEEDED A WRITABLE DIRECTORY. An Android app has no /tmp, so
bind() in the child had nowhere to put the node and the client refused by
name 20 s later. AF_UNIX endpoints now accept the ABSTRACT namespace with a
leading '@' - the systemd/D-Bus convention - which has no filesystem
presence at all: no directory, no mode bits, and no stale node to unlink
after a crash, which is the bind() failure Listen() has been working
around. Its reach is the network namespace, which for an app is the app.
StartSpawned mints '@' names; an explicit path still works everywhere, and
that is what P12's "connect to something somebody else is listening on"
will use.
3. PBUFFER WAS A DirectGLES-ONLY KNOB. Twice over: trace-replay-ci.sh gated
`--ez use_pbuffer true` on the backend name, and the JNI demanded a native
window for DirectVulkan unconditionally. A surface SHAPE is not a backend
property, and the coupling mattered because the Android GLWS makes a window
surface whenever gNativeWindow is non-null - so handing Magma a window was
the same as asking for one. Until P12 a spawned server cannot be given a
window at all: an ANativeWindow* is a pointer into the CLIENT's process and
SetWindowHandle is refused on the way across with Fatal{UnmigratedSurface,
"AndroidNativeWindow@P12"} (Rule H). Which is exactly what happened, one op
after the server had come up green.
4. THE RUNNER LOOKED IN ONE OF THE TWO OUTPUT DIRECTORIES. assembleTraceRelease
writes apk/trace/release; CI has staged the same artifact under
apk/trace/debug. A tree that has built locally has both, and picking the
wrong one retraces last month's APK and says nothing about it. Newest of
either, now.
MEASURED AND NOT FIXED: DirectVulkan on this device diverges under BOTH split
transports and does so nondeterministically - inproc scored ssim=0.988998 /
9836 px, spawn scored 0.976494 / 14658 px and then 0.988998 / 9836 px on a
repeat, while monolith with the same pbuffer surface scores 1.000000 / 0 px.
The two transports draw from the same set of outcomes, so this is the split
path on Magma against a real GPU, not the process boundary: P6 neither caused
it nor can fix it. DirectVulkan spawn IS green on desktop (lavapipe, 1.000000).
G14: one test name added, AnAbstractEndpointNeedsNoWritableDirectoryAndLeaves-
NoFile, which pins both halves - the handshake crosses an abstract name, and
nothing is left on the filesystem. A '@' mistaken for a path would have bound
a file of that literal name, passed here, and failed on the one platform this
exists for.
|
||
|
|
11807ac7d0 |
[MG_Remote] (P6/sm): two INDEPENDENT processes, joined by a name, sharing memory
The cross-process session is up. A server process is started on its own with an
endpoint and nothing else, it listens; a client started separately connects to
that name; Hello crosses, Welcome comes back with four SegmentRefs, six
descriptors cross by SCM_RIGHTS, and the client maps memory another process
created. ClientSession::Active() is non-null at the end of it, which is the flag
that arms the thirty-seven wire emitters.
INDEPENDENT, NOT FORKED - and that is a correction to this package's first
shape. The version committed an hour ago forked the server and handed it fds
3..6. It worked, and it was less code, but inheritance can only ever produce a
server that is a CHILD OF ITS CLIENT, which is the one arrangement the end state
cannot use: there the server is a standing application and the client is
somewhere else entirely, possibly on another kernel. CONTRACT-P6 §3.1 called
this Dial == Connect and filed it as a later shape. It is the primary one, and
fork/execve survives only as a way to START a process - every descriptor it used
to carry is gone.
What that costs and buys, concretely: the rendezvous becomes a NAME, so
SocketTransport grows Listen/AcceptPair/ConnectTo, with a bounded connect retry
that is the CLIENT's (a launcher that waited for the server to be ready would be
coupling the two again by the back door). The bells stop being inherited fds and
are created by the server and handed over with the segments - slots 4 and 5 of
six SCM_RIGHTS offers. And the client connects TWICE to the same path: an fd
offer is a sendmsg whose ancillary data rides with specific bytes, so it may not
share a socket with the framed control stream, or the frame reassembler and the
descriptor receiver race for the same bytes.
The pieces behind it:
- SessionSegments::AdoptFromDescriptors, which is the same Adopt+Map loop
AttachInProcess has run on every inproc session since P5. That was the point
of writing the inproc attach as dup+Adopt+Map rather than as an alias: only
the origin of the descriptor is new.
- ServerSession::SetExternalDoorbells, the narrow fix for a6 row A7-1 - the
§3.9 accessors abort for every role but InProcess, so a spawn apply thread
died before popping its first record. lk deletes them; until then an injected
pair is honest about where the bells come from.
- MG_Backend::InitServerRoleForSpawn: steps 1 and 2 of the split bring-up
extracted so the server process runs the SAME code the inproc server role
runs. The capability bits are a statement about the server's own backend and
a second copy would diverge the first time a backend registers a slot.
- ClientSession::FinishStartup: everything below the attach - producer, reply
pool, event ring, segment table, encoder, first CapsSnapshot, emitter tables -
shared verbatim between inproc and spawn. Its one parameter is not a
transport question: under inproc the client starts the apply thread because
there is no server-side main; under spawn there is one, and it started the
thread before it sent the first descriptor.
G1 CAUGHT A REAL LEAK on the way: InitServerRoleForSpawn was guarded on the
inside, so the pull build still DEFINED the symbol - 2 added, 1 removed, .text
+16 bytes. The whole function and its declaration are now inside the guard.
"It returns false there" is not the same as "it is not there", and the gate is
what said so.
Gates: ServerSpawnTest 4/4; unit suite 2344 with the same two pre-existing
environmental failures; G1 pull 0 added / 0 removed, .text 10822147 unchanged.
|
||
|
|
70c45ab9ce |
[MG_Remote] (P6/sm): two MobileGL processes, talking
The first time this project has had a second process. fork + execve of a real server image, the control socket surviving the exec, bytes crossing a process boundary, and the child exiting on EOF with nothing left behind. mobilegl_server_main EXISTS NOW. a6 found ARCHITECTURE.md:488-496 had described it for three phases while the symbol was nowhere in the tree, and the only MobileGLServer target was the P0 spike stub, which links no MobileGL library at all. The spike keeps its evidence and its name becomes MobileGLServerSpike; the real image links the library, so an undefined symbol is a build error rather than a dlsym failure in a child whose stderr is /dev/null. WHAT sm OWNS AND WHAT IT LEAVES: the process, the inherited descriptors, the transport and the exit discipline. Not the session, not the applier, not the four segments - wiring those from ServerMain before lk has a seam to wire them THROUGH is how call sites get written twice. The loop is a read/answer/exit-on- EOF stand-in whose SHAPE is the part that matters: one reader, EOF ends the process, and no timeout is ever allowed to stand in for the death fact (§5.4). ANTI-RECURSION IS TWO CATCHES, and neither is ARCHITECTURE.md:488's. That rule says the child hard-sets Transport to Monolith; a6 measured that 226 live lines test `Transport != Monolith` to select the SERVER arm, 148 in MG_Backend, so obeying it flips the whole backend to frontend glue in the one process with no frontend. Instead: (a) MOBILEGL_IPC_DIAL=no, structural and separate from role, and (b) MOBILEGL_TRANSPORT and every MOBILEGL_IPC_* scrubbed from the child's envp. The child refuses with 64 for (a) and 65 for (b) so the two safeties are distinguishable - two that cannot be told apart are one. TWO BUGS WORTH RECORDING, both found by the test rather than by review: dup2(n, n) IS A NO-OP THAT DOES NOT CLEAR CLOSE-ON-EXEC. FdPassing sets SOCK_CLOEXEC on the aux pair, so the child depends on dup2 to clear it - but in a gtest process a fresh socketpair lands on exactly fds 3..8, so a source fd often IS its own target. The no-op left CLOEXEC set and the following close() then shut the descriptor we had just "installed". The child came up with no socket and the parent saw TRANSPORT_CLOSED on its first send. Now spelled out rather than written cleverly. THE CHILD'S DIAGNOSTICS WERE INVISIBLE. WireLogError reaches the file sink only - Defines.h builds the logger with the console sink off - so a child that refused to start said so into a file the parent does not know the path of. ARCHITECTURE.md §15.2 warns about exactly this on Android. Both refusal paths now also write stderr directly, and both carry a distinct exit code the parent can name without reading anything. TESTS: six cases, 6/6. Each exists because something would otherwise pass silently - the reply carries the child's own getpid() so a test that talked to itself cannot pass (ID-124's lesson at the smallest scale); the process tree is COUNTED before, during and after, not promised; the child is proven not to exit while merely idle before it is proven to exit on EOF; an unresolvable image is a named refusal that spawns nothing (S1); a surviving client knob is caught by name (S3); and a descriptor crosses execve by SCM_RIGHTS, which is the mechanism the four segments will ride on and is now exercised before it is needed. Also records the asio decision in the contract: declined for P6 because spawn needs five platform mechanisms and asio covers one - its own socketpair returns operation_not_supported on Windows, it has no descriptor passing, segment delivery is POSIX-only by design, and there is no fork. Revisit at P6.5, where asio::generic::stream_protocol carries a raw family/type/protocol triple and therefore gives AF_VSOCK from one implementation. Gates: unit suite 2346, up 6, same two pre-existing environmental failures. G1 pull 0 added / 0 removed, .text size 10822147 unchanged, and zero spawn, server or SocketTransport symbols in the pull build. |