mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
161 lines
8.3 KiB
C++
161 lines
8.3 KiB
C++
// MobileGL - MobileGL/MG_Remote/Server/SessionRuntime.cpp
|
|
// 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: the per-session runtime and the thread routing (SessionRuntime.h states the shape).
|
|
|
|
#include "SessionRuntime.h"
|
|
|
|
#include <MG_Backend/DirectGLES/DirectGLES.h>
|
|
#include <MG_Remote/FatalFunnel.h>
|
|
#include <MG_Backend/Record/StagedShadow.h>
|
|
#include <MG_Remote/Wire/PipeWireCodec.h>
|
|
|
|
#include <atomic>
|
|
|
|
namespace MobileGL::MG_Remote::Server {
|
|
|
|
namespace {
|
|
// The calling thread's session. nullptr on every thread that is not one of a session's own
|
|
// two, which is the whole of the fallback rule.
|
|
thread_local SessionRuntime* t_runtime = nullptr;
|
|
|
|
// The resolver MG_Pipe asks per thread. Installing it is a single store of a function
|
|
// pointer that is never replaced, so it needs no lock and no teardown: the server role
|
|
// lives for the process (ID-8).
|
|
MG_Pipe::PipeInputs* ResolveThreadInputs() {
|
|
SessionRuntime* runtime = t_runtime;
|
|
return runtime != nullptr ? &runtime->Inputs() : nullptr;
|
|
}
|
|
|
|
// P14 S4 (docs/Disaggregated/design/11-state-ownership.md). THE NATIVE TUPLE'S KEY. The
|
|
// backend keys its per-context native state - the EGLContext, its config, the draw/read
|
|
// binding - on (session, client context token); this is the one place that answers the pair
|
|
// for the calling thread. The session half is the runtime's ADDRESS, which is stable for the
|
|
// session's whole life and unique among live sessions in the process; the token half is what
|
|
// `bind_context` last set on the ring. False (== the {0,0} key) on any thread that is not a
|
|
// session's own, which is the client process, the inproc one-process shape and every unit
|
|
// case - i.e. exactly the single-context world the backend still serves unchanged.
|
|
Bool ResolveThreadNativeContextKey(Uint64* outSessionKey, Uint64* outContextToken) {
|
|
SessionRuntime* runtime = t_runtime;
|
|
if (runtime == nullptr) return false;
|
|
*outSessionKey = reinterpret_cast<Uint64>(runtime);
|
|
*outContextToken = runtime->Session().CurrentContextToken();
|
|
return true;
|
|
}
|
|
|
|
// P14 S5 (docs/Disaggregated/design/11-state-ownership.md). WHOSE APPLIER IS THIS THREAD
|
|
// IN? The same two-word answer as the native key above, and deliberately the same
|
|
// spelling: the session half is the ServerSession's own address - which is the key the
|
|
// registration side uses (ServerSession::CreateContext passes `this`), so the two agree
|
|
// by construction rather than by two conventions - and the token half is what
|
|
// `bind_context` last set on this session's ring. A thread that is not a session's
|
|
// answers false and gets the process-wide applier, i.e. the single-context world every
|
|
// other shape in this tree still runs.
|
|
Bool ResolveThreadApplierKey(MG_Pipe::MGPipeApplierKey* outKey) {
|
|
SessionRuntime* runtime = t_runtime;
|
|
if (runtime == nullptr) return false;
|
|
outKey->SessionKey = reinterpret_cast<Uint64>(&runtime->Session());
|
|
outKey->ContextToken = runtime->Session().CurrentContextToken();
|
|
return true;
|
|
}
|
|
|
|
// P14 S6 (docs/Disaggregated/design/11-state-ownership.md). WHICH TWIN TABLE DOES THIS
|
|
// THREAD'S BACKEND OBJECT BELONG TO? The backend's twin tables are per {session, share
|
|
// group} - a twin owns a DRIVER id, and a driver id belongs to one native context's
|
|
// object namespace - so this answers the same two words the applier's OBJECT RECORDS are
|
|
// filed under, resolved through the same `CurrentContextToken()` and the same
|
|
// MGPipeApplierShareGroupKeyFor rule the applier itself uses. Sharing a helper rather
|
|
// than re-deriving the rule is the point: a record and the twin it describes must always
|
|
// land in the same bucket, and a second spelling of "0 means its own group" is exactly
|
|
// how they would stop doing that.
|
|
Bool ResolveThreadTwinKey(MG_Backend::DirectGLES::TwinKey* outKey) {
|
|
SessionRuntime* runtime = t_runtime;
|
|
if (runtime == nullptr) return false;
|
|
outKey->SessionKey = reinterpret_cast<Uint64>(&runtime->Session());
|
|
outKey->ShareGroupKey = MG_Pipe::MGPipeApplierShareGroupKeyFor(
|
|
outKey->SessionKey, runtime->Session().CurrentContextToken());
|
|
return true;
|
|
}
|
|
|
|
// The server's staged stores (StagedShadow.h) file their entries under the same two
|
|
// words as the twin tables, for the same reason one level up: the bytes they hold belong
|
|
// to one session's group, and a session's teardown may drop only its own.
|
|
Bool ResolveThreadStagedBucket(MG_Record::StagedBucket* outBucket) {
|
|
MG_Backend::DirectGLES::TwinKey key;
|
|
if (!ResolveThreadTwinKey(&key)) return false;
|
|
outBucket->SessionKey = key.SessionKey;
|
|
outBucket->ShareGroupKey = key.ShareGroupKey;
|
|
return true;
|
|
}
|
|
} // namespace
|
|
|
|
void Detail::InstallSessionRuntimeHooks() {
|
|
if (MG_Pipe::g_pipeInputsThreadResolver != &ResolveThreadInputs) {
|
|
MG_Pipe::g_pipeInputsThreadResolver = &ResolveThreadInputs;
|
|
}
|
|
MG_Backend::DirectGLES::SetNativeContextKeyResolver(&ResolveThreadNativeContextKey);
|
|
MG_Pipe::MGPipeSetApplierKeyResolver(&ResolveThreadApplierKey);
|
|
// P14 S6: the twin tables' key. Same probe, one level down, installed beside the two it
|
|
// is the twin of (the native tuple's and the applier's).
|
|
MG_Backend::DirectGLES::SetTwinKeyResolver(&ResolveThreadTwinKey);
|
|
MG_Record::SetStagedBucketResolver(&ResolveThreadStagedBucket);
|
|
}
|
|
|
|
SessionRuntime* Detail::CurrentRuntime() { return t_runtime; }
|
|
|
|
ServerSession* Detail::CurrentSession() {
|
|
SessionRuntime* runtime = t_runtime;
|
|
return runtime != nullptr ? &runtime->Session() : nullptr;
|
|
}
|
|
|
|
ServerLoop* Detail::CurrentLoop() {
|
|
SessionRuntime* runtime = t_runtime;
|
|
return runtime != nullptr ? &runtime->Loop() : nullptr;
|
|
}
|
|
|
|
MG_Pipe::PipeInputs* Detail::CurrentInputs() { return ResolveThreadInputs(); }
|
|
|
|
Detail::ThreadSessionScope::ThreadSessionScope(SessionRuntime& runtime) {
|
|
InstallSessionRuntimeHooks();
|
|
m_previous = t_runtime;
|
|
t_runtime = &runtime;
|
|
m_previousLatchDomain = FatalLatchDomainBind(runtime.LatchDomain());
|
|
// The segment resolver is the OTHER process-wide hook that used to mean "one session": the
|
|
// thunk is one function pointer for the process, and which TABLE it answers from is now
|
|
// per thread (PipeWireCodec.h). Bound before anything on this thread can decode a record.
|
|
runtime.Session().Segments().BindThreadResolver();
|
|
}
|
|
|
|
Detail::ThreadSessionScope::~ThreadSessionScope() {
|
|
if (m_previous != nullptr) {
|
|
m_previous->Session().Segments().BindThreadResolver();
|
|
} else {
|
|
Wire::SegmentTable::UnbindThreadResolver();
|
|
}
|
|
FatalLatchDomainBind(m_previousLatchDomain);
|
|
t_runtime = m_previous;
|
|
}
|
|
|
|
SessionRuntime::SessionRuntime()
|
|
: m_latchDomain(FatalLatchDomainOpen()) {}
|
|
|
|
SessionRuntime::~SessionRuntime() {
|
|
// P14 S5: this runtime's session is the key every applier it registered was filed
|
|
// under, so the last act of the session is to drop them. Safe here for the reason the
|
|
// latch domain is (SessionLoop::Stop has joined the apply thread; the control thread's
|
|
// scope outlives this object), and idempotent against Accept's own release.
|
|
MG_Pipe::MGPipeApplierReleaseSession(reinterpret_cast<Uint64>(&m_session));
|
|
// No thread may still be bound to this domain: the apply thread is joined by
|
|
// ServerLoop::Stop and the control thread's scope outlives this object.
|
|
FatalLatchDomainClose(m_latchDomain);
|
|
m_latchDomain = 0;
|
|
}
|
|
|
|
Uint32 LiveServerSessionCount() { return ServerSessionCount(); }
|
|
|
|
} // namespace MobileGL::MG_Remote::Server
|