mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-12 20:43:23 +09:00
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.
104 lines
5.0 KiB
C++
104 lines
5.0 KiB
C++
// MobileGL - MobileGL/MG_Remote/Server/SessionRuntime.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
|
|
|
|
// P14 S2 (docs/Disaggregated/design/11-state-ownership.md): ONE SERVED SESSION'S PROCESS-LOCAL
|
|
// STATE, and the routing that lets the rest of the tree keep spelling the singletons.
|
|
//
|
|
// WHAT THE SINGLETONS WERE. ServerSessionInstance()/ServerLoopInstance() were the session, so one
|
|
// session per process was a property of every file that named them. This slice gives each served
|
|
// session its own {ServerSession, ServerLoop, PipeInputs} triple, held here, and resolves the two
|
|
// singleton accessors through the CALLING THREAD's session - which is what makes the ~900 sites
|
|
// that spell gPipeInputs, MGB_CTX, ServerSession::Active() or ServerLoopInstance() find the right
|
|
// one without one line of any of them moving.
|
|
//
|
|
// WHO IS A SESSION THREAD. Two threads per session, both created by the session itself:
|
|
// * the CONTROL thread - ServerMain::RunSession's own thread, which reads the Hello, drives the
|
|
// blocking control pump and ends the session (a std::thread per session in the supervisors, a
|
|
// forked process in the `--serve` worker shape);
|
|
// * the APPLY thread - ServerLoop's mgl-srv-apply, which applies the ring and owns the backend.
|
|
// ThreadSessionScope is installed on both, first thing. A thread with no scope resolves to the
|
|
// process-wide defaults, which is exactly the old behaviour and is what the CLIENT process, the
|
|
// inproc one-process client+server shape and every unit case keep getting.
|
|
//
|
|
// THE INPROC CLIENT+SERVER SHAPE IS DELIBERATELY UNTOUCHED. ClientSession::Start calls
|
|
// ServerLoopInstance().Start(ServerSessionInstance()) and never opens a scope, so its applier and
|
|
// its app thread still share the one process-wide PipeInputs block - the verb barrier's premise
|
|
// (one block two roles alternate on) is a property of that shape and this file must not break it.
|
|
// Only ServerMain::RunSession, which serves a session with its own transport, opens a scope.
|
|
|
|
#pragma once
|
|
#include <Includes.h>
|
|
#include <MG_Backend/MGPipe/PipeInputs.h>
|
|
|
|
#include "ServerLoop.h"
|
|
#include "ServerSession.h"
|
|
|
|
#include <cstdint>
|
|
|
|
namespace MobileGL::MG_Remote::Server {
|
|
|
|
class SessionRuntime {
|
|
public:
|
|
SessionRuntime();
|
|
~SessionRuntime();
|
|
SessionRuntime(const SessionRuntime&) = delete;
|
|
SessionRuntime& operator=(const SessionRuntime&) = delete;
|
|
|
|
ServerSession& Session() { return m_session; }
|
|
ServerLoop& Loop() { return m_loop; }
|
|
MG_Pipe::PipeInputs& Inputs() { return m_inputs; }
|
|
|
|
// The latch domain this session owns (FatalFunnel.h). A named fault latched by one session
|
|
// must not end another, and must not be cleared by another's reap.
|
|
int LatchDomain() const { return m_latchDomain; }
|
|
|
|
private:
|
|
ServerSession m_session;
|
|
ServerLoop m_loop;
|
|
MG_Pipe::PipeInputs m_inputs;
|
|
int m_latchDomain = 0;
|
|
};
|
|
|
|
namespace Detail {
|
|
// The session of the CALLING thread, or nullptr when it is not a session's own thread.
|
|
SessionRuntime* CurrentRuntime();
|
|
ServerSession* CurrentSession();
|
|
ServerLoop* CurrentLoop();
|
|
// What MG_Pipe's per-thread resolver asks for: the calling thread's block, or nullptr for
|
|
// "use the process-wide one".
|
|
MG_Pipe::PipeInputs* CurrentInputs();
|
|
|
|
// Installs the process-wide hooks the per-session blocks need (the PipeInputs resolver).
|
|
// Idempotent, and called by ThreadSessionScope itself so no bring-up order can miss it.
|
|
void InstallSessionRuntimeHooks();
|
|
|
|
// THE SCOPE. Makes `runtime` the calling thread's session for as long as it lives: the
|
|
// session/loop the singleton accessors answer with, the PipeInputs block MGB_CTX reads,
|
|
// this session's segment resolver and this session's latch domain. Restores what was there
|
|
// before on the way out (two nested scopes are not a shape anything uses, but a thread that
|
|
// served one session and is then reused must not keep answering with the dead one).
|
|
class ThreadSessionScope {
|
|
public:
|
|
explicit ThreadSessionScope(SessionRuntime& runtime);
|
|
~ThreadSessionScope();
|
|
ThreadSessionScope(const ThreadSessionScope&) = delete;
|
|
ThreadSessionScope& operator=(const ThreadSessionScope&) = delete;
|
|
|
|
private:
|
|
SessionRuntime* m_previous = nullptr;
|
|
int m_previousLatchDomain = 0;
|
|
};
|
|
} // namespace Detail
|
|
|
|
// Sessions accepted and not yet closed in THIS process. 1 is the old shape; more is this
|
|
// slice's. ServerSession::Close gates the process-wide hooks (the reverse-channel table, the
|
|
// segment resolver) on it, and ServerMain logs it beside each session it starts.
|
|
Uint32 LiveServerSessionCount();
|
|
|
|
} // namespace MobileGL::MG_Remote::Server
|