Bug: Hidden loading rings keep the compositor running, so Files uses 7-94% of a CPU core while idle
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 72/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- csharp, xml
- Domain
- desktop, performance
Research direction
Read src/Files.App/Styles/TabBarStyles.xaml around the loading ring and visual state, then src/Files.App/UserControls/Pane/InfoPane.xaml around PreviewLoadingIndicator. Check whether both rings deactivate while collapsed and activate when shown; inspect other indeterminate progress indicators for the same pattern. Reproduce by opening a folder, leaving Files idle, and measuring compositor thread CPU; done means the idle animation stops and CPU use falls to the reported near-zero level.
Written by the indexing model from the issue text.
Description
Description
Once any folder has been loaded, Files keeps its composition thread busy for as long as the process lives, even though nothing on screen changes: no input, another application in the foreground, window minimized. A window that has only ever shown the Home page idles at 0.3%.
The cause is two indeterminate ProgressRings that are hidden with Visibility="Collapsed" but never deactivated. After they have been shown once, their looping animation keeps the compositor scheduling frames, and on every frame the compositor re-evaluates every live expression and keyframe animation in the window. The cost therefore grows with the amount of content and the number of tabs that are alive.
Measurements
| What is open | main as is |
Rings deactivated while collapsed (experiment below) |
|---|---|---|
| Home page only | not active (process 0.3%) | |
| Empty folder | 2.5% | |
| Folder with 28 items | 6.2% | 0.0% |
C:\Windows\System32 (a screen full of rows) |
7.0-11.3% | 0.0% |
| Same, info pane enabled | 12.3-12.5% | 0.0% |
Sixty folder switches inside one tab did not increase the load; it follows what is on screen.
| State | Process CPU (one core) | Compositor thread |
|---|---|---|
| 50 s after start | 16.7% | 15.8% |
| First half hour, six 6-10 s samples | 13.3-21.9% | 13.3-21.5% |
| Minimized | 22.5% | 21.9% |
| About 70 minutes after start | 95.9% | 93.6% (847 CPU-seconds accumulated) |
| Notepad (WinUI 3, same Windows App SDK runtime), same interval | 2.5% | 0.2% |
What the compositor is doing
Kernel CPU sampling (ETW) of the Store build while idle, compositor thread only, symbols from the Microsoft symbol server:
| Share of the thread's CPU | Function |
|---|---|
| 98.5% | dwmcorei!CComposition::PreRender |
| 83.3% | dwmcorei!CExpressionManager::UpdateExpressions |
| 69.5% | dwmcorei!CBaseExpression::CalculateValue |
| 18.6% | dwmcorei!CKeyframeAnimation::CalculateValueWorker |
| 2.0% | dwmcorei!CPathGeometry::GetProperty |
| 0.4% | dwmcorei!CRenderTargetManager::RenderAndPresent |
So the thread is not drawing; it is evaluating animations every frame.
Cause
The tab header's loading ring is collapsed by default and only its Visibility is switched by the Loading visual state. IsActive is never set, so it keeps its default of True:
https://github.com/files-community/Files/blob/cbfb4ba54a89632fbceb82ea94ada60a9bae3737/src/Files.App/Styles/TabBarStyles.xaml#L166-L171
https://github.com/files-community/Files/blob/cbfb4ba54a89632fbceb82ea94ada60a9bae3737/src/Files.App/Styles/TabBarStyles.xaml#L489-L497
The state is entered on every folder load through BaseShellPage.SetLoadingIndicatorForTabs and BaseTabBar.SetLoadingIndicatorStatus, which is why a Home-only window is not affected.
The info pane's preview loading ring follows the same pattern:
https://github.com/files-community/Files/blob/cbfb4ba54a89632fbceb82ea94ada60a9bae3737/src/Files.App/UserControls/Pane/InfoPane.xaml#L216-L219
Verification
With this change applied to the dev build (an experiment, not a proposed final form), the compositor thread went to 0.0% in the cases marked in the first table:
--- a/src/Files.App/Styles/TabBarStyles.xaml
+++ b/src/Files.App/Styles/TabBarStyles.xaml
@@ -168,6 +168,7 @@
Margin="{ThemeResource TabViewItemHeaderIconMargin}"
+ IsActive="False"
Visibility="Collapsed" />
@@ -491,6 +492,7 @@
<Setter Target="LoadingIndicator.Visibility" Value="Visible" />
+ <Setter Target="LoadingIndicator.IsActive" Value="True" />
<Setter Target="IconBox.Visibility" Value="Collapsed" />
--- a/src/Files.App/UserControls/Pane/InfoPane.xaml
+++ b/src/Files.App/UserControls/Pane/InfoPane.xaml
@@ -215,6 +215,7 @@
x:Name="PreviewLoadingIndicator"
+ IsActive="False"
IsIndeterminate="True"
@@ -551,6 +552,7 @@
<Setter Target="PreviewLoadingIndicator.Visibility" Value="Visible" />
+ <Setter Target="PreviewLoadingIndicator.IsActive" Value="True" />
The tab ring alone accounted for the default-settings cases (7.0-11.3% to 0.0%); the info pane ring accounted for the rest with the pane enabled (12.5% to 0.0%).
Suggested fix
Drive IsActive together with the visibility for both rings, or use x:Load so the ring does not exist while it is not needed. It is worth checking the other progress rings and indeterminate progress bars for the same pattern.
Also observed, not yet understood
With a restored session of eight tabs and the info pane enabled, two of three starts of the experiment build still showed 17.8-18.9% on the compositor thread. In one of them the info pane's ring was visible and spinning (UI Automation reported PreviewLoadingIndicator, "Busy"), meaning the preview had not left its loading state. That looks like a separate bug in the preview loading path.
What made the Store session climb from about 20% to 94% over an hour was not isolated. More tabs and more realized content mean more animations to evaluate per frame, which is consistent with it but not verified.
PowerShell script used for the measurement (Get-ThreadCpu.ps1)
# Shows which threads of a process use CPU over a sampling window, with thread names.
# Usage: .\Get-ThreadCpu.ps1 -ProcessName Files -Seconds 10 (leave the app untouched while it runs)
param([string]$ProcessName = 'Files', [int]$Seconds = 10)
Add-Type @'
using System;
using System.Runtime.InteropServices;
public static class ThreadName
{
[DllImport("kernel32.dll")] static extern IntPtr OpenThread(uint access, bool inherit, uint tid);
[DllImport("kernel32.dll")] static extern bool CloseHandle(IntPtr h);
[DllImport("kernel32.dll")] static extern int GetThreadDescription(IntPtr h, out IntPtr desc);
[DllImport("kernel32.dll")] static extern IntPtr LocalFree(IntPtr p);
public static string Get(uint tid)
{
IntPtr h = OpenThread(0x0800 /* THREAD_QUERY_LIMITED_INFORMATION */, false, tid);
if (h == IntPtr.Zero) return "";
try
{
IntPtr d;
if (GetThreadDescription(h, out d) < 0 || d == IntPtr.Zero) return "";
string s = Marshal.PtrToStringUni(d); LocalFree(d); return s;
}
finally { CloseHandle(h); }
}
}
'@
foreach ($p in (Get-Process $ProcessName)) {
$before = @{}; foreach ($t in $p.Threads) { try { $before[$t.Id] = $t.TotalProcessorTime.TotalMilliseconds } catch {} }
$cpu0 = $p.TotalProcessorTime.TotalMilliseconds
$sw = [Diagnostics.Stopwatch]::StartNew(); Start-Sleep -Seconds $Seconds; $p.Refresh(); $ms = $sw.Elapsed.TotalMilliseconds
'{0} ({1}): {2:n1}% of one core over {3} s' -f $p.ProcessName, $p.Id, (100 * ($p.TotalProcessorTime.TotalMilliseconds - $cpu0) / $ms), $Seconds
$p.Threads | ForEach-Object {
try {
[pscustomobject]@{
ThreadId = $_.Id
Name = [ThreadName]::Get([uint32]$_.Id)
Percent = [math]::Round(100 * ($_.TotalProcessorTime.TotalMilliseconds - $before[$_.Id]) / $ms, 1)
}
} catch {}
} | Where-Object { $_.Percent -ge 0.1 } | Sort-Object Percent -Descending | Select-Object -First 8 | Format-Table -AutoSize
}
Related
- Earlier reports with the same symptom, all closed without an identified cause: #12767 ("Folder with over 1000+ files will cause high CPU usage even when idle", 2023), #18619 ("Files randomly uses 20-30% of CPU when fully closed"), #18000 (background idle CPU; the reporter still measured about 3% in September), #15408.
- No open issue about idle CPU use was found (searched on 2026-10-12).
- Performance tracking issue: #4180.
Steps To Reproduce
- Start Files and open any folder in Details layout, for example
C:\Windows\System32. Wait until it has loaded. - Switch to another application and leave Files untouched.
- In Process Explorer, open Files.exe > Threads, or run the script from the description. The thread named "DWM Compositor Thread" keeps using CPU.
Expected: no measurable CPU while nothing changes, as with a Home-only window.
Actual: 2.5-12% of a core per the table above on a fresh process, and far more in a long session.
Files Version
4.3.0.0
Windows Version
10.0.26300.9457
User ID
No response
Log File
- Dominant language
- C#
- Stars
- 46.3k
- Forks
- 3k
- Avg merge
- 2d 29m
- Merged PRs (30d)
- 27
Getting set up
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from files-community/Files
-
area - accessibility
Difficulty 2/5 1-3 hours Newbie friendliness 66/100
files-community/Files#19047 · 2 comments ·
Maintainers usually reply within 1 day
-
area - toolbar
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
files-community/Files#19011 · 1 comment ·
Maintainers usually reply within 1 day
-
area - cloud drives
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
files-community/Files#18821 · 2 comments ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
files-community/Files#18582 · 1 comment ·
Maintainers usually reply within 1 day
-
Difficulty 3/5 1-2 days Newbie friendliness 58/100
files-community/Files#19079 · 3 comments ·
Maintainers usually reply within 1 day
All issues in files-community/Files
Similar issues
-
bug 優先: 低
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
Maintainers usually reply within 1 day
-
Difficulty 1/5 Under an hour Newbie friendliness 92/100
-
.NET area: storage bug good first issue
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Larson-Logan/medal-sync#61 ·
Maintainers usually reply within 1 day
-
area-ai bug untriaged
Difficulty 2/5 1-3 hours Newbie friendliness 67/100
dotnet/extensions#7893 ·
Maintainers usually reply within 1 day
-
Difficulty 1/5 Under an hour Newbie friendliness 72/100