fix(native): harden player teardown and shelf recovery

This commit is contained in:
edde746
2026-07-25 16:16:24 +02:00
parent 4af77f4696
commit f8f366a513
26 changed files with 2895 additions and 302 deletions
+416 -62
View File
@@ -23,6 +23,20 @@ constexpr wchar_t kRegOriginalHeight[] = L"OriginalHeight";
constexpr wchar_t kRegOriginalHDR[] = L"OriginalHDREnabled";
constexpr wchar_t kRegModeChanged[] = L"ModeChanged";
constexpr wchar_t kRegHDRChanged[] = L"HDRChanged";
constexpr wchar_t kRegModeTakeoverEligible[] = L"ModeTakeoverEligible";
constexpr wchar_t kRegHDRTakeoverEligible[] = L"HDRTakeoverEligible";
constexpr wchar_t kRegModeRecoverySlot[] = L"ModeRecoverySlot";
constexpr wchar_t kRegHDRRecoverySlot[] = L"HDRRecoverySlot";
constexpr wchar_t kRegModeDeviceNameAlternate[] = L"ModeDeviceNameAlternate";
constexpr wchar_t kRegHDRDeviceNameAlternate[] = L"HDRDeviceNameAlternate";
constexpr wchar_t kRegOriginalRefreshRateAlternate[] = L"OriginalRefreshRateAlternate";
constexpr wchar_t kRegOriginalWidthAlternate[] = L"OriginalWidthAlternate";
constexpr wchar_t kRegOriginalHeightAlternate[] = L"OriginalHeightAlternate";
constexpr wchar_t kRegOriginalHDRAlternate[] = L"OriginalHDREnabledAlternate";
constexpr wchar_t kRegModeHandoffPending[] = L"ModeHandoffPending";
constexpr wchar_t kRegHDRHandoffPending[] = L"HDRHandoffPending";
constexpr wchar_t kRegModePreviousRecoverySlot[] = L"ModePreviousRecoverySlot";
constexpr wchar_t kRegHDRPreviousRecoverySlot[] = L"HDRPreviousRecoverySlot";
std::recursive_mutex g_display_override_mutex;
bool g_live_mode_recovery_record = false;
@@ -46,7 +60,11 @@ class RecoveryRunGuard {
bool PrepareModeRecoveryAtRegistry(const std::wstring& device_name, DWORD width, DWORD height, DWORD refresh_rate);
bool PrepareHDRRecoveryAtRegistry(const std::wstring& device_name, bool enabled);
bool CompleteRecoveryOperationAtRegistry(const wchar_t* marker);
bool CompleteRecoveryOperationAtRegistry(
const wchar_t* marker, const wchar_t* takeover_disposition, const wchar_t* handoff_pending);
bool FinalizeModeRecoveryAtRegistry(bool os_apply_succeeded);
bool FinalizeHDRRecoveryAtRegistry(bool os_apply_succeeded);
bool MarkTakeoverEligibleAtRegistry(const wchar_t* takeover_disposition);
} // namespace
@@ -240,12 +258,14 @@ bool DisplayModeManager::SetDisplayMode(HWND window, DWORD width, DWORD height,
}
if (changed) {
FinalizeModeRecoveryAtRegistry(true);
mode_changed_ = true;
} else if (!mode_was_changed) {
// Do not discard an independently persisted HDR operation owned by
// another manager or retained from startup recovery.
CompleteRecoveryOperationAtRegistry(kRegModeChanged);
// A failed takeover rolls back to its prior marked original. A fresh
// failed operation still clears only its own marker.
const bool recovery_completed = FinalizeModeRecoveryAtRegistry(false);
g_live_mode_recovery_record = false;
if (!recovery_completed) MarkTakeoverEligibleAtRegistry(kRegModeTakeoverEligible);
}
return changed;
@@ -256,6 +276,7 @@ bool DisplayModeManager::RestoreOriginalMode(HWND) {
if (!mode_changed_) return false;
if (original_device_name_.empty()) {
g_live_mode_recovery_record = false;
MarkTakeoverEligibleAtRegistry(kRegModeTakeoverEligible);
return false;
}
@@ -269,14 +290,18 @@ bool DisplayModeManager::RestoreOriginalMode(HWND) {
}
if (rc != DISP_CHANGE_SUCCESSFUL) {
// The explicit owner has given up. Keep the durable marker, but release it
// so a later topology notification can restore a reconnected target.
// so a later topology notification can restore a reconnected target or a
// conflicting mode request can consume the failed recovery disposition.
g_live_mode_recovery_record = false;
MarkTakeoverEligibleAtRegistry(kRegModeTakeoverEligible);
return false;
}
mode_changed_ = false;
CompleteRecoveryOperationAtRegistry(kRegModeChanged);
const bool recovery_completed =
CompleteRecoveryOperationAtRegistry(kRegModeChanged, kRegModeTakeoverEligible, kRegModeHandoffPending);
g_live_mode_recovery_record = false;
if (!recovery_completed) MarkTakeoverEligibleAtRegistry(kRegModeTakeoverEligible);
return true;
}
@@ -385,8 +410,9 @@ bool DisplayModeManager::SetHDREnabled(HWND window, bool enabled) {
const LONG result = SetHDRStateForTarget(*target_id, enabled);
if (result != ERROR_SUCCESS) {
if (!hdr_was_changed) {
CompleteRecoveryOperationAtRegistry(kRegHDRChanged);
const bool recovery_completed = FinalizeHDRRecoveryAtRegistry(false);
g_live_hdr_recovery_record = false;
if (!recovery_completed) MarkTakeoverEligibleAtRegistry(kRegHDRTakeoverEligible);
}
return false;
}
@@ -398,6 +424,7 @@ bool DisplayModeManager::SetHDREnabled(HWND window, bool enabled) {
ChangeDisplaySettingsExW(device_name.c_str(), &pre_toggle_dm, nullptr, CDS_FULLSCREEN, nullptr);
}
FinalizeHDRRecoveryAtRegistry(true);
hdr_changed_ = true;
return true;
}
@@ -407,12 +434,14 @@ bool DisplayModeManager::RestoreOriginalHDRState(HWND window) {
if (!hdr_changed_) return false;
if (original_hdr_device_name_.empty()) {
g_live_hdr_recovery_record = false;
MarkTakeoverEligibleAtRegistry(kRegHDRTakeoverEligible);
return false;
}
const auto target_id = GetDisplayTargetId(original_hdr_device_name_);
if (!target_id) {
g_live_hdr_recovery_record = false;
MarkTakeoverEligibleAtRegistry(kRegHDRTakeoverEligible);
return false;
}
@@ -428,6 +457,7 @@ bool DisplayModeManager::RestoreOriginalHDRState(HWND window) {
if (SetHDRStateForTarget(*target_id, original_hdr_enabled_) != ERROR_SUCCESS) {
g_live_hdr_recovery_record = false;
MarkTakeoverEligibleAtRegistry(kRegHDRTakeoverEligible);
return false;
}
@@ -439,8 +469,10 @@ bool DisplayModeManager::RestoreOriginalHDRState(HWND window) {
}
hdr_changed_ = false;
CompleteRecoveryOperationAtRegistry(kRegHDRChanged);
const bool recovery_completed =
CompleteRecoveryOperationAtRegistry(kRegHDRChanged, kRegHDRTakeoverEligible, kRegHDRHandoffPending);
g_live_hdr_recovery_record = false;
if (!recovery_completed) MarkTakeoverEligibleAtRegistry(kRegHDRTakeoverEligible);
return true;
}
@@ -528,8 +560,30 @@ bool RecoveryRecordExists() {
}
bool exists = false;
for (const wchar_t* value_name :
{kRegVersion, kRegModeDeviceName, kRegLegacyDeviceName, kRegHDRDeviceName, kRegOriginalRefreshRate,
kRegOriginalWidth, kRegOriginalHeight, kRegOriginalHDR, kRegModeChanged, kRegHDRChanged}) {
{kRegVersion,
kRegModeDeviceName,
kRegLegacyDeviceName,
kRegHDRDeviceName,
kRegOriginalRefreshRate,
kRegOriginalWidth,
kRegOriginalHeight,
kRegOriginalHDR,
kRegModeChanged,
kRegHDRChanged,
kRegModeTakeoverEligible,
kRegHDRTakeoverEligible,
kRegModeRecoverySlot,
kRegHDRRecoverySlot,
kRegModeDeviceNameAlternate,
kRegHDRDeviceNameAlternate,
kRegOriginalRefreshRateAlternate,
kRegOriginalWidthAlternate,
kRegOriginalHeightAlternate,
kRegOriginalHDRAlternate,
kRegModeHandoffPending,
kRegHDRHandoffPending,
kRegModePreviousRecoverySlot,
kRegHDRPreviousRecoverySlot}) {
DWORD size = 0;
const LONG result = RegQueryValueExW(key, value_name, nullptr, nullptr, nullptr, &size);
if (result == ERROR_SUCCESS || result == ERROR_MORE_DATA) {
@@ -606,8 +660,30 @@ class Win32DisplayRecoveryBackend final : public DisplayRecoveryBackend {
bool deleted = true;
for (const wchar_t* value_name :
{kRegVersion, kRegModeDeviceName, kRegLegacyDeviceName, kRegHDRDeviceName, kRegOriginalRefreshRate,
kRegOriginalWidth, kRegOriginalHeight, kRegOriginalHDR, kRegModeChanged, kRegHDRChanged}) {
{kRegVersion,
kRegModeDeviceName,
kRegLegacyDeviceName,
kRegHDRDeviceName,
kRegOriginalRefreshRate,
kRegOriginalWidth,
kRegOriginalHeight,
kRegOriginalHDR,
kRegModeChanged,
kRegHDRChanged,
kRegModeTakeoverEligible,
kRegHDRTakeoverEligible,
kRegModeRecoverySlot,
kRegHDRRecoverySlot,
kRegModeDeviceNameAlternate,
kRegHDRDeviceNameAlternate,
kRegOriginalRefreshRateAlternate,
kRegOriginalWidthAlternate,
kRegOriginalHeightAlternate,
kRegOriginalHDRAlternate,
kRegModeHandoffPending,
kRegHDRHandoffPending,
kRegModePreviousRecoverySlot,
kRegHDRPreviousRecoverySlot}) {
const LONG result = RegDeleteValueW(key, value_name);
deleted = deleted && (result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND);
}
@@ -620,33 +696,78 @@ namespace {
bool ReadValidModeValues(
DisplayRecoveryBackend& backend, const wchar_t* device_value_name, std::wstring& device_name, DWORD& width,
DWORD& height, DWORD& refresh_rate) {
return backend.ReadString(device_value_name, device_name) && backend.ReadDWORD(kRegOriginalWidth, width) &&
width > 0 && backend.ReadDWORD(kRegOriginalHeight, height) && height > 0 &&
backend.ReadDWORD(kRegOriginalRefreshRate, refresh_rate) && refresh_rate > 0;
DWORD& height, DWORD& refresh_rate, bool alternate = false) {
const wchar_t* width_value_name = alternate ? kRegOriginalWidthAlternate : kRegOriginalWidth;
const wchar_t* height_value_name = alternate ? kRegOriginalHeightAlternate : kRegOriginalHeight;
const wchar_t* refresh_value_name = alternate ? kRegOriginalRefreshRateAlternate : kRegOriginalRefreshRate;
return backend.ReadString(device_value_name, device_name) && backend.ReadDWORD(width_value_name, width) &&
width > 0 && backend.ReadDWORD(height_value_name, height) && height > 0 &&
backend.ReadDWORD(refresh_value_name, refresh_rate) && refresh_rate > 0;
}
bool ReadValidHDRValues(
DisplayRecoveryBackend& backend, const wchar_t* device_value_name, std::wstring& device_name, DWORD& original_hdr) {
return backend.ReadString(device_value_name, device_name) && backend.ReadDWORD(kRegOriginalHDR, original_hdr) &&
DisplayRecoveryBackend& backend, const wchar_t* device_value_name, std::wstring& device_name, DWORD& original_hdr,
bool alternate = false) {
const wchar_t* original_value_name = alternate ? kRegOriginalHDRAlternate : kRegOriginalHDR;
return backend.ReadString(device_value_name, device_name) && backend.ReadDWORD(original_value_name, original_hdr) &&
original_hdr <= 1;
}
bool ReadValidMarkedMode(
DisplayRecoveryBackend& backend, std::wstring& device_name, DWORD& width, DWORD& height, DWORD& refresh_rate) {
DWORD version = 0;
DWORD marker = 0;
return backend.ReadDWORD(kRegVersion, version) && version == kRecoveryVersion &&
backend.ReadDWORD(kRegModeChanged, marker) && marker == 1 &&
ReadValidModeValues(backend, kRegModeDeviceName, device_name, width, height, refresh_rate);
bool ReadRecoverySlot(DisplayRecoveryBackend& backend, const wchar_t* slot_value_name, bool& alternate) {
DWORD slot = 0;
if (!backend.ReadDWORD(slot_value_name, slot)) {
// Version-1 records created before alternate slots implicitly use the
// original value set.
alternate = false;
return true;
}
if (slot > 1) return false;
alternate = slot == 1;
return true;
}
bool ReadValidMarkedHDR(DisplayRecoveryBackend& backend, std::wstring& device_name, DWORD& original_hdr) {
bool ReadValidModeSlot(
DisplayRecoveryBackend& backend, bool alternate, std::wstring& device_name, DWORD& width, DWORD& height,
DWORD& refresh_rate) {
return ReadValidModeValues(
backend, alternate ? kRegModeDeviceNameAlternate : kRegModeDeviceName, device_name, width, height, refresh_rate,
alternate);
}
bool ReadValidHDRSlot(DisplayRecoveryBackend& backend, bool alternate, std::wstring& device_name, DWORD& original_hdr) {
return ReadValidHDRValues(
backend, alternate ? kRegHDRDeviceNameAlternate : kRegHDRDeviceName, device_name, original_hdr, alternate);
}
bool ReadValidMarkedMode(
DisplayRecoveryBackend& backend, std::wstring& device_name, DWORD& width, DWORD& height, DWORD& refresh_rate,
bool* alternate_slot = nullptr) {
DWORD version = 0;
DWORD marker = 0;
return backend.ReadDWORD(kRegVersion, version) && version == kRecoveryVersion &&
backend.ReadDWORD(kRegHDRChanged, marker) && marker == 1 &&
ReadValidHDRValues(backend, kRegHDRDeviceName, device_name, original_hdr);
bool alternate = false;
if (!backend.ReadDWORD(kRegVersion, version) || version != kRecoveryVersion ||
!backend.ReadDWORD(kRegModeChanged, marker) || marker != 1 ||
!ReadRecoverySlot(backend, kRegModeRecoverySlot, alternate) ||
!ReadValidModeSlot(backend, alternate, device_name, width, height, refresh_rate)) {
return false;
}
if (alternate_slot) *alternate_slot = alternate;
return true;
}
bool ReadValidMarkedHDR(
DisplayRecoveryBackend& backend, std::wstring& device_name, DWORD& original_hdr, bool* alternate_slot = nullptr) {
DWORD version = 0;
DWORD marker = 0;
bool alternate = false;
if (!backend.ReadDWORD(kRegVersion, version) || version != kRecoveryVersion ||
!backend.ReadDWORD(kRegHDRChanged, marker) || marker != 1 ||
!ReadRecoverySlot(backend, kRegHDRRecoverySlot, alternate) ||
!ReadValidHDRSlot(backend, alternate, device_name, original_hdr)) {
return false;
}
if (alternate_slot) *alternate_slot = alternate;
return true;
}
bool DeleteRecordIfNoMarkedOperations(DisplayRecoveryBackend& backend) {
@@ -662,12 +783,82 @@ bool DeleteRecordIfNoMarkedOperations(DisplayRecoveryBackend& backend) {
return true;
}
bool CompleteRecoveryOperation(DisplayRecoveryBackend& backend, const wchar_t* marker) {
if (!backend.ClearMarker(marker)) return false;
bool IsTakeoverEligible(DisplayRecoveryBackend& backend, const wchar_t* takeover_disposition) {
DWORD value = 0;
return backend.ReadDWORD(takeover_disposition, value) && value == 1;
}
bool MarkTakeoverEligible(DisplayRecoveryBackend& backend, const wchar_t* takeover_disposition) {
return backend.WriteDWORD(takeover_disposition, 1);
}
bool ResetTakeoverEligibility(DisplayRecoveryBackend& backend, const wchar_t* takeover_disposition) {
return backend.WriteDWORD(takeover_disposition, 0);
}
bool ReadHandoffPending(DisplayRecoveryBackend& backend, const wchar_t* handoff_value_name, bool& pending) {
DWORD value = 0;
if (!backend.ReadDWORD(handoff_value_name, value)) {
pending = false;
return true;
}
if (value > 1) return false;
pending = value == 1;
return true;
}
bool ReadStoredRecoverySlot(DisplayRecoveryBackend& backend, const wchar_t* slot_value_name, bool& alternate) {
DWORD value = 0;
if (!backend.ReadDWORD(slot_value_name, value) || value > 1) return false;
alternate = value == 1;
return true;
}
bool CompleteRecoveryOperation(
DisplayRecoveryBackend& backend, const wchar_t* marker, const wchar_t* takeover_disposition,
const wchar_t* handoff_pending) {
// Handoff metadata must be inactive before its shared marker disappears.
if (!backend.WriteDWORD(handoff_pending, 0) || !ResetTakeoverEligibility(backend, takeover_disposition) ||
!backend.ClearMarker(marker)) {
return false;
}
DeleteRecordIfNoMarkedOperations(backend);
return true;
}
bool ConfirmPreparedRecovery(DisplayRecoveryBackend& backend, const wchar_t* handoff_pending) {
bool pending = false;
if (!ReadHandoffPending(backend, handoff_pending, pending)) return false;
return !pending || backend.WriteDWORD(handoff_pending, 0);
}
bool RollbackRecoveryHandoff(
DisplayRecoveryBackend& backend, const wchar_t* takeover_disposition, const wchar_t* recovery_slot,
const wchar_t* previous_recovery_slot, const wchar_t* handoff_pending) {
bool previous_alternate = false;
if (!ReadStoredRecoverySlot(backend, previous_recovery_slot, previous_alternate) ||
!backend.WriteDWORD(recovery_slot, previous_alternate ? 1 : 0) ||
!MarkTakeoverEligible(backend, takeover_disposition) || !backend.WriteDWORD(handoff_pending, 0)) {
return false;
}
return true;
}
bool FinalizePreparedRecovery(
DisplayRecoveryBackend& backend, bool os_apply_succeeded, const wchar_t* marker,
const wchar_t* takeover_disposition, const wchar_t* recovery_slot, const wchar_t* previous_recovery_slot,
const wchar_t* handoff_pending) {
if (os_apply_succeeded) return ConfirmPreparedRecovery(backend, handoff_pending);
bool pending = false;
if (!ReadHandoffPending(backend, handoff_pending, pending)) return false;
if (pending) {
return RollbackRecoveryHandoff(
backend, takeover_disposition, recovery_slot, previous_recovery_slot, handoff_pending);
}
return CompleteRecoveryOperation(backend, marker, takeover_disposition, handoff_pending);
}
bool PreserveValidModeSiblingOrClear(DisplayRecoveryBackend& backend) {
DWORD marker = 0;
if (!backend.ReadDWORD(kRegModeChanged, marker)) {
@@ -711,22 +902,59 @@ bool DisplayModeManager::PrepareModeRecovery(
DWORD existing_height = 0;
DWORD existing_refresh_rate = 0;
std::wstring existing_device_name;
if (ReadValidMarkedMode(backend, existing_device_name, existing_width, existing_height, existing_refresh_rate)) {
// A valid marked original is already protecting a live or failed
// operation. Reuse it only when this manager has the same original;
// replacing it would lose the only restoration point.
return existing_device_name == device_name && existing_width == width && existing_height == height &&
existing_refresh_rate == refresh_rate;
bool existing_alternate = false;
if (ReadValidMarkedMode(
backend, existing_device_name, existing_width, existing_height, existing_refresh_rate, &existing_alternate)) {
bool handoff_pending = false;
if (!ReadHandoffPending(backend, kRegModeHandoffPending, handoff_pending) || handoff_pending) return false;
const bool same_original = existing_device_name == device_name && existing_width == width &&
existing_height == height && existing_refresh_rate == refresh_rate;
if (same_original) {
// Reusing the restoration point makes it live again, so stale takeover
// permission must be durably revoked before the Windows mutation.
return ResetTakeoverEligibility(backend, kRegModeTakeoverEligible);
}
if (!IsTakeoverEligible(backend, kRegModeTakeoverEligible)) return false;
// Stage the replacement in the inactive slot. Handoff metadata keeps both
// originals recoverable from the selector switch until the OS apply is
// confirmed.
const bool replacement_alternate = !existing_alternate;
const wchar_t* replacement_device_name = replacement_alternate ? kRegModeDeviceNameAlternate : kRegModeDeviceName;
const wchar_t* replacement_width = replacement_alternate ? kRegOriginalWidthAlternate : kRegOriginalWidth;
const wchar_t* replacement_height = replacement_alternate ? kRegOriginalHeightAlternate : kRegOriginalHeight;
const wchar_t* replacement_refresh =
replacement_alternate ? kRegOriginalRefreshRateAlternate : kRegOriginalRefreshRate;
if (!backend.WriteDWORD(kRegVersion, kRecoveryVersion) ||
!backend.WriteString(replacement_device_name, device_name) || !backend.WriteDWORD(replacement_width, width) ||
!backend.WriteDWORD(replacement_height, height) || !backend.WriteDWORD(replacement_refresh, refresh_rate) ||
!backend.WriteDWORD(kRegModePreviousRecoverySlot, existing_alternate ? 1 : 0) ||
!ResetTakeoverEligibility(backend, kRegModeTakeoverEligible)) {
return false;
}
if (!backend.WriteDWORD(kRegModeHandoffPending, 1)) {
MarkTakeoverEligible(backend, kRegModeTakeoverEligible);
return false;
}
if (!backend.WriteDWORD(kRegModeRecoverySlot, replacement_alternate ? 1 : 0)) {
RollbackRecoveryHandoff(
backend, kRegModeTakeoverEligible, kRegModeRecoverySlot, kRegModePreviousRecoverySlot,
kRegModeHandoffPending);
return false;
}
return true;
}
// Deactivate an incomplete old mode operation before replacing any
// originals. A crash anywhere before the final write is therefore a
// harmless pre-mutation prefix.
// An incomplete operation has no authoritative original to preserve. Keep
// the existing marker-last preparation sequence and select the primary slot
// before publishing the new operation.
if (!backend.ClearMarker(kRegModeChanged)) return false;
return backend.WriteDWORD(kRegVersion, kRecoveryVersion) && backend.WriteString(kRegModeDeviceName, device_name) &&
backend.WriteDWORD(kRegOriginalWidth, width) && backend.WriteDWORD(kRegOriginalHeight, height) &&
backend.WriteDWORD(kRegOriginalRefreshRate, refresh_rate) && backend.WriteDWORD(kRegModeChanged, 1);
backend.WriteDWORD(kRegOriginalRefreshRate, refresh_rate) && backend.WriteDWORD(kRegModeHandoffPending, 0) &&
ResetTakeoverEligibility(backend, kRegModeTakeoverEligible) && backend.WriteDWORD(kRegModeRecoverySlot, 0) &&
backend.WriteDWORD(kRegModeChanged, 1);
}
bool DisplayModeManager::PrepareHDRRecovery(
@@ -736,14 +964,42 @@ bool DisplayModeManager::PrepareHDRRecovery(
DWORD existing_original = 0;
std::wstring existing_device_name;
if (ReadValidMarkedHDR(backend, existing_device_name, existing_original)) {
return existing_device_name == device_name && existing_original == (enabled ? 1u : 0u);
bool existing_alternate = false;
if (ReadValidMarkedHDR(backend, existing_device_name, existing_original, &existing_alternate)) {
bool handoff_pending = false;
if (!ReadHandoffPending(backend, kRegHDRHandoffPending, handoff_pending) || handoff_pending) return false;
const bool same_original = existing_device_name == device_name && existing_original == (enabled ? 1u : 0u);
if (same_original) return ResetTakeoverEligibility(backend, kRegHDRTakeoverEligible);
if (!IsTakeoverEligible(backend, kRegHDRTakeoverEligible)) return false;
const bool replacement_alternate = !existing_alternate;
const wchar_t* replacement_device_name = replacement_alternate ? kRegHDRDeviceNameAlternate : kRegHDRDeviceName;
const wchar_t* replacement_original = replacement_alternate ? kRegOriginalHDRAlternate : kRegOriginalHDR;
if (!backend.WriteDWORD(kRegVersion, kRecoveryVersion) ||
!backend.WriteString(replacement_device_name, device_name) ||
!backend.WriteDWORD(replacement_original, enabled ? 1 : 0) ||
!backend.WriteDWORD(kRegHDRPreviousRecoverySlot, existing_alternate ? 1 : 0) ||
!ResetTakeoverEligibility(backend, kRegHDRTakeoverEligible)) {
return false;
}
if (!backend.WriteDWORD(kRegHDRHandoffPending, 1)) {
MarkTakeoverEligible(backend, kRegHDRTakeoverEligible);
return false;
}
if (!backend.WriteDWORD(kRegHDRRecoverySlot, replacement_alternate ? 1 : 0)) {
RollbackRecoveryHandoff(
backend, kRegHDRTakeoverEligible, kRegHDRRecoverySlot, kRegHDRPreviousRecoverySlot, kRegHDRHandoffPending);
return false;
}
return true;
}
if (!backend.ClearMarker(kRegHDRChanged)) return false;
return backend.WriteDWORD(kRegVersion, kRecoveryVersion) && backend.WriteString(kRegHDRDeviceName, device_name) &&
backend.WriteDWORD(kRegOriginalHDR, enabled ? 1 : 0) && backend.WriteDWORD(kRegHDRChanged, 1);
backend.WriteDWORD(kRegOriginalHDR, enabled ? 1 : 0) && backend.WriteDWORD(kRegHDRHandoffPending, 0) &&
ResetTakeoverEligibility(backend, kRegHDRTakeoverEligible) && backend.WriteDWORD(kRegHDRRecoverySlot, 0) &&
backend.WriteDWORD(kRegHDRChanged, 1);
}
namespace {
@@ -758,9 +1014,29 @@ bool PrepareHDRRecoveryAtRegistry(const std::wstring& device_name, bool enabled)
return DisplayModeManager::PrepareHDRRecovery(backend, device_name, enabled);
}
bool CompleteRecoveryOperationAtRegistry(const wchar_t* marker) {
bool CompleteRecoveryOperationAtRegistry(
const wchar_t* marker, const wchar_t* takeover_disposition, const wchar_t* handoff_pending) {
Win32DisplayRecoveryBackend backend;
return CompleteRecoveryOperation(backend, marker);
return CompleteRecoveryOperation(backend, marker, takeover_disposition, handoff_pending);
}
bool FinalizeModeRecoveryAtRegistry(bool os_apply_succeeded) {
Win32DisplayRecoveryBackend backend;
return FinalizePreparedRecovery(
backend, os_apply_succeeded, kRegModeChanged, kRegModeTakeoverEligible, kRegModeRecoverySlot,
kRegModePreviousRecoverySlot, kRegModeHandoffPending);
}
bool FinalizeHDRRecoveryAtRegistry(bool os_apply_succeeded) {
Win32DisplayRecoveryBackend backend;
return FinalizePreparedRecovery(
backend, os_apply_succeeded, kRegHDRChanged, kRegHDRTakeoverEligible, kRegHDRRecoverySlot,
kRegHDRPreviousRecoverySlot, kRegHDRHandoffPending);
}
bool MarkTakeoverEligibleAtRegistry(const wchar_t* takeover_disposition) {
Win32DisplayRecoveryBackend backend;
return MarkTakeoverEligible(backend, takeover_disposition);
}
bool RecoverRecord(DisplayRecoveryBackend& backend, bool mode_is_live, bool hdr_is_live) {
@@ -794,9 +1070,32 @@ bool RecoverRecord(DisplayRecoveryBackend& backend, bool mode_is_live, bool hdr_
DWORD width = 0;
DWORD height = 0;
DWORD refresh_rate = 0;
const bool mode_requested =
mode_marker_read && mode_marker == 1 &&
ReadValidModeValues(backend, mode_device_value_name, mode_device_name, width, height, refresh_rate);
bool mode_handoff_pending = false;
std::wstring previous_mode_device_name;
DWORD previous_width = 0;
DWORD previous_height = 0;
DWORD previous_refresh_rate = 0;
bool mode_requested = false;
if (mode_marker_read && mode_marker == 1) {
if (has_version) {
bool handoff_state_valid = ReadHandoffPending(backend, kRegModeHandoffPending, mode_handoff_pending);
if (handoff_state_valid && mode_handoff_pending) {
bool previous_alternate = false;
handoff_state_valid =
ReadStoredRecoverySlot(backend, kRegModePreviousRecoverySlot, previous_alternate) &&
ReadValidModeSlot(backend, !previous_alternate, mode_device_name, width, height, refresh_rate) &&
ReadValidModeSlot(
backend, previous_alternate, previous_mode_device_name, previous_width, previous_height,
previous_refresh_rate);
} else if (handoff_state_valid) {
handoff_state_valid = ReadValidMarkedMode(backend, mode_device_name, width, height, refresh_rate);
}
mode_requested = handoff_state_valid;
} else {
mode_requested =
ReadValidModeValues(backend, mode_device_value_name, mode_device_name, width, height, refresh_rate);
}
}
if (!mode_is_live && (!mode_marker_read || mode_marker > 1 || (mode_marker == 1 && !mode_requested))) {
// Malformation in one operation does not erase a valid or live sibling.
backend.ClearMarker(kRegModeChanged);
@@ -806,8 +1105,27 @@ bool RecoverRecord(DisplayRecoveryBackend& backend, bool mode_is_live, bool hdr_
const bool hdr_marker_read = backend.ReadDWORD(kRegHDRChanged, hdr_marker);
std::wstring hdr_device_name;
DWORD original_hdr = 0;
const bool hdr_requested = hdr_marker_read && hdr_marker == 1 &&
ReadValidHDRValues(backend, hdr_device_value_name, hdr_device_name, original_hdr);
bool hdr_handoff_pending = false;
std::wstring previous_hdr_device_name;
DWORD previous_original_hdr = 0;
bool hdr_requested = false;
if (hdr_marker_read && hdr_marker == 1) {
if (has_version) {
bool handoff_state_valid = ReadHandoffPending(backend, kRegHDRHandoffPending, hdr_handoff_pending);
if (handoff_state_valid && hdr_handoff_pending) {
bool previous_alternate = false;
handoff_state_valid =
ReadStoredRecoverySlot(backend, kRegHDRPreviousRecoverySlot, previous_alternate) &&
ReadValidHDRSlot(backend, !previous_alternate, hdr_device_name, original_hdr) &&
ReadValidHDRSlot(backend, previous_alternate, previous_hdr_device_name, previous_original_hdr);
} else if (handoff_state_valid) {
handoff_state_valid = ReadValidMarkedHDR(backend, hdr_device_name, original_hdr);
}
hdr_requested = handoff_state_valid;
} else {
hdr_requested = ReadValidHDRValues(backend, hdr_device_value_name, hdr_device_name, original_hdr);
}
}
if (!hdr_is_live && (!hdr_marker_read || hdr_marker > 1 || (hdr_marker == 1 && !hdr_requested))) {
backend.ClearMarker(kRegHDRChanged);
}
@@ -821,19 +1139,43 @@ bool RecoverRecord(DisplayRecoveryBackend& backend, bool mode_is_live, bool hdr_
bool completed = true;
if (recover_mode) {
if (backend.IsDevicePresent(mode_device_name) &&
backend.RestoreMode(mode_device_name, width, height, refresh_rate)) {
// A failed marker clear leaves an idempotent restoration for a later pass.
completed = CompleteRecoveryOperation(backend, kRegModeChanged) && completed;
} else {
const bool restored =
backend.IsDevicePresent(mode_device_name) && backend.RestoreMode(mode_device_name, width, height, refresh_rate);
if (mode_handoff_pending) {
// Restore the replacement first and the pre-handoff original last. Both
// remain marked until both idempotent restores and cleanup succeed.
const bool previous_restored =
backend.IsDevicePresent(previous_mode_device_name) &&
backend.RestoreMode(previous_mode_device_name, previous_width, previous_height, previous_refresh_rate);
if (!restored || !previous_restored ||
!CompleteRecoveryOperation(backend, kRegModeChanged, kRegModeTakeoverEligible, kRegModeHandoffPending)) {
completed = false;
}
} else if (
!restored ||
!CompleteRecoveryOperation(backend, kRegModeChanged, kRegModeTakeoverEligible, kRegModeHandoffPending)) {
// Retain the restoration point for topology retries, but remember that
// one real non-live recovery attempt failed so a later conflicting mode
// request may deliberately replace it.
MarkTakeoverEligible(backend, kRegModeTakeoverEligible);
completed = false;
}
}
if (recover_hdr) {
if (backend.IsDevicePresent(hdr_device_name) && backend.RestoreHDR(hdr_device_name, original_hdr != 0)) {
completed = CompleteRecoveryOperation(backend, kRegHDRChanged) && completed;
} else {
const bool restored =
backend.IsDevicePresent(hdr_device_name) && backend.RestoreHDR(hdr_device_name, original_hdr != 0);
if (hdr_handoff_pending) {
const bool previous_restored = backend.IsDevicePresent(previous_hdr_device_name) &&
backend.RestoreHDR(previous_hdr_device_name, previous_original_hdr != 0);
if (!restored || !previous_restored ||
!CompleteRecoveryOperation(backend, kRegHDRChanged, kRegHDRTakeoverEligible, kRegHDRHandoffPending)) {
completed = false;
}
} else if (
!restored ||
!CompleteRecoveryOperation(backend, kRegHDRChanged, kRegHDRTakeoverEligible, kRegHDRHandoffPending)) {
MarkTakeoverEligible(backend, kRegHDRTakeoverEligible);
completed = false;
}
}
@@ -860,7 +1202,19 @@ bool DisplayModeManager::RecoverIfNeeded(DisplayRecoveryBackend& backend) {
#if defined(PLEZY_DISPLAY_MODE_MANAGER_TESTING)
bool DisplayModeManager::CompleteRecoveryOperationForTesting(DisplayRecoveryBackend& backend, bool mode) {
std::lock_guard<std::recursive_mutex> transaction_lock(g_display_override_mutex);
return CompleteRecoveryOperation(backend, mode ? kRegModeChanged : kRegHDRChanged);
return CompleteRecoveryOperation(
backend, mode ? kRegModeChanged : kRegHDRChanged, mode ? kRegModeTakeoverEligible : kRegHDRTakeoverEligible,
mode ? kRegModeHandoffPending : kRegHDRHandoffPending);
}
bool DisplayModeManager::FinalizePreparedRecoveryForTesting(
DisplayRecoveryBackend& backend, bool mode, bool os_apply_succeeded) {
std::lock_guard<std::recursive_mutex> transaction_lock(g_display_override_mutex);
return FinalizePreparedRecovery(
backend, os_apply_succeeded, mode ? kRegModeChanged : kRegHDRChanged,
mode ? kRegModeTakeoverEligible : kRegHDRTakeoverEligible, mode ? kRegModeRecoverySlot : kRegHDRRecoverySlot,
mode ? kRegModePreviousRecoverySlot : kRegHDRPreviousRecoverySlot,
mode ? kRegModeHandoffPending : kRegHDRHandoffPending);
}
bool DisplayModeManager::RecoverIfNeededForTesting(
+12 -4
View File
@@ -105,21 +105,29 @@ class DisplayModeManager {
// --- Crash recovery ---
// Persist a complete original followed by its operation marker. These
// runner-internal seams make the crash ordering deterministic in tests.
// Persist a complete original followed by its operation marker. A failed
// non-live recovery remains retryable, but durably permits a later
// conflicting request of the same kind to replace it. Conflicting originals
// are staged in an inactive slot. A persisted handoff keeps both originals
// recoverable across the selector switch and OS call; success confirms the
// new slot, while OS failure rolls back the prior selector and eligibility.
// Reusing the same original revokes permission before it becomes live.
// These runner-internal seams make the crash ordering deterministic in tests.
static bool PrepareModeRecovery(
DisplayRecoveryBackend& backend, const std::wstring& device_name, DWORD width, DWORD height, DWORD refresh_rate);
static bool PrepareHDRRecovery(DisplayRecoveryBackend& backend, const std::wstring& device_name, bool enabled);
// Check for and recover from a prior crash that left display settings
// changed. Successful operation markers are cleared independently. Failed
// operations remain for the next startup or display-topology notification.
// changed. Successful operation markers and their takeover dispositions are
// cleared independently. Failed operations remain recovery-first until a
// conflicting same-kind preparation consumes their persisted disposition.
static bool RecoverIfNeeded();
static bool RecoverIfNeeded(DisplayRecoveryBackend& backend);
#if defined(PLEZY_DISPLAY_MODE_MANAGER_TESTING)
// Exercise persisted lifecycle exits and per-operation live ownership without
// touching a real display or registry.
static bool CompleteRecoveryOperationForTesting(DisplayRecoveryBackend& backend, bool mode);
static bool FinalizePreparedRecoveryForTesting(DisplayRecoveryBackend& backend, bool mode, bool os_apply_succeeded);
static bool RecoverIfNeededForTesting(DisplayRecoveryBackend& backend, bool mode_is_live, bool hdr_is_live);
#endif
+631 -31
View File
@@ -19,8 +19,24 @@ constexpr wchar_t kOriginalHeight[] = L"OriginalHeight";
constexpr wchar_t kOriginalHDR[] = L"OriginalHDREnabled";
constexpr wchar_t kModeChanged[] = L"ModeChanged";
constexpr wchar_t kHDRChanged[] = L"HDRChanged";
constexpr wchar_t kModeTakeoverEligible[] = L"ModeTakeoverEligible";
constexpr wchar_t kHDRTakeoverEligible[] = L"HDRTakeoverEligible";
constexpr wchar_t kModeRecoverySlot[] = L"ModeRecoverySlot";
constexpr wchar_t kHDRRecoverySlot[] = L"HDRRecoverySlot";
constexpr wchar_t kModeDeviceNameAlternate[] = L"ModeDeviceNameAlternate";
constexpr wchar_t kHDRDeviceNameAlternate[] = L"HDRDeviceNameAlternate";
constexpr wchar_t kOriginalRefreshRateAlternate[] = L"OriginalRefreshRateAlternate";
constexpr wchar_t kOriginalWidthAlternate[] = L"OriginalWidthAlternate";
constexpr wchar_t kOriginalHeightAlternate[] = L"OriginalHeightAlternate";
constexpr wchar_t kOriginalHDRAlternate[] = L"OriginalHDREnabledAlternate";
constexpr wchar_t kModeHandoffPending[] = L"ModeHandoffPending";
constexpr wchar_t kHDRHandoffPending[] = L"HDRHandoffPending";
constexpr wchar_t kModePreviousRecoverySlot[] = L"ModePreviousRecoverySlot";
constexpr wchar_t kHDRPreviousRecoverySlot[] = L"HDRPreviousRecoverySlot";
constexpr wchar_t kModeDevice[] = L"\\\\.\\DISPLAY1";
constexpr wchar_t kHDRDevice[] = L"\\\\.\\DISPLAY2";
constexpr wchar_t kReplacementModeDevice[] = L"\\\\.\\DISPLAY3";
constexpr wchar_t kReplacementHDRDevice[] = L"\\\\.\\DISPLAY4";
void Check(bool condition, const char* message) {
if (!condition) {
@@ -29,14 +45,35 @@ void Check(bool condition, const char* message) {
}
}
struct ExpectedModeRestore {
std::wstring device_name;
DWORD width;
DWORD height;
DWORD refresh_rate;
};
struct ExpectedHDRRestore {
std::wstring device_name;
bool enabled;
};
class FakeRecoveryBackend final : public DisplayRecoveryBackend {
public:
std::map<std::wstring, DWORD> dwords;
std::map<std::wstring, std::wstring> strings;
std::map<std::wstring, bool> device_present = {{kModeDevice, true}, {kHDRDevice, true}};
std::map<std::wstring, bool> device_present = {
{kModeDevice, true}, {kHDRDevice, true}, {kReplacementModeDevice, true}, {kReplacementHDRDevice, true}};
std::vector<std::wstring> events;
std::wstring expected_mode_device = kModeDevice;
DWORD expected_mode_width = 3840;
DWORD expected_mode_height = 2160;
DWORD expected_mode_refresh_rate = 60;
std::wstring expected_hdr_device = kHDRDevice;
bool expected_original_hdr = false;
std::vector<ExpectedModeRestore> expected_mode_restores;
std::vector<ExpectedHDRRestore> expected_hdr_restores;
size_t mode_restore_attempts = 0;
size_t hdr_restore_attempts = 0;
bool mode_restore_succeeds = true;
bool hdr_restore_succeeds = true;
bool mode_marker_clear_succeeds = true;
@@ -44,6 +81,7 @@ class FakeRecoveryBackend final : public DisplayRecoveryBackend {
bool delete_succeeds = true;
bool final_mode_marker_write_succeeds = true;
bool final_hdr_marker_write_succeeds = true;
std::wstring rejected_dword_write_event;
int delete_attempts = 0;
void SeedBoth() {
@@ -76,8 +114,10 @@ class FakeRecoveryBackend final : public DisplayRecoveryBackend {
bool WriteDWORD(const wchar_t* value_name, DWORD value) override {
const std::wstring name(value_name);
events.push_back(L"write:" + name + L"=" + std::to_wstring(value));
if ((name == kModeChanged && value == 1 && !final_mode_marker_write_succeeds) ||
const std::wstring event = L"write:" + name + L"=" + std::to_wstring(value);
events.push_back(event);
if (event == rejected_dword_write_event ||
(name == kModeChanged && value == 1 && !final_mode_marker_write_succeeds) ||
(name == kHDRChanged && value == 1 && !final_hdr_marker_write_succeeds)) {
return false;
}
@@ -98,15 +138,35 @@ class FakeRecoveryBackend final : public DisplayRecoveryBackend {
}
bool RestoreMode(const std::wstring& device_name, DWORD width, DWORD height, DWORD refresh_rate) override {
Check(device_name == expected_mode_device, "mode restore must use its persisted display");
Check(width == 3840 && height == 2160 && refresh_rate == 60, "mode restore must use persisted originals");
if (expected_mode_restores.empty()) {
Check(device_name == expected_mode_device, "mode restore must use its persisted display");
Check(
width == expected_mode_width && height == expected_mode_height && refresh_rate == expected_mode_refresh_rate,
"mode restore must use persisted originals");
} else {
Check(mode_restore_attempts < expected_mode_restores.size(), "mode recovery made an unexpected extra restore");
const auto& expected = expected_mode_restores[mode_restore_attempts];
Check(device_name == expected.device_name, "mode handoff restore order must be deterministic");
Check(
width == expected.width && height == expected.height && refresh_rate == expected.refresh_rate,
"mode handoff restore must use each persisted original");
}
++mode_restore_attempts;
events.push_back(L"restore:mode");
return mode_restore_succeeds;
}
bool RestoreHDR(const std::wstring& device_name, bool enabled) override {
Check(device_name == expected_hdr_device, "HDR restore must use its independently persisted display");
Check(!enabled, "HDR restore must use the persisted original state");
if (expected_hdr_restores.empty()) {
Check(device_name == expected_hdr_device, "HDR restore must use its independently persisted display");
Check(enabled == expected_original_hdr, "HDR restore must use the persisted original state");
} else {
Check(hdr_restore_attempts < expected_hdr_restores.size(), "HDR recovery made an unexpected extra restore");
const auto& expected = expected_hdr_restores[hdr_restore_attempts];
Check(device_name == expected.device_name, "HDR handoff restore order must be deterministic");
Check(enabled == expected.enabled, "HDR handoff restore must use each persisted original");
}
++hdr_restore_attempts;
events.push_back(L"restore:hdr");
return hdr_restore_succeeds;
}
@@ -136,18 +196,24 @@ size_t EventIndex(const std::vector<std::wstring>& events, const std::wstring& e
return events.size();
}
bool ApplyModeAfterPreparing(FakeRecoveryBackend& backend) {
if (!DisplayModeManager::PrepareModeRecovery(backend, kModeDevice, 3840, 2160, 60)) {
bool ApplyModeAfterPreparing(
FakeRecoveryBackend& backend, const std::wstring& device_name = kModeDevice, DWORD width = 3840,
DWORD height = 2160, DWORD refresh_rate = 60, bool os_apply_succeeds = true) {
if (!DisplayModeManager::PrepareModeRecovery(backend, device_name, width, height, refresh_rate)) {
return false;
}
backend.events.push_back(L"os:mode");
return true;
backend.events.push_back(os_apply_succeeds ? L"os:mode" : L"os:mode:failed");
DisplayModeManager::FinalizePreparedRecoveryForTesting(backend, true, os_apply_succeeds);
return os_apply_succeeds;
}
bool ApplyHDRAfterPreparing(FakeRecoveryBackend& backend) {
if (!DisplayModeManager::PrepareHDRRecovery(backend, kHDRDevice, false)) return false;
backend.events.push_back(L"os:hdr");
return true;
bool ApplyHDRAfterPreparing(
FakeRecoveryBackend& backend, const std::wstring& device_name = kHDRDevice, bool enabled = false,
bool os_apply_succeeds = true) {
if (!DisplayModeManager::PrepareHDRRecovery(backend, device_name, enabled)) return false;
backend.events.push_back(os_apply_succeeds ? L"os:hdr" : L"os:hdr:failed");
DisplayModeManager::FinalizePreparedRecoveryForTesting(backend, false, os_apply_succeeds);
return os_apply_succeeds;
}
void TestMarkersArePersistedBeforeMutation() {
@@ -160,8 +226,10 @@ void TestMarkersArePersistedBeforeMutation() {
EventIndex(mode.events, L"write:ModeDeviceName") < mode_marker &&
EventIndex(mode.events, L"write:OriginalWidth=3840") < mode_marker &&
EventIndex(mode.events, L"write:OriginalHeight=2160") < mode_marker &&
EventIndex(mode.events, L"write:OriginalRefreshRate=60") < mode_marker,
"all mode originals must precede the operation marker");
EventIndex(mode.events, L"write:OriginalRefreshRate=60") < mode_marker &&
EventIndex(mode.events, L"write:ModeTakeoverEligible=0") < mode_marker &&
EventIndex(mode.events, L"write:ModeRecoverySlot=0") < mode_marker,
"all mode originals, slot, and reset disposition must precede the operation marker");
FakeRecoveryBackend hdr;
Check(ApplyHDRAfterPreparing(hdr), "a complete HDR recovery record must admit the OS mutation");
@@ -169,8 +237,10 @@ void TestMarkersArePersistedBeforeMutation() {
Check(
EventIndex(hdr.events, L"write:HDRDeviceName") < hdr_marker &&
EventIndex(hdr.events, L"write:OriginalHDREnabled=0") < hdr_marker &&
EventIndex(hdr.events, L"write:HDRTakeoverEligible=0") < hdr_marker &&
EventIndex(hdr.events, L"write:HDRRecoverySlot=0") < hdr_marker &&
hdr_marker < EventIndex(hdr.events, L"os:hdr"),
"the HDR original and marker must be durable before the OS mutation");
"the HDR original, slot, reset disposition, and marker must be durable before the OS mutation");
FakeRecoveryBackend failed_marker;
failed_marker.final_mode_marker_write_succeeds = false;
@@ -256,8 +326,12 @@ void TestModeAndHDRRestoreIndependently() {
EventIndex(backend.events, L"restore:mode") < backend.events.size() &&
EventIndex(backend.events, L"restore:hdr") < backend.events.size(),
"mode failure must not prevent the independent HDR restore");
Check(backend.dwords[kModeChanged] == 1, "the failed mode marker must remain set");
Check(backend.dwords[kHDRChanged] == 0, "the successful HDR marker must be cleared");
Check(
backend.dwords[kModeChanged] == 1 && backend.dwords[kModeTakeoverEligible] == 1,
"the failed mode marker and its takeover disposition must remain set");
Check(
backend.dwords[kHDRChanged] == 0 && backend.dwords[kHDRTakeoverEligible] == 0,
"the successful HDR marker and matching disposition must be cleared");
backend.events.clear();
backend.mode_restore_succeeds = true;
@@ -275,7 +349,9 @@ void TestFailedRestoreRemainsForTopologyRetry() {
backend.device_present[kModeDevice] = false;
Check(!DisplayModeManager::RecoverIfNeeded(backend), "an absent display must retain its marked restore");
Check(backend.dwords[kModeChanged] == 1, "an absent display must keep its operation marker");
Check(
backend.dwords[kModeChanged] == 1 && backend.dwords[kModeTakeoverEligible] == 1,
"an absent display must keep its operation marker and persist takeover eligibility");
Check(
EventIndex(backend.events, L"restore:mode") == backend.events.size(),
"an absent display must not call its restore API");
@@ -287,6 +363,9 @@ void TestFailedRestoreRemainsForTopologyRetry() {
Check(
EventIndex(backend.events, L"restore:mode") < backend.events.size(),
"the topology retry must attempt the retained restore");
Check(
!backend.RecordExists() && backend.dwords.find(kModeTakeoverEligible) == backend.dwords.end(),
"reconnect recovery must clear the marker and remove its disposition through normal cleanup");
}
void TestMarkerClearFailureRemainsRetryable() {
@@ -296,7 +375,9 @@ void TestMarkerClearFailureRemainsRetryable() {
backend.mode_marker_clear_succeeds = false;
Check(!DisplayModeManager::RecoverIfNeeded(backend), "marker persistence is part of recovery completion");
Check(backend.dwords[kModeChanged] == 1, "a failed marker clear must retain idempotent recovery evidence");
Check(
backend.dwords[kModeChanged] == 1 && backend.dwords[kModeTakeoverEligible] == 1,
"a failed marker clear must retain recovery evidence and become takeover eligible");
backend.events.clear();
backend.mode_marker_clear_succeeds = true;
@@ -304,17 +385,21 @@ void TestMarkerClearFailureRemainsRetryable() {
Check(
EventIndex(backend.events, L"restore:mode") < backend.events.size(),
"the retained marker must cause the restore to be retried");
Check(!backend.RecordExists(), "the successful retry must remove the marker and stale disposition");
}
void TestLifecycleCleanupPreservesPersistedSibling() {
FakeRecoveryBackend mode_completed;
mode_completed.SeedBoth();
mode_completed.dwords[kModeTakeoverEligible] = 1;
mode_completed.dwords[kHDRTakeoverEligible] = 1;
Check(
DisplayModeManager::CompleteRecoveryOperationForTesting(mode_completed, true),
"successful mode cleanup must durably clear its own marker");
Check(
mode_completed.dwords[kModeChanged] == 0 && mode_completed.dwords[kHDRChanged] == 1,
"mode cleanup must preserve a persisted HDR sibling even without local HDR ownership");
mode_completed.dwords[kModeChanged] == 0 && mode_completed.dwords[kModeTakeoverEligible] == 0 &&
mode_completed.dwords[kHDRChanged] == 1 && mode_completed.dwords[kHDRTakeoverEligible] == 1,
"mode cleanup must reset only its disposition while preserving a persisted HDR sibling");
Check(
mode_completed.dwords[kOriginalHDR] == 0 && mode_completed.strings[kHDRDeviceName] == kHDRDevice &&
mode_completed.delete_attempts == 0,
@@ -322,12 +407,15 @@ void TestLifecycleCleanupPreservesPersistedSibling() {
FakeRecoveryBackend hdr_failed_apply;
hdr_failed_apply.SeedBoth();
hdr_failed_apply.dwords[kModeTakeoverEligible] = 1;
hdr_failed_apply.dwords[kHDRTakeoverEligible] = 1;
Check(
DisplayModeManager::CompleteRecoveryOperationForTesting(hdr_failed_apply, false),
"failed HDR apply cleanup must durably clear its own marker");
Check(
hdr_failed_apply.dwords[kHDRChanged] == 0 && hdr_failed_apply.dwords[kModeChanged] == 1,
"HDR cleanup must preserve a persisted mode sibling even without local mode ownership");
hdr_failed_apply.dwords[kHDRChanged] == 0 && hdr_failed_apply.dwords[kHDRTakeoverEligible] == 0 &&
hdr_failed_apply.dwords[kModeChanged] == 1 && hdr_failed_apply.dwords[kModeTakeoverEligible] == 1,
"HDR cleanup must reset only its disposition while preserving a persisted mode sibling");
Check(
hdr_failed_apply.dwords[kOriginalWidth] == 3840 && hdr_failed_apply.strings[kModeDeviceName] == kModeDevice &&
hdr_failed_apply.delete_attempts == 0,
@@ -343,13 +431,18 @@ void TestReleasedLiveOperationRecoversAfterReconnect() {
!DisplayModeManager::RecoverIfNeededForTesting(backend, true, true),
"topology recovery must not take either genuinely live operation");
Check(backend.events.empty(), "live operations must not reach restore or persistence APIs");
Check(
backend.dwords.find(kModeTakeoverEligible) == backend.dwords.end() &&
backend.dwords.find(kHDRTakeoverEligible) == backend.dwords.end(),
"live operations must not acquire takeover dispositions");
Check(
!DisplayModeManager::RecoverIfNeededForTesting(backend, false, true),
"a released operation must remain marked while its target is absent");
Check(
backend.dwords[kModeChanged] == 1 && backend.dwords[kHDRChanged] == 1,
"an absent released mode and its live HDR sibling must both retain their markers");
backend.dwords[kModeChanged] == 1 && backend.dwords[kModeTakeoverEligible] == 1 &&
backend.dwords[kHDRChanged] == 1,
"an absent released mode must become eligible without changing its live HDR sibling");
backend.events.clear();
backend.device_present[kModeDevice] = true;
@@ -361,8 +454,503 @@ void TestReleasedLiveOperationRecoversAfterReconnect() {
EventIndex(backend.events, L"restore:hdr") == backend.events.size(),
"reconnect recovery must restore the released mode without stealing live HDR");
Check(
backend.dwords[kModeChanged] == 0 && backend.dwords[kHDRChanged] == 1 && backend.delete_attempts == 0,
"reconnect recovery must preserve the genuinely live sibling record");
backend.dwords[kModeChanged] == 0 && backend.dwords[kModeTakeoverEligible] == 0 &&
backend.dwords[kHDRChanged] == 1 && backend.delete_attempts == 0,
"reconnect recovery must clear its disposition while preserving the live sibling record");
}
void TestUnfailedMarkersRejectSameKindTakeover() {
FakeRecoveryBackend mode;
mode.SeedBoth();
const auto mode_dwords = mode.dwords;
const auto mode_strings = mode.strings;
Check(
!ApplyModeAfterPreparing(mode, kReplacementModeDevice, 2560, 1440, 120),
"a version-1 mode marker without a failed-recovery disposition must remain authoritative");
Check(
mode.dwords == mode_dwords && mode.strings == mode_strings && mode.events.empty(),
"rejected mode takeover must not rewrite either operation or reach the OS mutation");
FakeRecoveryBackend hdr;
hdr.SeedBoth();
hdr.dwords[kHDRTakeoverEligible] = 2;
const auto hdr_dwords = hdr.dwords;
const auto hdr_strings = hdr.strings;
Check(
!ApplyHDRAfterPreparing(hdr, kReplacementHDRDevice, true),
"only an HDR takeover disposition of exactly one may replace a valid marker");
Check(
hdr.dwords == hdr_dwords && hdr.strings == hdr_strings && hdr.events.empty(),
"rejected HDR takeover must preserve its mode sibling and avoid the OS mutation");
}
void TestAbsentModeTakeoverSurvivesRelaunchAndPreservesHDRSibling() {
FakeRecoveryBackend failed_recovery;
failed_recovery.SeedBoth();
failed_recovery.dwords[kModeTakeoverEligible] = 0;
failed_recovery.dwords[kHDRTakeoverEligible] = 1;
failed_recovery.device_present[kModeDevice] = false;
Check(
!DisplayModeManager::RecoverIfNeededForTesting(failed_recovery, false, true),
"an absent mode target must produce a retained failed recovery");
Check(
failed_recovery.dwords[kModeChanged] == 1 && failed_recovery.dwords[kModeTakeoverEligible] == 1,
"the failed non-live mode recovery must durably become takeover eligible");
Check(
failed_recovery.dwords[kHDRChanged] == 1 && failed_recovery.dwords[kHDRTakeoverEligible] == 1 &&
failed_recovery.dwords[kOriginalHDR] == 0 && failed_recovery.strings[kHDRDeviceName] == kHDRDevice,
"recording mode failure must not alter the live HDR sibling");
// A new backend represents the next process reading only persisted state.
FakeRecoveryBackend relaunched;
relaunched.dwords = failed_recovery.dwords;
relaunched.strings = failed_recovery.strings;
Check(
ApplyModeAfterPreparing(relaunched, kReplacementModeDevice, 2560, 1440, 120),
"a persisted failed mode recovery must admit a conflicting request after relaunch");
Check(
relaunched.dwords[kModeChanged] == 1 && relaunched.dwords[kModeTakeoverEligible] == 0 &&
relaunched.dwords[kModeRecoverySlot] == 1 && relaunched.dwords[kOriginalWidthAlternate] == 2560 &&
relaunched.dwords[kOriginalHeightAlternate] == 1440 &&
relaunched.dwords[kOriginalRefreshRateAlternate] == 120 &&
relaunched.strings[kModeDeviceNameAlternate] == kReplacementModeDevice,
"mode takeover must atomically select its staged original and revoke the consumed disposition");
Check(
relaunched.dwords[kOriginalWidth] == 3840 && relaunched.dwords[kOriginalHeight] == 2160 &&
relaunched.dwords[kOriginalRefreshRate] == 60 && relaunched.strings[kModeDeviceName] == kModeDevice,
"mode takeover must retain the prior originals in the inactive slot");
Check(
relaunched.dwords[kHDRChanged] == 1 && relaunched.dwords[kHDRTakeoverEligible] == 1 &&
relaunched.dwords[kOriginalHDR] == 0 && relaunched.strings[kHDRDeviceName] == kHDRDevice,
"mode takeover must preserve every persisted HDR sibling value");
Check(
EventIndex(relaunched.events, L"write:ModeTakeoverEligible=0") <
EventIndex(relaunched.events, L"write:ModeRecoverySlot=1") &&
EventIndex(relaunched.events, L"write:ModeRecoverySlot=1") < EventIndex(relaunched.events, L"os:mode") &&
EventIndex(relaunched.events, L"clear:ModeChanged") == relaunched.events.size() &&
EventIndex(relaunched.events, L"write:ModeChanged=1") == relaunched.events.size(),
"mode takeover must keep its marker authoritative and commit the staged slot immediately before mutation");
FakeRecoveryBackend recovery_relaunch;
recovery_relaunch.dwords = relaunched.dwords;
recovery_relaunch.strings = relaunched.strings;
recovery_relaunch.expected_mode_device = kReplacementModeDevice;
recovery_relaunch.expected_mode_width = 2560;
recovery_relaunch.expected_mode_height = 1440;
recovery_relaunch.expected_mode_refresh_rate = 120;
Check(
DisplayModeManager::RecoverIfNeededForTesting(recovery_relaunch, false, true),
"relaunch recovery must follow the committed mode slot");
Check(
EventIndex(recovery_relaunch.events, L"restore:mode") < recovery_relaunch.events.size(),
"the committed alternate mode original must reach recovery");
}
void TestBadModeHDRRestoreAllowsSameKindTakeover() {
FakeRecoveryBackend backend;
backend.SeedBoth();
backend.dwords[kModeTakeoverEligible] = 1;
backend.dwords[kHDRTakeoverEligible] = 0;
backend.hdr_restore_succeeds = false;
Check(
!DisplayModeManager::RecoverIfNeededForTesting(backend, true, false),
"a present HDR target whose restore fails must retain its marker");
Check(
EventIndex(backend.events, L"restore:hdr") < backend.events.size() && backend.dwords[kHDRChanged] == 1 &&
backend.dwords[kHDRTakeoverEligible] == 1,
"a failed HDR restore must durably grant only HDR takeover");
backend.events.clear();
Check(
ApplyHDRAfterPreparing(backend, kReplacementHDRDevice, true),
"a later conflicting HDR request must consume the failed-restore disposition");
Check(
backend.dwords[kHDRChanged] == 1 && backend.dwords[kHDRTakeoverEligible] == 0 &&
backend.dwords[kHDRRecoverySlot] == 1 && backend.dwords[kOriginalHDRAlternate] == 1 &&
backend.strings[kHDRDeviceNameAlternate] == kReplacementHDRDevice,
"HDR takeover must atomically select its staged replacement original");
Check(
backend.dwords[kOriginalHDR] == 0 && backend.strings[kHDRDeviceName] == kHDRDevice,
"HDR takeover must retain the prior original in the inactive slot");
Check(
backend.dwords[kModeChanged] == 1 && backend.dwords[kModeTakeoverEligible] == 1 &&
backend.dwords[kOriginalWidth] == 3840 && backend.dwords[kOriginalHeight] == 2160 &&
backend.dwords[kOriginalRefreshRate] == 60 && backend.strings[kModeDeviceName] == kModeDevice,
"HDR takeover must preserve every persisted mode sibling value");
Check(
EventIndex(backend.events, L"write:HDRTakeoverEligible=0") <
EventIndex(backend.events, L"write:HDRRecoverySlot=1") &&
EventIndex(backend.events, L"write:HDRRecoverySlot=1") < EventIndex(backend.events, L"os:hdr") &&
EventIndex(backend.events, L"clear:HDRChanged") == backend.events.size() &&
EventIndex(backend.events, L"write:HDRChanged=1") == backend.events.size(),
"HDR takeover must keep its marker authoritative and commit the staged slot immediately before mutation");
FakeRecoveryBackend recovery_relaunch;
recovery_relaunch.dwords = backend.dwords;
recovery_relaunch.strings = backend.strings;
recovery_relaunch.expected_hdr_device = kReplacementHDRDevice;
recovery_relaunch.expected_original_hdr = true;
Check(
DisplayModeManager::RecoverIfNeededForTesting(recovery_relaunch, true, false),
"relaunch recovery must follow the committed HDR slot");
Check(
EventIndex(recovery_relaunch.events, L"restore:hdr") < recovery_relaunch.events.size(),
"the committed alternate HDR original must reach recovery");
}
void TestFailedOSApplyRollsBackTakeover() {
FakeRecoveryBackend mode;
mode.SeedBoth();
mode.dwords[kModeTakeoverEligible] = 1;
mode.dwords[kHDRTakeoverEligible] = 1;
Check(
!ApplyModeAfterPreparing(mode, kReplacementModeDevice, 2560, 1440, 120, false),
"a failed mode OS apply must report failure");
Check(
mode.dwords[kModeChanged] == 1 && mode.dwords[kModeRecoverySlot] == 0 &&
mode.dwords[kModeTakeoverEligible] == 1 && mode.dwords[kModeHandoffPending] == 0 &&
mode.dwords[kOriginalWidth] == 3840 && mode.dwords[kOriginalHeight] == 2160 &&
mode.dwords[kOriginalRefreshRate] == 60 && mode.strings[kModeDeviceName] == kModeDevice &&
mode.dwords[kHDRChanged] == 1 && mode.dwords[kHDRTakeoverEligible] == 1,
"failed mode apply must restore the prior selector, eligibility, original, and sibling");
Check(
EventIndex(mode.events, L"write:ModeHandoffPending=1") < EventIndex(mode.events, L"write:ModeRecoverySlot=1") &&
EventIndex(mode.events, L"write:ModeRecoverySlot=1") < EventIndex(mode.events, L"os:mode:failed") &&
EventIndex(mode.events, L"os:mode:failed") < EventIndex(mode.events, L"write:ModeRecoverySlot=0") &&
EventIndex(mode.events, L"clear:ModeChanged") == mode.events.size(),
"mode rollback must retain both records until the failed OS apply is observed");
FakeRecoveryBackend mode_relaunch;
mode_relaunch.dwords = mode.dwords;
mode_relaunch.strings = mode.strings;
Check(
DisplayModeManager::RecoverIfNeededForTesting(mode_relaunch, false, true),
"relaunch after failed mode apply must recover the prior original");
FakeRecoveryBackend hdr;
hdr.SeedBoth();
hdr.dwords[kModeTakeoverEligible] = 1;
hdr.dwords[kHDRTakeoverEligible] = 1;
Check(!ApplyHDRAfterPreparing(hdr, kReplacementHDRDevice, true, false), "a failed HDR OS apply must report failure");
Check(
hdr.dwords[kHDRChanged] == 1 && hdr.dwords[kHDRRecoverySlot] == 0 && hdr.dwords[kHDRTakeoverEligible] == 1 &&
hdr.dwords[kHDRHandoffPending] == 0 && hdr.dwords[kOriginalHDR] == 0 &&
hdr.strings[kHDRDeviceName] == kHDRDevice && hdr.dwords[kModeChanged] == 1 &&
hdr.dwords[kModeTakeoverEligible] == 1,
"failed HDR apply must restore the prior selector, eligibility, original, and sibling");
Check(
EventIndex(hdr.events, L"write:HDRHandoffPending=1") < EventIndex(hdr.events, L"write:HDRRecoverySlot=1") &&
EventIndex(hdr.events, L"write:HDRRecoverySlot=1") < EventIndex(hdr.events, L"os:hdr:failed") &&
EventIndex(hdr.events, L"os:hdr:failed") < EventIndex(hdr.events, L"write:HDRRecoverySlot=0") &&
EventIndex(hdr.events, L"clear:HDRChanged") == hdr.events.size(),
"HDR rollback must retain both records until the failed OS apply is observed");
FakeRecoveryBackend hdr_relaunch;
hdr_relaunch.dwords = hdr.dwords;
hdr_relaunch.strings = hdr.strings;
Check(
DisplayModeManager::RecoverIfNeededForTesting(hdr_relaunch, true, false),
"relaunch after failed HDR apply must recover the prior original");
}
void TestCrashDuringTakeoverHandoffRecoversBothOriginals() {
FakeRecoveryBackend mode;
mode.SeedBoth();
mode.dwords[kModeTakeoverEligible] = 1;
mode.dwords[kHDRTakeoverEligible] = 1;
Check(
DisplayModeManager::PrepareModeRecovery(mode, kReplacementModeDevice, 2560, 1440, 120),
"mode takeover preparation must reach its pre-apply handoff");
Check(
mode.dwords[kModeChanged] == 1 && mode.dwords[kModeHandoffPending] == 1 &&
mode.dwords[kModePreviousRecoverySlot] == 0 && mode.dwords[kModeRecoverySlot] == 1,
"mode pre-apply handoff must retain both slot identities under one marker");
mode.expected_mode_restores = {{kReplacementModeDevice, 2560, 1440, 120}, {kModeDevice, 3840, 2160, 60}};
Check(
DisplayModeManager::RecoverIfNeededForTesting(mode, false, true),
"crash recovery must restore both mode originals from a pending handoff");
Check(
mode.mode_restore_attempts == 2 && mode.dwords[kModeChanged] == 0 && mode.dwords[kModeHandoffPending] == 0 &&
mode.dwords[kHDRChanged] == 1,
"mode handoff recovery must restore replacement then prior and preserve its sibling");
FakeRecoveryBackend hdr;
hdr.SeedBoth();
hdr.dwords[kModeTakeoverEligible] = 1;
hdr.dwords[kHDRTakeoverEligible] = 1;
Check(
DisplayModeManager::PrepareHDRRecovery(hdr, kReplacementHDRDevice, true),
"HDR takeover preparation must reach its pre-apply handoff");
Check(
hdr.dwords[kHDRChanged] == 1 && hdr.dwords[kHDRHandoffPending] == 1 &&
hdr.dwords[kHDRPreviousRecoverySlot] == 0 && hdr.dwords[kHDRRecoverySlot] == 1,
"HDR pre-apply handoff must retain both slot identities under one marker");
hdr.expected_hdr_restores = {{kReplacementHDRDevice, true}, {kHDRDevice, false}};
Check(
DisplayModeManager::RecoverIfNeededForTesting(hdr, true, false),
"crash recovery must restore both HDR originals from a pending handoff");
Check(
hdr.hdr_restore_attempts == 2 && hdr.dwords[kHDRChanged] == 0 && hdr.dwords[kHDRHandoffPending] == 0 &&
hdr.dwords[kModeChanged] == 1,
"HDR handoff recovery must restore replacement then prior and preserve its sibling");
}
void TestHDRReconnectRecoversBeforeTakeover() {
FakeRecoveryBackend backend;
backend.SeedBoth();
backend.dwords[kModeChanged] = 0;
backend.device_present[kHDRDevice] = false;
Check(!DisplayModeManager::RecoverIfNeeded(backend), "an absent HDR target must retain its recovery marker");
Check(
backend.dwords[kHDRChanged] == 1 && backend.dwords[kHDRTakeoverEligible] == 1 &&
EventIndex(backend.events, L"restore:hdr") == backend.events.size(),
"absent HDR recovery must become eligible without calling the restore API");
backend.events.clear();
backend.device_present[kHDRDevice] = true;
Check(
DisplayModeManager::RecoverIfNeeded(backend),
"reconnecting HDR before a conflicting request must recover the old target");
Check(
EventIndex(backend.events, L"restore:hdr") < backend.events.size() && !backend.RecordExists(),
"successful reconnect recovery must win and remove its disposition");
}
void TestEligibleSameOriginalReuseResetsDisposition() {
FakeRecoveryBackend mode;
mode.SeedBoth();
mode.dwords[kModeTakeoverEligible] = 1;
mode.dwords[kHDRTakeoverEligible] = 1;
Check(ApplyModeAfterPreparing(mode), "an eligible mode marker must remain reusable by the same original");
Check(
mode.dwords[kModeTakeoverEligible] == 0 && mode.dwords[kHDRTakeoverEligible] == 1,
"same-original mode reuse must revoke only mode takeover");
mode.events.clear();
Check(
!ApplyModeAfterPreparing(mode, kReplacementModeDevice, 2560, 1440, 120) && mode.events.empty(),
"a mismatched mode request must not steal a marker after same-original reuse");
FakeRecoveryBackend hdr;
hdr.SeedBoth();
hdr.dwords[kModeTakeoverEligible] = 1;
hdr.dwords[kHDRTakeoverEligible] = 1;
Check(ApplyHDRAfterPreparing(hdr), "an eligible HDR marker must remain reusable by the same original");
Check(
hdr.dwords[kHDRTakeoverEligible] == 0 && hdr.dwords[kModeTakeoverEligible] == 1,
"same-original HDR reuse must revoke only HDR takeover");
hdr.events.clear();
Check(
!ApplyHDRAfterPreparing(hdr, kReplacementHDRDevice, true) && hdr.events.empty(),
"a mismatched HDR request must not steal a marker after same-original reuse");
}
void TestRepeatedModeTakeoverStagesOnlyTheInactiveSlot() {
FakeRecoveryBackend backend;
backend.SeedBoth();
backend.dwords[kModeTakeoverEligible] = 1;
backend.dwords[kHDRTakeoverEligible] = 1;
Check(
ApplyModeAfterPreparing(backend, kReplacementModeDevice, 2560, 1440, 120),
"the first mode takeover must commit the alternate slot");
backend.events.clear();
backend.device_present[kReplacementModeDevice] = false;
Check(
!DisplayModeManager::RecoverIfNeededForTesting(backend, false, true),
"a later failed recovery must make the alternate mode original eligible");
Check(
backend.dwords[kModeRecoverySlot] == 1 && backend.dwords[kModeTakeoverEligible] == 1,
"the active alternate slot must remain selected after recovery failure");
backend.events.clear();
backend.device_present[kReplacementModeDevice] = true;
backend.rejected_dword_write_event = L"write:OriginalRefreshRate=75";
Check(
!ApplyModeAfterPreparing(backend, kModeDevice, 1920, 1080, 75),
"a repeated takeover must fail when staging into the primary slot fails");
Check(
backend.dwords[kModeChanged] == 1 && backend.dwords[kModeRecoverySlot] == 1 &&
backend.dwords[kOriginalWidthAlternate] == 2560 && backend.dwords[kOriginalHeightAlternate] == 1440 &&
backend.dwords[kOriginalRefreshRateAlternate] == 120 &&
backend.strings[kModeDeviceNameAlternate] == kReplacementModeDevice &&
EventIndex(backend.events, L"os:mode") == backend.events.size(),
"failed repeated staging must not overwrite or deselect the active alternate original");
FakeRecoveryBackend relaunched;
relaunched.dwords = backend.dwords;
relaunched.strings = backend.strings;
relaunched.expected_mode_device = kReplacementModeDevice;
relaunched.expected_mode_width = 2560;
relaunched.expected_mode_height = 1440;
relaunched.expected_mode_refresh_rate = 120;
Check(
DisplayModeManager::RecoverIfNeededForTesting(relaunched, false, true),
"relaunch must still recover the alternate original after repeated takeover staging fails");
}
void TestLiveMarkersCannotBecomeEligibleOrBeTakenOver() {
FakeRecoveryBackend mode;
mode.SeedBoth();
mode.dwords[kHDRChanged] = 0;
Check(
!DisplayModeManager::RecoverIfNeededForTesting(mode, true, false),
"a live mode marker must be skipped by synchronous recovery");
Check(
mode.events.empty() && mode.dwords.find(kModeTakeoverEligible) == mode.dwords.end(),
"skipping live mode recovery must not grant takeover");
Check(
!ApplyModeAfterPreparing(mode, kReplacementModeDevice, 2560, 1440, 120),
"a mismatched request must not steal a live mode marker");
FakeRecoveryBackend hdr;
hdr.SeedBoth();
hdr.dwords[kModeChanged] = 0;
Check(
!DisplayModeManager::RecoverIfNeededForTesting(hdr, false, true),
"a live HDR marker must be skipped by synchronous recovery");
Check(
hdr.events.empty() && hdr.dwords.find(kHDRTakeoverEligible) == hdr.dwords.end(),
"skipping live HDR recovery must not grant takeover");
Check(
!ApplyHDRAfterPreparing(hdr, kReplacementHDRDevice, true),
"a mismatched request must not steal a live HDR marker");
}
void TestTakeoverPersistenceFailuresRemainConservative() {
FakeRecoveryBackend failed_grant;
failed_grant.SeedBoth();
failed_grant.dwords[kHDRChanged] = 0;
failed_grant.device_present[kModeDevice] = false;
failed_grant.rejected_dword_write_event = L"write:ModeTakeoverEligible=1";
Check(
!DisplayModeManager::RecoverIfNeeded(failed_grant),
"failure to persist mode takeover eligibility must leave recovery incomplete");
Check(
failed_grant.dwords[kModeChanged] == 1 &&
failed_grant.dwords.find(kModeTakeoverEligible) == failed_grant.dwords.end(),
"a failed eligibility write must leave the old marker authoritative");
failed_grant.rejected_dword_write_event.clear();
failed_grant.events.clear();
Check(
!ApplyModeAfterPreparing(failed_grant, kReplacementModeDevice, 2560, 1440, 120) && failed_grant.events.empty(),
"an unpersisted failure must not admit takeover");
FakeRecoveryBackend failed_reuse_reset;
failed_reuse_reset.SeedBoth();
failed_reuse_reset.dwords[kHDRTakeoverEligible] = 1;
failed_reuse_reset.rejected_dword_write_event = L"write:HDRTakeoverEligible=0";
Check(!ApplyHDRAfterPreparing(failed_reuse_reset), "same-original reuse must fail when takeover cannot be reset");
Check(
failed_reuse_reset.dwords[kHDRChanged] == 1 && failed_reuse_reset.dwords[kHDRTakeoverEligible] == 1 &&
EventIndex(failed_reuse_reset.events, L"os:hdr") == failed_reuse_reset.events.size(),
"failed HDR disposition reset must retain the marker and prevent mutation");
FakeRecoveryBackend failed_mode_stage;
failed_mode_stage.SeedBoth();
failed_mode_stage.dwords[kModeTakeoverEligible] = 1;
failed_mode_stage.dwords[kHDRTakeoverEligible] = 1;
failed_mode_stage.rejected_dword_write_event = L"write:OriginalRefreshRateAlternate=120";
Check(
!ApplyModeAfterPreparing(failed_mode_stage, kReplacementModeDevice, 2560, 1440, 120),
"mode takeover must fail if a staged replacement original cannot be persisted");
Check(
failed_mode_stage.dwords[kModeChanged] == 1 && failed_mode_stage.dwords[kModeTakeoverEligible] == 1 &&
failed_mode_stage.dwords.find(kModeRecoverySlot) == failed_mode_stage.dwords.end() &&
failed_mode_stage.dwords[kOriginalWidth] == 3840 && failed_mode_stage.dwords[kOriginalHeight] == 2160 &&
failed_mode_stage.dwords[kOriginalRefreshRate] == 60 &&
failed_mode_stage.strings[kModeDeviceName] == kModeDevice && failed_mode_stage.dwords[kHDRChanged] == 1 &&
failed_mode_stage.dwords[kHDRTakeoverEligible] == 1 && failed_mode_stage.dwords[kOriginalHDR] == 0 &&
failed_mode_stage.strings[kHDRDeviceName] == kHDRDevice &&
EventIndex(failed_mode_stage.events, L"os:mode") == failed_mode_stage.events.size(),
"failed mode staging must leave the old marker and originals authoritative");
FakeRecoveryBackend failed_mode_commit;
failed_mode_commit.SeedBoth();
failed_mode_commit.dwords[kModeTakeoverEligible] = 1;
failed_mode_commit.dwords[kHDRTakeoverEligible] = 1;
failed_mode_commit.rejected_dword_write_event = L"write:ModeRecoverySlot=1";
Check(
!ApplyModeAfterPreparing(failed_mode_commit, kReplacementModeDevice, 2560, 1440, 120),
"mode takeover must fail when its atomic authority switch cannot be persisted");
Check(
failed_mode_commit.dwords[kModeChanged] == 1 && failed_mode_commit.dwords[kModeTakeoverEligible] == 1 &&
failed_mode_commit.dwords[kModeRecoverySlot] == 0 && failed_mode_commit.dwords[kModeHandoffPending] == 0 &&
failed_mode_commit.dwords[kOriginalWidth] == 3840 && failed_mode_commit.dwords[kOriginalHeight] == 2160 &&
failed_mode_commit.dwords[kOriginalRefreshRate] == 60 &&
failed_mode_commit.strings[kModeDeviceName] == kModeDevice &&
failed_mode_commit.dwords[kOriginalWidthAlternate] == 2560 &&
failed_mode_commit.strings[kModeDeviceNameAlternate] == kReplacementModeDevice &&
failed_mode_commit.dwords[kHDRChanged] == 1 && failed_mode_commit.dwords[kHDRTakeoverEligible] == 1 &&
failed_mode_commit.dwords[kOriginalHDR] == 0 && failed_mode_commit.strings[kHDRDeviceName] == kHDRDevice &&
EventIndex(failed_mode_commit.events, L"os:mode") == failed_mode_commit.events.size(),
"failed mode commit must keep the primary recovery point authoritative despite complete staging");
FakeRecoveryBackend mode_relaunch;
mode_relaunch.dwords = failed_mode_commit.dwords;
mode_relaunch.strings = failed_mode_commit.strings;
Check(
DisplayModeManager::RecoverIfNeededForTesting(mode_relaunch, false, true),
"relaunch after a failed mode commit must recover the old primary original");
Check(
EventIndex(mode_relaunch.events, L"restore:mode") < mode_relaunch.events.size(),
"failed mode authority switch must not redirect relaunch recovery to staging");
FakeRecoveryBackend failed_hdr_stage;
failed_hdr_stage.SeedBoth();
failed_hdr_stage.dwords[kModeTakeoverEligible] = 1;
failed_hdr_stage.dwords[kHDRTakeoverEligible] = 1;
failed_hdr_stage.rejected_dword_write_event = L"write:OriginalHDREnabledAlternate=1";
Check(
!ApplyHDRAfterPreparing(failed_hdr_stage, kReplacementHDRDevice, true),
"HDR takeover must fail if its staged replacement original cannot be persisted");
Check(
failed_hdr_stage.dwords[kHDRChanged] == 1 && failed_hdr_stage.dwords[kHDRTakeoverEligible] == 1 &&
failed_hdr_stage.dwords.find(kHDRRecoverySlot) == failed_hdr_stage.dwords.end() &&
failed_hdr_stage.dwords[kOriginalHDR] == 0 && failed_hdr_stage.strings[kHDRDeviceName] == kHDRDevice &&
failed_hdr_stage.dwords[kModeChanged] == 1 && failed_hdr_stage.dwords[kModeTakeoverEligible] == 1 &&
failed_hdr_stage.dwords[kOriginalWidth] == 3840 && failed_hdr_stage.dwords[kOriginalHeight] == 2160 &&
failed_hdr_stage.dwords[kOriginalRefreshRate] == 60 &&
failed_hdr_stage.strings[kModeDeviceName] == kModeDevice &&
EventIndex(failed_hdr_stage.events, L"os:hdr") == failed_hdr_stage.events.size(),
"failed HDR staging must leave the old marker and original authoritative");
FakeRecoveryBackend failed_hdr_commit;
failed_hdr_commit.SeedBoth();
failed_hdr_commit.dwords[kModeTakeoverEligible] = 1;
failed_hdr_commit.dwords[kHDRTakeoverEligible] = 1;
failed_hdr_commit.rejected_dword_write_event = L"write:HDRRecoverySlot=1";
Check(
!ApplyHDRAfterPreparing(failed_hdr_commit, kReplacementHDRDevice, true),
"HDR takeover must fail when its atomic authority switch cannot be persisted");
Check(
failed_hdr_commit.dwords[kHDRChanged] == 1 && failed_hdr_commit.dwords[kHDRTakeoverEligible] == 1 &&
failed_hdr_commit.dwords[kHDRRecoverySlot] == 0 && failed_hdr_commit.dwords[kHDRHandoffPending] == 0 &&
failed_hdr_commit.dwords[kOriginalHDR] == 0 && failed_hdr_commit.strings[kHDRDeviceName] == kHDRDevice &&
failed_hdr_commit.dwords[kOriginalHDRAlternate] == 1 &&
failed_hdr_commit.strings[kHDRDeviceNameAlternate] == kReplacementHDRDevice &&
failed_hdr_commit.dwords[kModeChanged] == 1 && failed_hdr_commit.dwords[kModeTakeoverEligible] == 1 &&
failed_hdr_commit.dwords[kOriginalWidth] == 3840 && failed_hdr_commit.dwords[kOriginalHeight] == 2160 &&
failed_hdr_commit.dwords[kOriginalRefreshRate] == 60 &&
failed_hdr_commit.strings[kModeDeviceName] == kModeDevice &&
EventIndex(failed_hdr_commit.events, L"os:hdr") == failed_hdr_commit.events.size(),
"failed HDR commit must keep the primary recovery point authoritative despite complete staging");
FakeRecoveryBackend hdr_relaunch;
hdr_relaunch.dwords = failed_hdr_commit.dwords;
hdr_relaunch.strings = failed_hdr_commit.strings;
Check(
DisplayModeManager::RecoverIfNeededForTesting(hdr_relaunch, true, false),
"relaunch after a failed HDR commit must recover the old primary original");
Check(
EventIndex(hdr_relaunch.events, L"restore:hdr") < hdr_relaunch.events.size(),
"failed HDR authority switch must not redirect relaunch recovery to staging");
}
void TestVersionlessModeRecoveryAndCleanup() {
@@ -431,7 +1019,9 @@ void TestCleanupFailureDoesNotBlockNewOverride() {
Check(
DisplayModeManager::RecoverIfNeeded(backend),
"successful restoration must complete even when stale-value deletion fails");
Check(backend.dwords[kModeChanged] == 0, "the successful restore marker must be clear");
Check(
backend.dwords[kModeChanged] == 0 && backend.dwords[kModeTakeoverEligible] == 0,
"the successful restore marker and matching disposition must be clear");
Check(backend.delete_attempts == 1, "completed recovery must make one best-effort cleanup attempt");
backend.events.clear();
@@ -456,6 +1046,16 @@ int main() {
mpv::TestMarkerClearFailureRemainsRetryable();
mpv::TestLifecycleCleanupPreservesPersistedSibling();
mpv::TestReleasedLiveOperationRecoversAfterReconnect();
mpv::TestUnfailedMarkersRejectSameKindTakeover();
mpv::TestAbsentModeTakeoverSurvivesRelaunchAndPreservesHDRSibling();
mpv::TestBadModeHDRRestoreAllowsSameKindTakeover();
mpv::TestFailedOSApplyRollsBackTakeover();
mpv::TestCrashDuringTakeoverHandoffRecoversBothOriginals();
mpv::TestHDRReconnectRecoversBeforeTakeover();
mpv::TestEligibleSameOriginalReuseResetsDisposition();
mpv::TestRepeatedModeTakeoverStagesOnlyTheInactiveSlot();
mpv::TestLiveMarkersCannotBecomeEligibleOrBeTakenOver();
mpv::TestTakeoverPersistenceFailuresRemainConservative();
mpv::TestVersionlessModeRecoveryAndCleanup();
mpv::TestVersionlessHDRRecoveryAndCleanup();
mpv::TestVersionlessModeAndHDRUseSharedDevice();
+1 -1
View File
@@ -547,7 +547,7 @@ void MpvPlayer::Dispose() {
auto cancelled = pending_requests_.CancelAll();
for (auto& callback : cancelled.status) {
callback(-1);
callback(MPV_ERROR_UNINITIALIZED);
}
for (auto& callback : cancelled.properties) {
callback(-1, "");
@@ -94,7 +94,7 @@ void TestPendingPropertyWriteFailsOnDispose() {
player.Dispose();
Check(callback_count == 1, "dispose must complete a pending property write exactly once");
Check(status < 0, "dispose must fail a pending property write");
Check(status == MPV_ERROR_UNINITIALIZED, "dispose must cancel a pending property write as uninitialized");
player.Dispose();
Check(callback_count == 1, "repeated dispose must not complete a property write twice");
+5 -3
View File
@@ -282,9 +282,11 @@ void MpvPlayerPlugin::HandleMethodCall(
if (plezy::mpv_common::SetPropertyStatusSucceeded(error)) {
(*result_ptr)->Success();
} else {
(*result_ptr)
->Error(
plezy::mpv_common::kSetPropertyFailedCode, plezy::mpv_common::SetPropertyErrorDescription(error));
const auto* error_code = plezy::mpv_common::SetPropertyErrorCode(error);
const auto description = error == MPV_ERROR_UNINITIALIZED
? std::string("Player not initialized")
: plezy::mpv_common::SetPropertyErrorDescription(error);
(*result_ptr)->Error(error_code, description);
}
});
});