[Fix] (Pipe): the device-loss debug knob re-arms when set to a new value.

This commit is contained in:
2026-10-02 23:59:55 -04:00
parent b06c2a9bf2
commit c60fcc17c5
3 changed files with 48 additions and 3 deletions
+14 -1
View File
@@ -186,9 +186,22 @@ namespace MobileGL::MG_Pipe {
}
}
#endif
if (at <= 0) return false;
static std::atomic<long> armedAt{0};
static std::atomic<long> seen{0};
static std::atomic<bool> fired{false};
if (at <= 0) {
// Unset (or 0) disarms: the same value set again counts afresh.
if (armedAt.load(std::memory_order_relaxed) != 0) armedAt.store(0, std::memory_order_relaxed);
return false;
}
// A NEW VALUE RE-ARMS. One server process outlives many sessions, so a second loss is
// injected by setting the knob again (another value, or 0 and then a value): the count
// restarts from the checks made after it was seen.
if (long previous = armedAt.load(std::memory_order_acquire);
previous != at && armedAt.compare_exchange_strong(previous, at, std::memory_order_acq_rel)) {
seen.store(0, std::memory_order_release);
fired.store(false, std::memory_order_release);
}
if (fired.load(std::memory_order_acquire)) return false;
if (seen.fetch_add(1, std::memory_order_acq_rel) + 1 < at) return false;
if (fired.exchange(true, std::memory_order_acq_rel)) return false;
+4 -2
View File
@@ -119,8 +119,10 @@ namespace MobileGL::MG_Pipe {
// A DEBUG KNOB THAT MAKES ONE DEVICE CHECK REPORT A LOSS, so the containment above can be
// driven without a GPU that faults on demand. MOBILEGL_DEBUG_INJECT_DEVICE_LOST_AT=N (or, on
// Android, the property debug.mobilegl.inject_device_lost_at) makes the N-th device check the
// backends make after the knob was first seen nonzero answer "lost", once per process. The
// backends ask at their frame boundaries and readbacks; unset (the default) it is one getenv.
// backends make after the knob was first seen nonzero answer "lost", once per value: setting
// another value (or 0 and then a value again) re-arms it, so one server process can lose
// several sessions in turn. The backends ask at their frame boundaries and readbacks; unset
// (the default) it is one getenv.
bool MGPipeDebugDeviceLossDue();
#endif
+30
View File
@@ -250,3 +250,33 @@ TEST(FatalFunnelSeam, ADeviceLossWithNoArmedSessionDiesThroughSessionFail) {
<< "an unarmed device loss must keep the inproc shape's death";
#endif
}
#if !defined(_WIN32)
// THE DEBUG KNOB RE-ARMS ON A NEW VALUE. One server process serves many sessions, so a device test
// that loses a second session in the same process sets the knob again. Each value fires once, at its
// N-th check; the same value set again after 0 counts afresh. In a death-test child, so the knob's
// process-wide counters start at zero. Red with the old once-per-process latch: the second value
// never fires (exit code 2).
TEST(FatalFunnelSeam, TheDeviceLossKnobFiresOncePerValueAndReArmsOnANewOne) {
EXPECT_EXIT(
{
const auto checks = [](int n) {
int fired = 0;
for (int i = 0; i < n; ++i) fired += MobileGL::MG_Pipe::MGPipeDebugDeviceLossDue() ? 1 << i : 0;
return fired;
};
::unsetenv("MOBILEGL_DEBUG_INJECT_DEVICE_LOST_AT");
if (checks(3) != 0) std::_Exit(1);
::setenv("MOBILEGL_DEBUG_INJECT_DEVICE_LOST_AT", "2", 1);
if (checks(4) != 0b0010) std::_Exit(1); // the 2nd check only, then never again
::setenv("MOBILEGL_DEBUG_INJECT_DEVICE_LOST_AT", "1", 1);
if (checks(3) != 0b001) std::_Exit(2); // a new value re-arms
::setenv("MOBILEGL_DEBUG_INJECT_DEVICE_LOST_AT", "0", 1);
if (checks(2) != 0) std::_Exit(3);
::setenv("MOBILEGL_DEBUG_INJECT_DEVICE_LOST_AT", "1", 1);
if (checks(2) != 0b01) std::_Exit(4); // 0 then the same value again re-arms too
std::_Exit(0);
},
::testing::ExitedWithCode(0), "");
}
#endif