CmdPal: Add adaptive parallel fallback processing and consistent updates (#42273)

## Summary of the Pull Request

This PR replaces sequential fallback processing with an adaptive
parallel dispatch model and isolates fallback work onto a dedicated
thread pool, preventing misbehaving extensions from starving the .NET
ThreadPool on blocked synchronous COM RPC call.

The other major change is allowing MainListPage to allow take control
over the debounce of search updates directly, reducing latency and
improving smoothness of the search.

- Adds `DedicatedThreadPool` — an elastic pool of background threads
(min 2, max 32) that expand on demand when all threads are blocked in
COM calls and shrink after 30s idle.
- Extracts all fallback dispatch machinery (adaptive workers,
per-command inflight tracking, pending-retry slots) from MainListPage
into a standalone FallbackUpdateManager.
- Prevents one fallback from monopolizing all threads by capping
concurrent in-flight calls per fallback to 4.
- Starts with low degree of parallelism (2) and gently scales up to half
of CPU cores per batch. If a fallback takes more than 200ms, another
worker is spawned so remaining commands aren't blocked.
- Adds `ThrottledDebouncedAction` to coalesce rapid `RaiseItemsChanged`
calls from fallback completions and user input (100ms for external
events, 50ms adjusted for keystrokes), replacing unbatched direct calls.
- Bypasses the UI-layer debounce timer for the main list page since it
now handles its own throttling, eliminating double-debounce latency.
- Introduces diagnostics for fallbacks and timing hidden behind feature
flags.


<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] Closes: #42286
- [x] Related to: #44407
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end-user-facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
This commit is contained in:
Jiří Polášek
2026-03-05 21:34:24 +01:00
committed by GitHub
parent f651d1a611
commit f0134e4448
10 changed files with 898 additions and 62 deletions

View File

@@ -351,17 +351,24 @@ public sealed partial class SearchBar : UserControl,
}
// TODO: We could encapsulate this in a Behavior if we wanted to bind to the Filter property.
_debounceTimer.Debounce(
() =>
{
DoFilterBoxUpdate();
},
//// Couldn't find a good recommendation/resource for value here. PT uses 50ms as default, so that is a reasonable default
//// This seems like a useful testing site for typing times: https://keyboardtester.info/keyboard-latency-test/
//// i.e. if another keyboard press comes in within 50ms of the last, we'll wait before we fire off the request
interval: TimeSpan.FromMilliseconds(50),
//// If we're not already waiting, and this is blanking out or the first character type, we'll start filtering immediately instead to appear more responsive and either clear the filter to get back home faster or at least chop to the first starting letter.
immediate: FilterBox.Text.Length <= 1);
var hasCustomDebounce = (CurrentPageViewModel as ListViewModel)?.HasCustomDebounceLogic == true;
if (hasCustomDebounce)
{
// Good, the page handles debouncing on its own
DoFilterBoxUpdate();
}
else
{
_debounceTimer.Debounce(
DoFilterBoxUpdate,
//// Couldn't find a good recommendation/resource for value here. PT uses 50ms as default, so that is a reasonable default
//// This seems like a useful testing site for typing times: https://keyboardtester.info/keyboard-latency-test/
//// i.e. if another keyboard press comes in within 50ms of the last, we'll wait before we fire off the request
interval: TimeSpan.FromMilliseconds(50),
//// If we're not already waiting, and this is blanking out or the first character type, we'll start filtering immediately
//// instead to appear more responsive and either clear the filter to get back home faster or at least chop to the first starting letter.
immediate: FilterBox.Text.Length <= 1);
}
}
private void DoFilterBoxUpdate()

View File

@@ -29,6 +29,15 @@
<UseWinRT>true</UseWinRT>
</PropertyGroup>
<!-- Defines: to quickly add project-wide define constants / feature flags -->
<PropertyGroup Condition=" '$(Configuration)' == 'DEBUG' ">
<DefineConstants>$(DefineConstants);CMDPAL_FF_EXAMPLE_FLAG</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'RELEASE' ">
<DefineConstants>$(DefineConstants)</DefineConstants>
</PropertyGroup>
<!-- For debugging purposes, uncomment this block to enable AOT builds -->
<!-- <PropertyGroup>
<EnableCmdPalAOT>true</EnableCmdPalAOT>