Skip to content

fix: serialize Performance tab system-mutations through the operation lock#1432

Merged
laurentiu021 merged 1 commit into
mainfrom
fix/performance-vm-operation-lock
Jul 10, 2026
Merged

fix: serialize Performance tab system-mutations through the operation lock#1432
laurentiu021 merged 1 commit into
mainfrom
fix/performance-vm-operation-lock

Conversation

@laurentiu021

Copy link
Copy Markdown
Owner

Problem

Every system-mutating command on the Performance tab is async and awaits real system work (registry writes, powercfg, EmptyWorkingSet across all processes), but nothing serialized them against each other. The snapshot's SemaphoreSlim guards only the load-modify of the _snapshot field — not the full apply→revert sequence.

Worst case: pressing Restore All while an Apply's registry write was still in flight let Restore All null _snapshot and delete the persisted baseline, leaving a tweak applied with nothing to revert it. Other tabs that mutate the same system state (Tweaks, Cleanup's SFC/DISM) could interleave too.

Fix

All ten mutating commands (Apply power plan / visual effects / Game Mode / Xbox Game Bar / GPU / processor state, plus Restore All, Trim RAM, Create Restore Point, Toggle Hibernation) now acquire the app-wide OperationLockService SystemModification lock — the same lock SFC/DISM and the Environment Variables editor already use — immediately after the confirmation dialog. If another system-modification operation holds it, the command reports Cannot start — <op> is already running. and bails before touching the system. Toggle-based commands re-sync from the live profile on that early return so the UI never shows an unapplied change.

This also aligns the tab with its sibling EnvironmentVariablesViewModel, which already wraps every mutation in the same guard.

Tests

Three regression tests pin the guard (dialog stubbed to Yes + SystemModification lock held → command bails at the guard, snapshot preserved), mirroring ShortcutCleanerViewModelTests.DeleteSelected_WhenDiskLocked_DoesNotDelete:

  • RestoreAll_WhenSystemModificationLocked_BailsAtGuard (asserts _snapshot survives)
  • TrimRam_WhenSystemModificationLocked_BailsAtGuard
  • ApplyPowerPlan_WhenSystemModificationLocked_BailsAtGuard

Regression test red before fix / green after: without the guard the commands proceed past the (absent) lock check and set a success/other status — Assert.Contains("already running") fails; with the guard they short-circuit and it passes. (dotnet test is not run locally; verified via CI.)

Regression sweep

  • dotnet build main + tests: 0 warnings / 0 errors.
  • Leak-term + debug-code scan on both touched files: clean.
  • Author headers intact; version bumped 1.52.96 → 1.52.97; CHANGELOG entry in this PR.

… lock

Every mutating command on the Performance tab (Apply power plan / visual
effects / Game Mode / Xbox Game Bar / GPU / processor state, plus Restore
All, Trim RAM, Create Restore Point, Toggle Hibernation) is async and awaits
real system work, but nothing serialized them against each other. In the
worst case, pressing Restore All while an Apply's registry write was still in
flight let Restore All null the in-memory snapshot and delete the persisted
baseline, leaving a tweak applied with nothing to revert it.

Each of these ten commands now acquires the app-wide OperationLockService
SystemModification lock (the same lock SFC/DISM and the Environment Variables
editor use) immediately after its confirmation dialog. If another
system-modification operation is running, the command reports the contention
and bails before touching the system. Toggle-based commands re-sync from the
live profile on that early return so the UI never shows an unapplied change.

Adds regression tests pinning the guard on Restore All, Trim RAM and Apply
power plan (dialog stubbed to Yes + lock held -> command bails at the guard,
snapshot preserved).
@laurentiu021 laurentiu021 merged commit 6fb3f01 into main Jul 10, 2026
4 checks passed
@laurentiu021 laurentiu021 deleted the fix/performance-vm-operation-lock branch July 10, 2026 23:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant