mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
[Feat] (Remote): give inproc T0 the server's own store and never an AHB
This commit is contained in:
@@ -164,6 +164,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
||||
return VkBufferManager::SelfTestWireImport(why, whyBytes);
|
||||
},
|
||||
#endif
|
||||
// T0 in one process: the client gets the store's own mapping, as monolith's
|
||||
// donation does; the client seeds it from its shadow once this returns.
|
||||
.DonateInProcess = [](MG_Pipe::MGPipeHandle res, Uint64 size) -> void* {
|
||||
auto* m = WireManager("donate");
|
||||
return m != nullptr ? m->DonateWireBuffer(res, size, nullptr) : nullptr;
|
||||
},
|
||||
};
|
||||
} // namespace
|
||||
|
||||
|
||||
@@ -2574,8 +2574,8 @@ if (MOBILEGL_BUILD_DISAGGREGATED)
|
||||
list(APPEND MGL_SPLIT_ARM_TEST_LISTS "MGL_SPLIT_ARM_TESTS_${MGL_SPLIT_ARM_INDEX}")
|
||||
math(EXPR MGL_SPLIT_ARM_INDEX "${MGL_SPLIT_ARM_INDEX} + 1")
|
||||
# P11 B2 (CONTRACT-P11 B2): THE SHARED-SEGMENT ARMS WITH MOBILEGL_IPC_ADOPT_TIER=0, which A1 made
|
||||
# die at the handshake and B2 turned into an ASK. A host has no AHardwareBuffer, so the server
|
||||
# refuses T0 by name at its first native bind, publishes no kCapAdoptT0, and the client falls
|
||||
# die at the handshake and B2 turned into an ASK. A host has no AHardwareBuffer (and Espryt cannot
|
||||
# hand an in-process client its stores), so the server refuses T0 by name at its first native bind, publishes no kCapAdoptT0, and the client falls
|
||||
# back to T2 with one named line at its first map_persistent: every case green, the arena
|
||||
# emulated (SplitLogPaths declares it), ASessionThatCannotRunT0FallsBackToT2OnceByName reading
|
||||
# the two lines. `.AdoptT0Disallowed.` is the same on the spawn arm with the server's allow switch
|
||||
@@ -2583,6 +2583,13 @@ if (MOBILEGL_BUILD_DISAGGREGATED)
|
||||
foreach(mglT0Arm IN ITEMS Split Spawn)
|
||||
string(TOUPPER "${mglT0Arm}" mglT0ArmUpper)
|
||||
string(TOLOWER "${mglT0Arm}" mglT0ArmLower)
|
||||
# Split is one process: Espryt cannot hand its client its stores, and an in-process session
|
||||
# never takes the AHardwareBuffer arm, so the refusal names that rather than the platform.
|
||||
if(mglT0Arm STREQUAL "Split")
|
||||
set(mglT0Fallback "in process")
|
||||
else()
|
||||
set(mglT0Fallback "no AHardwareBuffer")
|
||||
endif()
|
||||
gtest_discover_tests(MobileGLIntegrationTest
|
||||
TEST_PREFIX "DirectGLES.${mglT0Arm}.AdoptTier0."
|
||||
TEST_LIST "MGL_SPLIT_ARM_TESTS_${MGL_SPLIT_ARM_INDEX}"
|
||||
@@ -2591,7 +2598,7 @@ if (MOBILEGL_BUILD_DISAGGREGATED)
|
||||
PROPERTIES
|
||||
LABELS "integration-gpu\;integration-${mglT0ArmLower}"
|
||||
TIMEOUT ${MGL_ITEST_TIMEOUT}
|
||||
ENVIRONMENT "${MGL_ITEST_GLES_${mglT0ArmUpper}_ENVIRONMENT}\;MOBILEGL_IPC_ADOPT_TIER=0\;MGITEST_ADOPT_T0_FALLBACK=no AHardwareBuffer"
|
||||
ENVIRONMENT "${MGL_ITEST_GLES_${mglT0ArmUpper}_ENVIRONMENT}\;MOBILEGL_IPC_ADOPT_TIER=0\;MGITEST_ADOPT_T0_FALLBACK=${mglT0Fallback}"
|
||||
)
|
||||
list(APPEND MGL_SPLIT_ARM_TEST_LISTS "MGL_SPLIT_ARM_TESTS_${MGL_SPLIT_ARM_INDEX}")
|
||||
math(EXPR MGL_SPLIT_ARM_INDEX "${MGL_SPLIT_ARM_INDEX} + 1")
|
||||
@@ -4090,6 +4097,14 @@ if (MOBILEGL_BUILD_DISAGGREGATED)
|
||||
mgl_itest_magma_arm_shape(${mglItestArm})
|
||||
mgl_itest_join_environment(mglItestMagmaBandEnvironment
|
||||
${MGL_ITEST_MAGMA_BAND_FLAVOUR_ENV} ${mglItestArmTransportEnv})
|
||||
# Where the >= 16 MiB arena lands on this arm (P11 A2). Split is one process: T0 (the default
|
||||
# tier) is granted in process and the arena is the store Magma hands its client - adopted.
|
||||
# Across processes a host has no shareable memory, so the session runs T2 - emulated.
|
||||
if("${mglItestArm}" STREQUAL "Split")
|
||||
set(mglItestArenaArm adopted)
|
||||
else()
|
||||
set(mglItestArenaArm emulated)
|
||||
endif()
|
||||
foreach(bandCase RenderbufferReadOfExactlyTwoMiBIsBandedNotRefused
|
||||
TwoMiBPlusOneRowIsBanded
|
||||
AReadLargerThanTheWholeReplySegmentIsBanded
|
||||
@@ -4137,9 +4152,8 @@ if (MOBILEGL_BUILD_DISAGGREGATED)
|
||||
ENVIRONMENT "${mglItestMagmaBandEnvironment}\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p10-fence${mglItestArmLogTag}-${fenceCase}.log")
|
||||
endforeach()
|
||||
# P11 A2 (MG_Remote/CONTRACT-P11.md §2): the >= 16 MiB arena on Magma's three arms, in the
|
||||
# same flavour. Declared emulated: every split acquisition declines (the wire resource
|
||||
# table's MapPersistent returns null, VkBufferManager.cpp), so the tier case asserts T2
|
||||
# on every arm; DirectVulkan.ArenaArm. is its monolith (adopted) half.
|
||||
# same flavour, declared per arm (mglItestArenaArm above); DirectVulkan.ArenaArm. is its
|
||||
# monolith (adopted) half.
|
||||
foreach(arenaCase IN LISTS MGL_ITEST_ARENA_CASES)
|
||||
gtest_discover_tests(MobileGLIntegrationTest
|
||||
TEST_PREFIX "DirectVulkan.${mglItestArm}.Arena."
|
||||
@@ -4147,7 +4161,7 @@ if (MOBILEGL_BUILD_DISAGGREGATED)
|
||||
DISCOVERY_TIMEOUT 30
|
||||
PROPERTIES LABELS "${mglItestMagmaLabels}" TIMEOUT ${MGL_ITEST_TIMEOUT}
|
||||
${mglItestArmProperties}
|
||||
ENVIRONMENT "${mglItestMagmaBandEnvironment}\;MGITEST_PERSISTENT_MAP_ARM=emulated\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p11-arena${mglItestArmLogTag}-${arenaCase}.log")
|
||||
ENVIRONMENT "${mglItestMagmaBandEnvironment}\;MGITEST_PERSISTENT_MAP_ARM=${mglItestArenaArm}\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p11-arena${mglItestArmLogTag}-${arenaCase}.log")
|
||||
endforeach()
|
||||
# P11 B2 addendum: DeletedBoundBufferScenario on Magma's three arms, same flavour (the
|
||||
# Espryt arms come from mgl_itest_register_split_arms). Before the frontend fix Magma did
|
||||
@@ -4167,7 +4181,7 @@ if (MOBILEGL_BUILD_DISAGGREGATED)
|
||||
endforeach()
|
||||
# ...and the one-process arena pair (the Espryt half is beside mgl_itest_register_split_arms).
|
||||
string(REPLACE "\\;" ";" mglItestPairEnv "${mglItestMagmaBandEnvironment}")
|
||||
list(APPEND mglItestPairEnv "MGITEST_PERSISTENT_MAP_ARM=emulated"
|
||||
list(APPEND mglItestPairEnv "MGITEST_PERSISTENT_MAP_ARM=${mglItestArenaArm}"
|
||||
"MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p11-arena-pair${mglItestArmLogTag}.log")
|
||||
string(REPLACE "\\;" ";" mglItestPairLabels "${mglItestMagmaLabels}")
|
||||
set(mglItestPairName "DirectVulkan.${mglItestArm}.Arena.LargeArenaAdoptionScenario.GpuWriteThenTheTierCaseInOneProcess")
|
||||
@@ -4181,13 +4195,17 @@ if (MOBILEGL_BUILD_DISAGGREGATED)
|
||||
${mglItestArmProperties})
|
||||
# P11 A1 + B2 (CONTRACT-P11 §1, B2): MOBILEGL_IPC_ADOPT_TIER=0 on every arm, one case set.
|
||||
# On tcp a stream refuses the tier by name at the client's handshake (A1, the stream case
|
||||
# reads the line); over shared segments (split, spawn) the knob ASKS T0, a host has no
|
||||
# AHardwareBuffer, and the session falls back to T2 by name (B2, the fallback case reads
|
||||
# both lines). Register only the assertion applicable to each transport; the
|
||||
# parity check names these two transport-specific exceptions explicitly.
|
||||
# reads the line); on spawn the knob ASKS T0, a host has no AHardwareBuffer, and the session
|
||||
# falls back to T2 by name (B2, the fallback case reads both lines); on split the client is
|
||||
# the server's process and T0 is granted in process (the tier case reads `adopted`).
|
||||
# Register only the assertion applicable to each transport; the parity check names these
|
||||
# transport-specific exceptions explicitly.
|
||||
if(mglItestArm STREQUAL "Tcp")
|
||||
set(mglItestAdoptTier0Env "MGITEST_ADOPT_TIER_REFUSAL=T0")
|
||||
set(mglItestAdoptTier0Case AStreamSessionRefusesTheAdoptTierOnceByName)
|
||||
elseif(mglItestArm STREQUAL "Split")
|
||||
set(mglItestAdoptTier0Env "MOBILEGL_IPC_ALLOW_ADOPT_T0=1")
|
||||
set(mglItestAdoptTier0Case)
|
||||
else()
|
||||
set(mglItestAdoptTier0Env "MGITEST_ADOPT_T0_FALLBACK=no AHardwareBuffer")
|
||||
set(mglItestAdoptTier0Case ASessionThatCannotRunT0FallsBackToT2OnceByName)
|
||||
@@ -4199,7 +4217,7 @@ if (MOBILEGL_BUILD_DISAGGREGATED)
|
||||
DISCOVERY_TIMEOUT 30
|
||||
PROPERTIES LABELS "${mglItestMagmaLabels}" TIMEOUT ${MGL_ITEST_TIMEOUT}
|
||||
${mglItestArmProperties}
|
||||
ENVIRONMENT "${mglItestMagmaBandEnvironment}\;MOBILEGL_IPC_ADOPT_TIER=0\;${mglItestAdoptTier0Env}\;MGITEST_PERSISTENT_MAP_ARM=emulated\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p11-adopt-tier0${mglItestArmLogTag}-${arenaCase}.log")
|
||||
ENVIRONMENT "${mglItestMagmaBandEnvironment}\;MOBILEGL_IPC_ADOPT_TIER=0\;${mglItestAdoptTier0Env}\;MGITEST_PERSISTENT_MAP_ARM=${mglItestArenaArm}\;MOBILEGL_LOG_FILE_PATH=${CMAKE_CURRENT_BINARY_DIR}/p11-adopt-tier0${mglItestArmLogTag}-${arenaCase}.log")
|
||||
endforeach()
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
@@ -224,7 +224,9 @@ def specialized_required():
|
||||
}
|
||||
for backend in ("DirectGLES", "DirectVulkan"):
|
||||
names.add(f"{backend}.Tcp.AdoptTier0.LargeArenaAdoptionScenario.AStreamSessionRefusesTheAdoptTierOnceByName")
|
||||
for arm in ("Split", "Spawn"):
|
||||
# Magma's split arm is granted T0 in process (its tier case reads `adopted`), so only Espryt,
|
||||
# which cannot hand an in-process client its stores, falls back there.
|
||||
for arm in (("Split", "Spawn") if backend == "DirectGLES" else ("Spawn",)):
|
||||
names.add(f"{backend}.{arm}.AdoptTier0.LargeArenaAdoptionScenario.ASessionThatCannotRunT0FallsBackToT2OnceByName")
|
||||
for arm in ("Split", "Spawn"):
|
||||
for case in ("Rgb16fDrawnBaseWithARefusedReadbackDeclinesByName",
|
||||
|
||||
@@ -100,7 +100,8 @@ void main() { word = 0xC0FFEEu; }
|
||||
// which also set MOBILEGL_IPC_ADOPT_TIER=0; a harness marker, never read by the library.
|
||||
constexpr const char* kAdoptTierRefusalMarker = "MGITEST_ADOPT_TIER_REFUSAL";
|
||||
// P11 B2: set by the shared-segment `.AdoptTier0.` / `.AdoptT0Disallowed.` entries to the reason word the
|
||||
// server gives for not granting T0 ("no AHardwareBuffer" on a host, "disallowed" with its allow switch off).
|
||||
// server gives for not granting T0 ("no AHardwareBuffer" on a host, "in process" where the backend cannot hand an
|
||||
// in-process client its stores, "disallowed" with its allow switch off).
|
||||
constexpr const char* kAdoptT0FallbackMarker = "MGITEST_ADOPT_T0_FALLBACK";
|
||||
// Draws issued against the arena inside the counted window. One definition, many draws:
|
||||
// "one per definition" (1) and "one per draw" (kDrawsInTheWindow) have to be different
|
||||
|
||||
@@ -157,6 +157,11 @@ namespace MobileGL::MG_Pipe {
|
||||
// CapsSnapshot Accept sends (no context exists yet); set in the one the first native
|
||||
// bind republishes. The client adopts through T0 only while it reads this bit.
|
||||
kCapAdoptT0 = 1ull << 11,
|
||||
// T0 IN ONE PROCESS. Set with kCapAdoptT0 when the client is this process (inproc) and the
|
||||
// backend can donate a store: map_persistent's OK answer carries the address of the
|
||||
// server's own mapped store, and the client allocates no AHardwareBuffer - the store is
|
||||
// the one memory both sides already share, as monolith's donation is.
|
||||
kCapAdoptInProcess = 1ull << 12,
|
||||
};
|
||||
|
||||
// Readiness is supplied independently by each backend's integration gate.
|
||||
|
||||
@@ -2860,6 +2860,23 @@ namespace MobileGL::MG_Pipe {
|
||||
if (g_resourceOps == nullptr || g_resourceOps->ImportExternal == nullptr) return false;
|
||||
return g_resourceOps->ImportExternal(handle.Handle, ahb, size);
|
||||
}
|
||||
|
||||
void* MGPipeApplyDonateInProcess(const MGPHandleOnly& handle) {
|
||||
if (static_cast<MGPipeKind>(handle.Kind) != MGPipeKind::Buffer) {
|
||||
MGP_TRIP_WIRE_REPORT("MGPipe: " MGP_TRIP_WIRE_TAG("ProtocolCorruption")
|
||||
" donate_in_process {slot=%u, gen=%u}: a T0 store is the buffer family's "
|
||||
"and the handle names another kind (%u)",
|
||||
handle.Handle.Slot, handle.Handle.Gen, handle.Kind);
|
||||
return nullptr;
|
||||
}
|
||||
MGPipeResourceRecord* record = ResolveResource("donate_in_process", handle.Handle);
|
||||
if (record == nullptr || record->Desc.Width == 0) return nullptr;
|
||||
// adopt_external's pin, for adopt_external's reason: the client writes the donated store
|
||||
// directly, as a monolith client writes its adopted map.
|
||||
PinNoLiveHostWrites(*record, handle.Handle, "donate_in_process");
|
||||
if (g_resourceOps == nullptr || g_resourceOps->DonateInProcess == nullptr) return nullptr;
|
||||
return g_resourceOps->DonateInProcess(handle.Handle, record->Desc.Width);
|
||||
}
|
||||
#endif
|
||||
|
||||
// ================================================================================
|
||||
|
||||
@@ -142,6 +142,10 @@ namespace MobileGL::MG_Pipe {
|
||||
// pattern of its own, and read that back through the held pointer (and through the
|
||||
// backend's own host map of the import). False with the reason in `why`.
|
||||
Bool (*SelfTestExternal)(char* why, Uint64 whyBytes);
|
||||
// T0 for a client in this process: the store's own host-visible coherent mapping (the
|
||||
// pointer a monolith map_persistent donates), handed to the client as its persistent map.
|
||||
// The store takes T0's semantics from here on. Null = not donated: the store runs T2.
|
||||
void* (*DonateInProcess)(MGPipeHandle res, Uint64 size);
|
||||
};
|
||||
|
||||
// Install / read the table. A null argument uninstalls, which is what a backend does at
|
||||
@@ -1541,6 +1545,9 @@ namespace MobileGL::MG_Pipe {
|
||||
// without the member, declines (false); the codec answers DECLINED and the store runs T2.
|
||||
// MapPersistentRoundtrips is not moved: under split the server never counted map_persistent.
|
||||
Bool MGPipeApplyAdoptExternal(const MGPHandleOnly& handle, void* ahb, Uint64 size);
|
||||
// T0 in one process: buffer `handle`'s store, donated through the backend's DonateInProcess.
|
||||
// Null declines (not a live buffer, or a backend without the member); the store runs T2.
|
||||
void* MGPipeApplyDonateInProcess(const MGPHandleOnly& handle);
|
||||
|
||||
// ---------------------------------------------------------------------------------
|
||||
// P3a: the five vertex-input entry points (D-G, D-H, D-I)
|
||||
|
||||
@@ -75,7 +75,10 @@ namespace MobileGL::MG_Pipe {
|
||||
// handled it and `status` is the answer (OK: the client's AHardwareBuffer is now the
|
||||
// store; DECLINED: the store runs T2); false = not a T0 session, and the codec gives
|
||||
// today's constant decline. `seq` is the record's, which is what the store's Offer names.
|
||||
virtual Bool OnMapPersistent(const MGPHandleOnly&, Uint64 /*seq*/, Int32& /*status*/) {
|
||||
// `inProcessBase`: a client in this process gets the donated store's address in the OK
|
||||
// answer (kCapAdoptInProcess); 0 = the answer carries no bytes (the AHardwareBuffer arm).
|
||||
virtual Bool OnMapPersistent(const MGPHandleOnly&, Uint64 /*seq*/, Int32& /*status*/,
|
||||
Uint64& /*inProcessBase*/) {
|
||||
return false;
|
||||
}
|
||||
virtual Bool OnFenceCreate(const MGPHandleOnly&) { return false; }
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
@@ -3011,11 +3012,26 @@ namespace MobileGL::MG_Remote::Client {
|
||||
Bool ClientSession::AdoptT0() {
|
||||
if (!m_adoptT0Asked) return false;
|
||||
if (m_adoptT0Decision >= 0) return m_adoptT0Decision == 1;
|
||||
if (Caps().HasCap(MG_Pipe::kCapAdoptT0)) {
|
||||
const Bool peerIsThisProcess = m_transport != nullptr &&
|
||||
Transport::AdoptT0::PeerIsThisProcess(m_transport->Role() == Transport::TransportRole::InProcess);
|
||||
if (Caps().HasCap(MG_Pipe::kCapAdoptT0) && peerIsThisProcess &&
|
||||
!Caps().HasCap(MG_Pipe::kCapAdoptInProcess)) {
|
||||
// A server in this process offered the AHardwareBuffer arm. The stores are this
|
||||
// process's memory already; an in-process session never allocates one.
|
||||
m_adoptT0Decision = 0;
|
||||
++m_t0Fallbacks;
|
||||
MGLOG_W("MG_Remote client: Refuse{AdoptT0Unavailable, \"in process\"} - the server is this process "
|
||||
"and published kCapAdoptT0 without kCapAdoptInProcess; the session runs T2");
|
||||
} else if (Caps().HasCap(MG_Pipe::kCapAdoptT0)) {
|
||||
m_adoptT0Decision = 1;
|
||||
MGLOG_I("MG_Remote client: T0 - the server published kCapAdoptT0 (its POST self-test of the "
|
||||
"sustained-lock pattern passed), so this session adopts persistent stores as "
|
||||
"AHardwareBuffers the server imports");
|
||||
if (Caps().HasCap(MG_Pipe::kCapAdoptInProcess)) {
|
||||
MGLOG_I("MG_Remote client: T0 in process - the server is this process, so this session's "
|
||||
"persistent maps are the server's own store mappings (no AHardwareBuffer)");
|
||||
} else {
|
||||
MGLOG_I("MG_Remote client: T0 - the server published kCapAdoptT0 (its POST self-test of the "
|
||||
"sustained-lock pattern passed), so this session adopts persistent stores as "
|
||||
"AHardwareBuffers the server imports");
|
||||
}
|
||||
} else {
|
||||
m_adoptT0Decision = 0;
|
||||
++m_t0Fallbacks;
|
||||
@@ -3036,6 +3052,7 @@ namespace MobileGL::MG_Remote::Client {
|
||||
void* ClientSession::AdoptPersistentT0(const MG_Pipe::MGPHandleOnly& handle, Uint64 size,
|
||||
const void* seed) {
|
||||
using namespace Transport::AdoptT0;
|
||||
if (Caps().HasCap(MG_Pipe::kCapAdoptInProcess)) return AdoptPersistentInProcess(handle, size, seed);
|
||||
// Everything that can fail runs BEFORE the record is encoded, so the record never waits on
|
||||
// a step that has not happened: either the Offer carries the handle or it says it does not.
|
||||
HeldStore store;
|
||||
@@ -3141,6 +3158,50 @@ namespace MobileGL::MG_Remote::Client {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// T0 IN ONE PROCESS (kCapAdoptInProcess): the OK answer carries the address of the server's own
|
||||
// mapping of the store, which becomes this buffer's persistent map - the memory both sides
|
||||
// already share, as monolith's donation is. The server has drained the GPU's use of the store
|
||||
// before answering and applies nothing of this client's until this call returns, so the
|
||||
// shadow is copied in here, by the side that owns it.
|
||||
void* ClientSession::AdoptPersistentInProcess(const MG_Pipe::MGPHandleOnly& handle, Uint64 size,
|
||||
const void* seed) {
|
||||
Uint64 base = 0;
|
||||
Uint64 returned = 0;
|
||||
Int32 status = Wire::ReplySink::kStatusError;
|
||||
EmitAndWait(MG_Pipe::MGPWireOp::MapPersistent, &handle, sizeof(handle), nullptr, 0, &base, sizeof(base),
|
||||
&status, &returned);
|
||||
if (status == Wire::ReplySink::kStatusError) {
|
||||
SessionFail(MGFatalFamily::ReplyError,
|
||||
"MGPipe: Fatal{ReplyError, \"map_persistent\"} - the in-process T0 row answered ERROR, "
|
||||
"which is not an acceptance answer");
|
||||
}
|
||||
if (status != Wire::ReplySink::kStatusOk) {
|
||||
++m_t0Declined;
|
||||
return nullptr;
|
||||
}
|
||||
if (returned != sizeof(base) || base == 0) {
|
||||
SessionFail(MGFatalFamily::UnexpectedMapAccept,
|
||||
"MGPipe: Fatal{UnexpectedMapAccept, \"map_persistent\"} - the server accepted an "
|
||||
"in-process T0 store without the store's address");
|
||||
}
|
||||
void* ptr = reinterpret_cast<void*>(static_cast<std::uintptr_t>(base));
|
||||
if (seed != nullptr) std::memcpy(ptr, seed, static_cast<SizeT>(size));
|
||||
++m_t0Imported;
|
||||
m_t0Bytes += size;
|
||||
// No AHardwareBuffer to hold: the entry only marks the handle T0 (its release is a no-op).
|
||||
const Uint64 key = T0Key(handle.Handle);
|
||||
if (auto it = m_t0Stores.find(key); it != m_t0Stores.end()) {
|
||||
Transport::AdoptT0::ReleaseHeld(it->second);
|
||||
++m_t0Released;
|
||||
}
|
||||
m_t0Stores[key] = Transport::AdoptT0::HeldStore{nullptr, ptr, size};
|
||||
MGLOG_D("MG_Remote client: T0 store {slot=%u, gen=%u} donated in process - %llu bytes; session total "
|
||||
"%llu stores / %llu bytes",
|
||||
handle.Handle.Slot, handle.Handle.Gen, static_cast<unsigned long long>(size),
|
||||
static_cast<unsigned long long>(m_t0Imported), static_cast<unsigned long long>(m_t0Bytes));
|
||||
return ptr;
|
||||
}
|
||||
|
||||
Bool ClientSession::EmitSharedImage(const MG_Pipe::MGPSharedImageOp& op, int sendFd,
|
||||
MG_Pipe::MGPSharedImageReply* reply, int* receivedFd) {
|
||||
if (receivedFd != nullptr) *receivedFd = -1;
|
||||
|
||||
@@ -293,6 +293,7 @@ namespace MobileGL::MG_Remote::Client {
|
||||
// server's inbox relies on), and wait for the answer. The held pointer on OK - the caller
|
||||
// adopts it as the store's mapping - or null on DECLINED (the store stays T2).
|
||||
void* AdoptPersistentT0(const MG_Pipe::MGPHandleOnly& handle, Uint64 size, const void* seed);
|
||||
void* AdoptPersistentInProcess(const MG_Pipe::MGPHandleOnly& handle, Uint64 size, const void* seed);
|
||||
// shared_image (docs/Disaggregated/notes/anland/plan-ahb-dmabuf.md): emits `op` and waits
|
||||
// for its answer. `sendFd` >= 0 (an Import) is queued on the aux socket for the record's
|
||||
// seq BEFORE the record is published; `receivedFd` non-null (an Allocate) takes the
|
||||
|
||||
@@ -116,9 +116,10 @@ namespace MobileGL::MG_Remote::Server {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
Bool ServerVerbSink::OnMapPersistent(const MG_Pipe::MGPHandleOnly& handle, Uint64 seq, Int32& status) {
|
||||
Bool ServerVerbSink::OnMapPersistent(const MG_Pipe::MGPHandleOnly& handle, Uint64 seq, Int32& status,
|
||||
Uint64& inProcessBase) {
|
||||
ServerSession* session = ServerSession::Active();
|
||||
return session != nullptr && session->AdoptStoreT0(handle, seq, status);
|
||||
return session != nullptr && session->AdoptStoreT0(handle, seq, status, inProcessBase);
|
||||
}
|
||||
|
||||
Bool ServerVerbSink::OnFenceCreate(const MG_Pipe::MGPHandleOnly& desc) {
|
||||
|
||||
@@ -156,7 +156,8 @@ namespace MobileGL::MG_Remote::Server {
|
||||
// queries, present, the T0 map, the control records and shared images.
|
||||
|
||||
// P11 B2: T0's map_persistent - ServerSession::AdoptStoreT0 (it owns the aux socket).
|
||||
Bool OnMapPersistent(const MG_Pipe::MGPHandleOnly& handle, Uint64 seq, Int32& status) override;
|
||||
Bool OnMapPersistent(const MG_Pipe::MGPHandleOnly& handle, Uint64 seq, Int32& status,
|
||||
Uint64& inProcessBase) override;
|
||||
Bool OnFenceCreate(const MG_Pipe::MGPHandleOnly&) override;
|
||||
Bool OnFenceDestroy(const MG_Pipe::MGPHandleOnly&) override;
|
||||
Bool OnFenceStatus(const MG_Pipe::MGPHandleOnly&, Uint32&) override;
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <atomic>
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <thread>
|
||||
@@ -726,6 +727,7 @@ namespace MobileGL::MG_Remote::Server {
|
||||
m_adoptT0Quiet = m_adoptAsk == Transport::kAdoptAskT0Default;
|
||||
m_adoptT0Settled = false;
|
||||
m_adoptT0 = false;
|
||||
m_adoptT0InProcess = false;
|
||||
m_t0Stores = m_t0Bytes = m_t0Declined = m_t0Refusals = 0;
|
||||
m_adoptInbox.Clear();
|
||||
m_adoptInbox.Attach(&transport);
|
||||
@@ -966,6 +968,7 @@ namespace MobileGL::MG_Remote::Server {
|
||||
// P11 B2: this session's T0 grant, never part of the process-wide m_capBits - it is a
|
||||
// statement about THIS client's ask and this server's self-test.
|
||||
if (m_adoptT0) callMask |= static_cast<Uint64>(MG_Pipe::kCapAdoptT0);
|
||||
if (m_adoptT0InProcess) callMask |= static_cast<Uint64>(MG_Pipe::kCapAdoptInProcess);
|
||||
const auto timerSupported = m_backend->GetBackendFunctions().GL.IsTimerQuerySupported;
|
||||
if ((callMask & MG_Pipe::kCapTimerQuery) && (!timerSupported || !timerSupported()))
|
||||
callMask &= ~static_cast<Uint64>(MG_Pipe::kCapTimerQuery);
|
||||
@@ -1113,7 +1116,7 @@ namespace MobileGL::MG_Remote::Server {
|
||||
m_adoptInbox.Clear();
|
||||
m_adoptInbox.Attach(nullptr);
|
||||
m_sharedImages.Clear();
|
||||
m_adoptT0Asked = m_adoptT0Settled = m_adoptT0 = m_adoptT0Quiet = false;
|
||||
m_adoptT0Asked = m_adoptT0Settled = m_adoptT0 = m_adoptT0Quiet = m_adoptT0InProcess = false;
|
||||
m_adoptAsk = 2;
|
||||
m_consumer.Detach();
|
||||
*m_commands = Transport::RingConsumer();
|
||||
@@ -1153,7 +1156,27 @@ namespace MobileGL::MG_Remote::Server {
|
||||
const char* backendName = m_backend == nullptr ? "server"
|
||||
: m_backend->GetBackendType() == BackendType::DirectVulkan ? "Magma" : "Espryt";
|
||||
std::string why;
|
||||
if (MG_Config::Ipc.AllowAdoptT0 == 0) {
|
||||
// A CLIENT IN THIS PROCESS SHARES THE SERVER'S MEMORY ALREADY: its persistent maps are the
|
||||
// stores' own mappings, donated as monolith donates them, and no AHardwareBuffer, import
|
||||
// or POST is involved. A backend that cannot donate leaves the session on T2 - an
|
||||
// in-process session never takes the AHardwareBuffer arm below.
|
||||
const Bool inProcess = m_transport != nullptr &&
|
||||
Transport::AdoptT0::PeerIsThisProcess(m_transport->Role() == Transport::TransportRole::InProcess);
|
||||
if (inProcess) {
|
||||
const MG_Pipe::MGPipeResourceOps* ops = MG_Pipe::MGPipeGetResourceOps();
|
||||
if (MG_Config::Ipc.AllowAdoptT0 == 0) {
|
||||
why = "this server's MOBILEGL_IPC_ALLOW_ADOPT_T0=0 disallows it";
|
||||
} else if (ops == nullptr || ops->DonateInProcess == nullptr) {
|
||||
why = std::string("the ") + backendName + " backend cannot hand its stores to a client in this process";
|
||||
} else {
|
||||
m_adoptT0 = true;
|
||||
m_adoptT0InProcess = true;
|
||||
MGLOG_I("MG_Remote server: T0 granted in process - the client is this process, so each "
|
||||
"map_persistent hands it the %s store's own mapping (no AHardwareBuffer)",
|
||||
backendName);
|
||||
return;
|
||||
}
|
||||
} else if (MG_Config::Ipc.AllowAdoptT0 == 0) {
|
||||
why = "this server's MOBILEGL_IPC_ALLOW_ADOPT_T0=0 disallows it";
|
||||
} else if (!Transport::AdoptT0::PlatformHasAhb()) {
|
||||
why = "no AHardwareBuffer on this platform";
|
||||
@@ -1182,7 +1205,9 @@ namespace MobileGL::MG_Remote::Server {
|
||||
if (!why.empty()) {
|
||||
++m_t0Refusals;
|
||||
const char* word = MG_Config::Ipc.AllowAdoptT0 == 0 ? "disallowed"
|
||||
: !Transport::AdoptT0::PlatformHasAhb() ? "no AHardwareBuffer" : "POST";
|
||||
: inProcess ? "in process"
|
||||
: !Transport::AdoptT0::PlatformHasAhb() ? "no AHardwareBuffer"
|
||||
: "POST";
|
||||
if (m_adoptT0Quiet) {
|
||||
// THE DEFAULT ASK (MOBILEGL_IPC_ADOPT_TIER unset) FALLS BACK QUIETLY: every host
|
||||
// session would otherwise carry this line. Counted (T0Refusals, the totals line).
|
||||
@@ -1202,9 +1227,29 @@ namespace MobileGL::MG_Remote::Server {
|
||||
backendName);
|
||||
}
|
||||
|
||||
Bool ServerSession::AdoptStoreT0(const MG_Pipe::MGPHandleOnly& handle, Uint64 seq, Int32& status) {
|
||||
Bool ServerSession::AdoptStoreT0(const MG_Pipe::MGPHandleOnly& handle, Uint64 seq, Int32& status,
|
||||
Uint64& inProcessBase) {
|
||||
if (!m_adoptT0) return false;
|
||||
status = Wire::ReplySink::kStatusDeclined;
|
||||
inProcessBase = 0;
|
||||
if (m_adoptT0InProcess) {
|
||||
void* base = MG_Pipe::MGPipeApplyDonateInProcess(handle);
|
||||
if (base == nullptr) {
|
||||
++m_t0Declined;
|
||||
MGLOG_W("MG_Remote server: T0 map_persistent {slot=%u, gen=%u} (record %llu) DECLINED - the "
|
||||
"backend did not donate the store (its log names why); the store runs T2",
|
||||
handle.Handle.Slot, handle.Handle.Gen, static_cast<unsigned long long>(seq));
|
||||
return true;
|
||||
}
|
||||
inProcessBase = static_cast<Uint64>(reinterpret_cast<std::uintptr_t>(base));
|
||||
++m_t0Stores;
|
||||
status = Wire::ReplySink::kStatusOk;
|
||||
MGLOG_D("MG_Remote server: T0 donated store {slot=%u, gen=%u} in process (record %llu); session "
|
||||
"total %llu stores",
|
||||
handle.Handle.Slot, handle.Handle.Gen, static_cast<unsigned long long>(seq),
|
||||
static_cast<unsigned long long>(m_t0Stores));
|
||||
return true;
|
||||
}
|
||||
int hop = -1;
|
||||
Transport::AdoptT0::Offer offer{};
|
||||
std::string why;
|
||||
|
||||
@@ -320,7 +320,12 @@ namespace MobileGL::MG_Remote::Server {
|
||||
// hang), read the AHB off its hop, and import it as `handle`'s store. False = this session is
|
||||
// not T0 (the codec's T2 decline answers); true = handled and `status` is the answer (OK, or
|
||||
// DECLINED with a named line).
|
||||
Bool AdoptStoreT0(const MG_Pipe::MGPHandleOnly& handle, Uint64 seq, Int32& status);
|
||||
// A client in this process (inproc) takes no AHardwareBuffer: the backend donates the
|
||||
// store's own mapping and `inProcessBase` carries its address in the OK answer.
|
||||
Bool AdoptStoreT0(const MG_Pipe::MGPHandleOnly& handle, Uint64 seq, Int32& status,
|
||||
Uint64& inProcessBase);
|
||||
// T0 granted for a client in this process: map_persistent donates (kCapAdoptInProcess).
|
||||
Bool AdoptT0InProcess() const { return m_adoptT0InProcess; }
|
||||
// The arm's proof for this session (B2): stores imported and their bytes, declined.
|
||||
Uint64 T0StoresImported() const { return m_t0Stores; }
|
||||
Uint64 T0BytesImported() const { return m_t0Bytes; }
|
||||
@@ -426,6 +431,7 @@ namespace MobileGL::MG_Remote::Server {
|
||||
Uint8 m_adoptAsk = 2;
|
||||
Bool m_adoptT0Settled = false;
|
||||
Bool m_adoptT0 = false;
|
||||
Bool m_adoptT0InProcess = false;
|
||||
AdoptInbox m_adoptInbox;
|
||||
SharedImages::SessionHolder m_sharedImages;
|
||||
Uint64 m_t0Stores = 0;
|
||||
|
||||
@@ -184,6 +184,18 @@ namespace MobileGL::MG_Remote::Transport::AdoptT0 {
|
||||
return test != nullptr ? test->HasAhb() : RealHasAhb();
|
||||
}
|
||||
|
||||
namespace {
|
||||
std::atomic<bool> g_peerInAnotherProcessForTest{false};
|
||||
} // namespace
|
||||
|
||||
bool PeerIsThisProcess(bool inProcessTransport) {
|
||||
return inProcessTransport && !g_peerInAnotherProcessForTest.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
void SetPeerInAnotherProcessForTest(bool asIfCrossProcess) {
|
||||
g_peerInAnotherProcessForTest.store(asIfCrossProcess, std::memory_order_release);
|
||||
}
|
||||
|
||||
bool AllocateHeld(std::uint64_t size, const void* seed, HeldStore& out, std::string& why) {
|
||||
const PlatformForTest* test = g_testPlatform.load(std::memory_order_acquire);
|
||||
return test != nullptr ? test->AllocateHeld(size, seed, out, why) : RealAllocateHeld(size, seed, out, why);
|
||||
|
||||
@@ -56,6 +56,14 @@ namespace MobileGL::MG_Remote::Transport::AdoptT0 {
|
||||
// True where AHardwareBuffer exists (Android). Everywhere else T0 is never granted.
|
||||
bool PlatformHasAhb();
|
||||
|
||||
// True when a session's peer is this very process (an in-process transport). Such a session
|
||||
// never takes this header's arm: its stores are this process's memory already, so T0 there is
|
||||
// the backend handing over its own store (kCapAdoptInProcess) or nothing (T2).
|
||||
bool PeerIsThisProcess(bool inProcessTransport);
|
||||
// Test seam: the protocol tests run over an in-process transport and stand for a peer in
|
||||
// another process while this is set.
|
||||
void SetPeerInAnotherProcessForTest(bool asIfCrossProcess);
|
||||
|
||||
// ---- client half ----------------------------------------------------------------------
|
||||
|
||||
// A store the client allocated. `ahb` is an AHardwareBuffer*, `ptr` its CPU lock, held until
|
||||
|
||||
@@ -1852,9 +1852,11 @@ namespace MobileGL::MG_Remote::Wire {
|
||||
// it (Server/AdoptInbox.h says why that wait cannot hang the apply thread); it answers
|
||||
// for itself or says "not T0", and everything below is the T2 arm, unchanged.
|
||||
Int32 t0Status = ReplySink::kStatusDeclined;
|
||||
Uint64 inProcessBase = 0;
|
||||
if (m_verbs != nullptr &&
|
||||
m_verbs->OnMapPersistent(*static_cast<const MGPHandleOnly*>(payload), seq, t0Status)) {
|
||||
PostReply(op, seq, t0Status, nullptr, 0);
|
||||
m_verbs->OnMapPersistent(*static_cast<const MGPHandleOnly*>(payload), seq, t0Status, inProcessBase)) {
|
||||
if (inProcessBase != 0) PostReply(op, seq, t0Status, &inProcessBase, sizeof(inProcessBase));
|
||||
else PostReply(op, seq, t0Status, nullptr, 0);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,9 @@ void FakeDrop(FakeAhb* f) {
|
||||
}
|
||||
}
|
||||
bool FakeHasAhb() { return true; }
|
||||
Uint64 g_fakeAllocations = 0;
|
||||
bool FakeAllocate(std::uint64_t size, const void* seed, T0::HeldStore& out, std::string& why) {
|
||||
++g_fakeAllocations;
|
||||
auto* f = new FakeAhb;
|
||||
f->fd = static_cast<int>(::syscall(SYS_memfd_create, "t0-fake", 1u /*MFD_CLOEXEC*/));
|
||||
if (f->fd < 0 || ::ftruncate(f->fd, static_cast<off_t>(size)) != 0) {
|
||||
@@ -144,6 +146,20 @@ MGPipeResourceOps MakeFakeOps() {
|
||||
}
|
||||
MGPipeResourceOps g_fakeOps = MakeFakeOps();
|
||||
|
||||
// ---- the stand-in donor: a backend that hands an in-process client its store's own mapping ---
|
||||
alignas(64) Uint8 g_donatedStore[kT0Bytes];
|
||||
Uint64 g_donations = 0;
|
||||
void* FakeDonateInProcess(MGPipeHandle res, Uint64 size) {
|
||||
++g_donations;
|
||||
return res.Slot == 5 && size == kT0Bytes ? g_donatedStore : nullptr;
|
||||
}
|
||||
MGPipeResourceOps MakeDonatingOps() {
|
||||
MGPipeResourceOps ops = MakeFakeOps();
|
||||
ops.DonateInProcess = &FakeDonateInProcess;
|
||||
return ops;
|
||||
}
|
||||
MGPipeResourceOps g_donatingOps = MakeDonatingOps();
|
||||
|
||||
// What the server's first native bind does for T0, on the apply thread like the real one: the
|
||||
// grant (and its POST), then the caps that bind republishes - which the client's make-current
|
||||
// adopts, as here.
|
||||
@@ -184,6 +200,7 @@ size_t CountOf(const std::string& log, const std::string& needle) {
|
||||
// The grant with the stand-ins, the store, and the map_persistent: returns the adopted pointer.
|
||||
void* AdoptOneStore(Uint8 salt, std::vector<Uint8>& seed) {
|
||||
MG_Config::Ipc.AdoptTier = 0;
|
||||
T0::SetPeerInAnotherProcessForTest(true);
|
||||
T0::SetPlatformForTest(&kFakePlatform);
|
||||
MG_Pipe::MGPipeSetResourceOps(&g_fakeOps);
|
||||
StartControlSession();
|
||||
@@ -223,6 +240,7 @@ TEST(RemoteClientAdoptT0, KnobZeroOverSharedSegmentsAsksT0AndFallsBackToT2ByName
|
||||
TEST(RemoteClientAdoptT0, KnobTwoIsTodaysT2WithNoT0Line) {
|
||||
const auto child = RunInChild([] {
|
||||
MG_Config::Ipc.AdoptTier = 2;
|
||||
T0::SetPeerInAnotherProcessForTest(true);
|
||||
T0::SetPlatformForTest(&kFakePlatform);
|
||||
MG_Pipe::MGPipeSetResourceOps(&g_fakeOps);
|
||||
StartControlSession();
|
||||
@@ -251,6 +269,7 @@ TEST(RemoteClientAdoptT0, KnobTwoIsTodaysT2WithNoT0Line) {
|
||||
TEST(RemoteClientAdoptT0, AnUnsetKnobFallsBackToT2QuietlyAndCountsIt) {
|
||||
const auto child = RunInChild([] {
|
||||
MG_Config::Ipc.AdoptTier = MG_Config::kAdoptTierUnset;
|
||||
T0::SetPeerInAnotherProcessForTest(true);
|
||||
MG_Pipe::MGPipeSetResourceOps(&g_fakeOps); // a backend WITH the members: the platform decides
|
||||
StartControlSession();
|
||||
if (!Srv::ServerSessionInstance().AdoptT0Asked()) ::_exit(210); // the Hello asked T0 by default
|
||||
@@ -292,6 +311,7 @@ TEST(RemoteClientAdoptT0, KnobOneIsRefusedByNameAndAsksT2) {
|
||||
TEST(RemoteClientAdoptT0, NoAhardwareBufferOnThisPlatformIsT2AndTwoNamedLines) {
|
||||
const auto child = RunInChild([] {
|
||||
MG_Config::Ipc.AdoptTier = 0;
|
||||
T0::SetPeerInAnotherProcessForTest(true);
|
||||
MG_Pipe::MGPipeSetResourceOps(&g_fakeOps); // a backend WITH the members: the platform decides
|
||||
StartControlSession();
|
||||
GrantAtFirstBind();
|
||||
@@ -383,4 +403,60 @@ TEST(RemoteClientAdoptT0, AnOfferThatNeverArrivesIsANamedDeclineNotAHang) {
|
||||
EXPECT_EQ(CountOf(child.Log, "Refuse{AdoptT0NoStore, \"record"), 1u) << child.Log;
|
||||
EXPECT_EQ(child.Log.find("Fatal{"), std::string::npos) << child.Log;
|
||||
}
|
||||
// T0 IN ONE PROCESS: the client is this process, so a backend that can donate hands it the store's
|
||||
// own mapping - no AHardwareBuffer is allocated, no Offer, no import, no POST - and the client's
|
||||
// pointer is the server's store, seeded with the shadow.
|
||||
// RED-ONCE (host): grant the AHardwareBuffer arm for an in-process client (drop the in-process
|
||||
// branch of ServerSession::SettleAdoptT0AtBind) -> the stand-in platform allocates, 223.
|
||||
TEST(RemoteClientAdoptT0, AnInProcessClientGetsTheServersStoreAndNoAhardwareBuffer) {
|
||||
const auto child = RunInChild([] {
|
||||
MG_Config::Ipc.AdoptTier = 0;
|
||||
T0::SetPlatformForTest(&kFakePlatform);
|
||||
MG_Pipe::MGPipeSetResourceOps(&g_donatingOps);
|
||||
StartControlSession();
|
||||
GrantAtFirstBind();
|
||||
if (!Srv::ServerSessionInstance().AdoptT0InProcess()) ::_exit(220);
|
||||
const auto seed = Pattern(kT0Bytes, 0x77);
|
||||
const MGPHandleOnly handle = MakeBufferStore(5, kT0Bytes);
|
||||
void* adopted = MGPipeRouteMapPersistent(handle, kT0Bytes, seed.data());
|
||||
if (adopted != g_donatedStore) ::_exit(221);
|
||||
if (std::memcmp(g_donatedStore, seed.data(), kT0Bytes) != 0) ::_exit(222);
|
||||
if (g_fakeAllocations != 0 || g_fakeImports.count != 0) ::_exit(223);
|
||||
if (g_donations != 1) ::_exit(224);
|
||||
if (Srv::ServerSessionInstance().T0StoresImported() != 1 || ClientSessionInstance().T0StoresImported() != 1)
|
||||
::_exit(225);
|
||||
ClientSessionInstance().Stop();
|
||||
});
|
||||
ExpectChildSuccess(child);
|
||||
EXPECT_NE(child.Log.find("T0 granted in process"), std::string::npos) << child.Log;
|
||||
EXPECT_EQ(child.Log.find("T0 POST"), std::string::npos) << child.Log;
|
||||
EXPECT_EQ(child.Log.find("Fatal{"), std::string::npos) << child.Log;
|
||||
}
|
||||
// T0 IN ONE PROCESS NEVER TAKES THE AHardwareBuffer ARM: a backend that cannot hand an in-process
|
||||
// client its store leaves the session on T2 by name - no allocation, no Offer, no import, no POST -
|
||||
// even where the transfer arm would work (the stand-in platform and importer are installed).
|
||||
// RED-ONCE (host): let SettleAdoptT0AtBind fall through to the AHardwareBuffer arm for an in-process
|
||||
// client -> the stand-in POST passes and the session is granted T0, 230.
|
||||
TEST(RemoteClientAdoptT0, AnInProcessClientWhoseBackendCannotDonateRunsT2AndNeverTheTransferArm) {
|
||||
const auto child = RunInChild([] {
|
||||
MG_Config::Ipc.AdoptTier = 0;
|
||||
T0::SetPlatformForTest(&kFakePlatform);
|
||||
MG_Pipe::MGPipeSetResourceOps(&g_fakeOps); // the transfer arm's members, no DonateInProcess
|
||||
StartControlSession();
|
||||
GrantAtFirstBind();
|
||||
if (Srv::ServerSessionInstance().AdoptT0()) ::_exit(230);
|
||||
if (Srv::ServerSessionInstance().T0Refusals() != 1) ::_exit(231);
|
||||
const auto seed = Pattern(kT0Bytes, 0x88);
|
||||
const MGPHandleOnly handle = MakeBufferStore(5, kT0Bytes);
|
||||
if (MGPipeRouteMapPersistent(handle, kT0Bytes, seed.data()) != nullptr) ::_exit(232);
|
||||
if (g_fakeAllocations != 0 || g_fakeImports.count != 0) ::_exit(233);
|
||||
if (ClientSessionInstance().AdoptT0()) ::_exit(234);
|
||||
ClientSessionInstance().Stop();
|
||||
});
|
||||
ExpectChildSuccess(child);
|
||||
EXPECT_EQ(CountOf(child.Log, "Refuse{AdoptT0Unavailable, \"in process\"}"), 1u) << child.Log;
|
||||
EXPECT_EQ(child.Log.find("T0 POST"), std::string::npos) << child.Log;
|
||||
EXPECT_EQ(child.Log.find("T0 granted"), std::string::npos) << child.Log;
|
||||
EXPECT_EQ(child.Log.find("Fatal{"), std::string::npos) << child.Log;
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user