mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-08 02:23:23 +09:00
[Feature] (P14): state ownership - multi-session, multi-context, share groups
Breaks the one process = one server = one session = one context equation
that the whole stack's correctness used to rest on. Design and per-slice
shape: docs/Disaggregated/design/11-state-ownership.md.
S1 wire: bind_context ring record (opcode 84) announces the current
context at the make-current edge; CreateContext/DestroyContext join the
SurfaceOp control frames; control revision 5 -> 6; the server keeps a
per-session token table and refuses unknown tokens by name.
S2 server: SessionRuntime per session with thread-scoped resolution
(singletons become fallbacks), per-thread gPipeInputs, per-session latch
domains, per-thread segment resolvers; the in-process unix supervisor
serves up to MOBILEGL_IPC_INPROC_MAX_SESSIONS (16) concurrent sessions;
surface mode is declared per session and the display lease refuses a
second on-screen session by name.
S3 frontend: ShareGroupState owns the shared object stores and name
generators (buffer/texture/renderbuffer/sampler/program), GLContext keeps
per-context bindings/errors/container objects; pGLContext becomes
thread-local with a process default context preserving the single-context
world verbatim; eglCreateContext builds a real GLContext and joins the
sharing context's group.
S4 backend: native EGL tuples keyed by (session, context token);
InitDisplayAndContext's unconditional teardown is gone - creating a
second context or surface no longer destroys the first; eglCreateContext
sharing is wired to the native driver; bind_context is the native switch
point.
S5 applier: MGPipeApplierState splits into MGPipeObjectRecords (per
share group) and MGPipeWorkingState (per context), resolved through a
{session, token} registry with a thread-local cached fast path.
Tests: MultiSessionTest (incl. two sessions x two contexts interleaved
with real clear/readback witnesses), ApplierOwnershipTest (with a
null-resolver negative control), ContextStateOwnershipTest,
ServerLoopEglTest native-context liveness; 99/99 of the acceptance set
green on the host build.
Known debts (documented in the design doc): twin registries still keyed
by handle alone, no per-record ContextSlot, process-global backend
generations, MakeCurrent dedup per current binding.
This commit is contained in:
@@ -1462,6 +1462,53 @@ namespace MobileGL::MG_Remote::Server {
|
||||
return true;
|
||||
}
|
||||
|
||||
// P14 S1. THE RING'S DOOR INTO THE SESSION'S CONTEXT TABLE.
|
||||
//
|
||||
// The record carries the client's current context token and this is where a session's
|
||||
// attribution moves. It is deliberately NOT an assertion like OnApplierReset's serial: the
|
||||
// client's EGL layer mints the token and the server has no second count to compare against,
|
||||
// so what is checked is a FACT ABOUT THIS SESSION'S OWN TABLE - a token that CreateContext
|
||||
// never created is corruption, not a late record. Token 0 is the release edge and is always
|
||||
// legal (a session whose client released its context has no context, which is exactly the
|
||||
// value a session that never bound one already answers).
|
||||
Bool ServerVerbSink::OnBindContext(const MG_Pipe::MGPBindContext& bind) {
|
||||
// NO SESSION IS A DECLINE, NOT A FAULT, and it is the one case here that a peer's bytes
|
||||
// cannot cause: a bind is answered by the session's own table, so with no accepted
|
||||
// session there is nothing to attribute the record to and nothing to refuse. (The window
|
||||
// is Close() clearing the active session while the apply thread is still draining.)
|
||||
ServerSession* session = ServerSession::Active();
|
||||
if (session == nullptr) return false;
|
||||
// PH-1 (3): the refusal latches in an armed session child (the peer wrote the token);
|
||||
// unarmed it dies, like every other contract fact about a peer.
|
||||
if (!session->BindContext(bind.ClientContextToken)) {
|
||||
return SessionLatch(MGFatalFamily::ProtocolCorruption, "MGPipe: Fatal{ProtocolCorruption, \"BindContext.ClientContextToken\"} - the "
|
||||
"record binds token %llu, which this session never created; a bind_context "
|
||||
"names a context CreateContext put in the session's table, and 0 (the "
|
||||
"release) is the only token that needs no entry",
|
||||
static_cast<unsigned long long>(bind.ClientContextToken));
|
||||
}
|
||||
++m_bindContexts;
|
||||
#if MOBILEGL_BUILD_DISAGGREGATED
|
||||
// P14 S4 (docs/Disaggregated/design/11-state-ownership.md). THE NATIVE HALF OF THE SWITCH.
|
||||
//
|
||||
// `bind_context` is where the session's context attribution moves, and it is also the ONLY
|
||||
// point at which the apply thread's native context may move: the MakeCurrent control frame
|
||||
// that precedes it still carries the OLD context (the client emits the binding after the
|
||||
// binding has taken effect, which is the ordering S1 chose on purpose), so a native switch
|
||||
// driven from there would bind the context the client just left. Frontend GL state is the
|
||||
// backend's per-tuple business; this call makes the tuple the resolver now names current.
|
||||
//
|
||||
// It is a no-op for the shapes S4 did not change: with no resolver installed (monolith, a
|
||||
// unit case, the inproc one-process shape) the key is {0,0}, and the single-context world's
|
||||
// bind does not move the native context - MakeNativeContextCurrentForBoundToken returns
|
||||
// without a driver call when the context it resolves to is already the one bound here.
|
||||
if (MG_Config::Transport != MG_Config::TransportMode::Monolith) {
|
||||
(void)MG_Backend::DirectGLES::MakeNativeContextCurrentForBoundToken();
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
Bool ServerVerbSink::OnObjectDeath(const MG_Pipe::MGPHandleOnly& death) {
|
||||
// §1's zero ruling: a null handle means "the object never crossed", and the client
|
||||
// emits NOTHING in that case (§5.2) - so a null handle arriving here is corruption,
|
||||
|
||||
Reference in New Issue
Block a user