fix(settings-ui): release page-owned resources on navigation #50649

Open
niels9001 opened 8:13am on September 18, 2026 wants to merge 1 commit into microsoft/powertoys niels9001-stable-dashboard-shortcuts from
niels9001-settings-launch-benchmarks

Pull Request Overview

  • Opened on September 18, 2026
  • Status Open
  • Commit count 1 with first commit September 11, 2026

Total Delta

0 Total Diff Delta

Open Days

Open 1 weekdays

Test Delta

0 Diff Delta in Test Files
Breakdown by Phase

How long has this pull request spent in each phase of its lifecycle?

Fraction of total time Business days Phase
 
4.7 days Authoring 1 commit before pull request opened for review
 
0.3 days Awaiting first review

Total time for pull request still awaiting merge: 5.0 business days

Author avatar

fix(settings-ui): release page-owned resources on navigation

Summary of the Pull Request

Release Settings page-owned view models, subscriptions, callbacks and native registrations when navigating away, while preserving reload behavior and standalone Quick Access hide/show behavior.

Draft follow-up to #50581. This PR targets niels9001-stable-dashboard-shortcuts rather than main, so its net diff contains only the lifetime follow-ups. The measured candidate is the unchanged commit 3eda888f4be05b18b1ebaf03277df2b558bb0db9; baseline is #50581 at 2bdc6357aa7b135533bcb2121b1ed3cabf3c7705. Existing PRs, including #50530, are unchanged.

PR Checklist


  • [x] Communication: Discussed and reviewed with the coordinating contributor.

  • [x] Tests: Added/updated focused unit coverage; Debug and Release results are listed below. The separate isolated collection limitation remains explicitly open.

  • [x] Dev docs: Added page-lifetime guidance and a Power Display lifetime checklist.

Issue closure: N/A; this is an investigation follow-up. Localization/new binaries/installer or CI wiring/public documentation changes: N/A; no new product binaries, dependencies or user-facing strings.

Detailed Description of the Pull Request / Additional comments



  • Dashboard and shared Quick Access ownership: src/settings-ui/Settings.UI/SettingsXAML/Views/DashboardPage.xaml.cs, src/settings-ui/Settings.UI/ViewModels/DashboardViewModel.cs, src/settings-ui/Settings.UI/ViewModels/PageViewModelLifetime\1.cs, andsrc/settings-ui/Settings.UI.Controls/QuickAccess/QuickAccessViewModel.csdispose owned subscriptions/work and recreate models on page reload.src/settings-ui/QuickAccess.UI/ViewModels/LauncherViewModel.csandsrc/settings-ui/QuickAccess.UI/QuickAccessXAML/MainWindow.xaml.csrelease the shared child on window close, not hide.src/settings-ui/Settings.UI.Library/EnabledModules.csandGeneralSettings.cs` remove only the callback still owned by that instance.


  • General page: src/settings-ui/Settings.UI/SettingsXAML/Views/GeneralPage.xaml.cs and src/settings-ui/Settings.UI/ViewModels/GeneralViewModel.cs clean up repository/shortcut/IPC registrations and cancel stale delayed work. Early Loading registration and Bindings.StopTracking() prevent long-lived shared publishers from retaining old generated binding trackers and models. PageViewModelBase.cs releases the exact conflict-manager subscription and ignores disposed/queued updates.


  • Power Display: src/settings-ui/Settings.UI/Helpers/NativeEventWaiter.cs, src/settings-ui/Settings.UI/SettingsXAML/Views/PowerDisplayPage.xaml.cs and src/settings-ui/Settings.UI/ViewModels/PowerDisplayViewModel.cs own cancellable event registrations and async work, guard six dialog callers against stale generations, and reject disposed Launch/Dismiss commands. The command guards are safety changes, not a claim to eliminate the remaining command-rooted model.


  • Managed/native hook lifetime: src/common/ManagedCommon/HotkeySettingsControlHook.cs releases callback targets and the native proxy after successful disposal. src/common/interop/KeyboardHook.cpp and KeyboardHook.h retain a strong dispatch snapshot so callbacks may close registrations without invalidating active dispatch. The native IDL/export contract is unchanged.


  • Guidance: doc/devdocs/core/settings/page-lifetimes.md and doc/devdocs/modules/powerdisplay-settings-lifetime-checklist.md document ownership and validation expectations.

No R2R/AOT, hidden-window reveal state machine, Quick Access launch-policy change, trust/signature/IPC-authentication change, elevation/GPO policy change, or installer change is included. Benchmark/diagnostic adapters were removed; only reviewed product/test/docs changes are committed.

Audited partial benchmark β€” not the planned full sample

Normal Runner-backed ordinary x64 Debug processes, naturally warm filesystem caches, full Dashboard, ten General/Home plus ten Power Display/Home round trips. Both private layouts contained Runner/Settings/QA and normal dependencies but zero native utility modules; this is not shipping Release or full-module memory validation. Native process counters were sampled with fixed windows and final-10-second medians; no forced GC/capture/build occurred during primary runs.

Only two complete balanced blocks are included: n=4 fresh sessions per variant, two independent blocks. Block 2 is wholly invalid because display geometry changed in its final session; all four attempts were retained. Block 3 was not attempted. The planned n=8 per variant was not achieved. Old B2/CIM/GC/pilot/A/A/different-context data are excluded.

| Metric (per-variant medians) | Baseline A2 | Candidate C3 |
|---|---:|---:|
| Settings private resident WS, Home 0 | 93.45 MiB | 93.39 MiB |
| Settings private resident WS, after 20 | 365.77 MiB | 196.06 MiB |
| Settings private resident growth | 272.32 MiB | 102.41 MiB |
| Settings private commit, after 20 | 400.18 MiB | 218.98 MiB |
| Settings cumulative CPU, after 20 | 53.617 s | 44.617 s |
| Family private resident WS, after 20 | 381.42 MiB | 211.85 MiB |
| Family private commit, after 20 | 453.68 MiB | 272.96 MiB |

The predeclared paired-block growth contrasts (C3 minus A2) were -165.60 MiB and -169.18 MiB, median -167.39 MiB. These differ from subtracting separate per-variant medians. With only two blocks there is no robust confidence-interval or population-tail claim. This supports reduced repeated-navigation retention, not faster first-frame presentation.

Post-close differences are retained rather than hidden: family private WS medians were 15.15/15.49 MiB and summed total WS 66.54/72.42 MiB (A2/C3). Summed working sets duplicate shared pages and are not unique physical RAM; private commit is not resident RAM.

Remaining limitations

A separate normal C3 post-GC diagnostic observed Dashboard/shared QA at one each and no retained General models/General trackers after 20 visits. One Power Display model, one ShortcutControl, an empty managed hook and three WinRT callback delegates remained; the native hook projection count was zero. The PD model had a COM/native-command-to-Action root. Native owner, permanence and disposed primitive state were not established from the graph.

The isolated real-WinUI whole-tracker criterion remains FAIL: empty native-callback-rooted General trackers survived, although old General model/page/binding weak survivors were zero and publisher listeners/caches were cleared. This draft does not claim zero leaks, full native/control cleanup, or that all remaining memory growth is native caches.

After the display interruption, narrow guarded recovery preserved the new placement, restored the original runtime/profile/task state, and removed private rollback data only after verification. No raw settings, task XML, heap dumps, screenshots or session artifacts are uploaded with this PR.

Validation Steps Performed


  • Existing repository build wrappers: ordinary x64 Debug and Release builds completed for the combined reviewed changes.


  • Each configuration: 134 focused Settings tests, 19 managed-hook tests and 2 native no-hook tests passed (155 total). Coverage includes page reload/disposal, shared Quick Access subscriptions, General compiled binding tracker teardown, Power Display cancellation/dialog generations/disposed commands, managed/native cycle release and reentrant native dispatch ownership. Unit validation did not install global keyboard hooks or inject keys.

  • Test paths include src/settings-ui/Settings.UI.UnitTests/Helpers/NativeEventWaiterTests.cs, the lifetime/compiled-binding/Power Display cases in src/settings-ui/Settings.UI.UnitTests/ViewModelTests/, src/common/interop/interop-tests/HotkeySettingsControlHookTests.cs, and src/common/UnitTests-CommonUtils/KeyboardHook.Tests.cpp.

  • Separate actual WinUI diagnostics covered same-instance page reload, 18 Power Display dialog boundary cases, and standalone QA hide/show/close. The known whole-tracker failure is documented above; these are not an unqualified UI-suite pass.

  • Separate normal A2/C3 Dashboard captures were byte-identical at the accepted context. Primary runs were uninstrumented apart from an external native counter observer.

  • All eight accepted primary sessions passed 20-trip/40-leg workload checks, ten steady batches per phase, exact family 2/3/2, zero missing counters, 1,557 strict geometry observations, timing guards and graceful shutdown. Native query brackets were at most 30.073 ms and sample gaps at most 1,016.239 ms. Observer/controller CPU was recorded separately and excluded from the PowerToys family.

  • All frozen participant hashes and unchanged common/runtime/resource inputs were checked. No new validation run or source rewrite was performed solely for publication.

CI and wider Release/multi-module validation remain pending draft review.

Comments Threads Pending Resolution

Resolved Comment Threads

No resolved comments have been left on this PR.