Skip to content

Commit 688f278

Browse files
elliotttateclaude
andcommitted
perf: Make Everything folder size calculation non-blocking
- Run all folder size calculations on background threads - Add semaphore to limit concurrent calculations to 3 - Add timeout (2s) for SDK3 folder size queries - Fire and forget size calculations during enumeration - Return cached sizes immediately without blocking - Properly dispose resources (semaphore and SDK3 service) This prevents the UI from freezing when navigating folders with Everything folder size calculation enabled. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 66246aa commit 688f278

File tree

2 files changed

+66
-33
lines changed

2 files changed

+66
-33
lines changed

src/Files.App/Services/SizeProvider/EverythingSizeProvider.cs

Lines changed: 54 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public sealed class EverythingSizeProvider : ISizeProvider
1919
private readonly IEverythingSearchService everythingService;
2020
private readonly IGeneralSettingsService generalSettings;
2121
private static EverythingSdk3Service _sdk3Service;
22+
private readonly SemaphoreSlim _calculationSemaphore = new(3); // Limit concurrent calculations
2223

2324
public event EventHandler<SizeChangedEventArgs>? SizeChanged;
2425

@@ -77,45 +78,55 @@ public Task ClearAsync()
7778

7879
public async Task UpdateAsync(string path, CancellationToken cancellationToken)
7980
{
80-
await Task.Yield();
81-
82-
8381
// Return cached size immediately if available
8482
if (sizes.TryGetValue(path, out ulong cachedSize))
8583
{
8684
RaiseSizeChanged(path, cachedSize, SizeChangedValueState.Final);
87-
}
88-
else
89-
{
90-
RaiseSizeChanged(path, 0, SizeChangedValueState.None);
91-
}
92-
93-
// Check if Everything is available
94-
if (!everythingService.IsEverythingAvailable())
95-
{
96-
await FallbackCalculateAsync(path, cancellationToken);
9785
return;
9886
}
9987

100-
try
88+
// Indicate that calculation is starting
89+
RaiseSizeChanged(path, 0, SizeChangedValueState.None);
90+
91+
// Run the entire calculation on a background thread to avoid blocking
92+
await Task.Run(async () =>
10193
{
102-
// Calculate using Everything
103-
ulong totalSize = await CalculateWithEverythingAsync(path, cancellationToken);
104-
105-
if (totalSize == 0)
94+
// Limit concurrent calculations
95+
await _calculationSemaphore.WaitAsync(cancellationToken);
96+
try
10697
{
107-
// Everything returned 0, use fallback
108-
await FallbackCalculateAsync(path, cancellationToken);
109-
return;
98+
// Check if Everything is available
99+
if (!everythingService.IsEverythingAvailable())
100+
{
101+
await FallbackCalculateAsync(path, cancellationToken);
102+
return;
103+
}
104+
105+
try
106+
{
107+
// Calculate using Everything
108+
ulong totalSize = await CalculateWithEverythingAsync(path, cancellationToken);
109+
110+
if (totalSize == 0)
111+
{
112+
// Everything returned 0, use fallback
113+
await FallbackCalculateAsync(path, cancellationToken);
114+
return;
115+
}
116+
sizes[path] = totalSize;
117+
RaiseSizeChanged(path, totalSize, SizeChangedValueState.Final);
118+
}
119+
catch (Exception ex)
120+
{
121+
// Fall back to standard calculation on error
122+
await FallbackCalculateAsync(path, cancellationToken);
123+
}
110124
}
111-
sizes[path] = totalSize;
112-
RaiseSizeChanged(path, totalSize, SizeChangedValueState.Final);
113-
}
114-
catch (Exception ex)
115-
{
116-
// Fall back to standard calculation on error
117-
await FallbackCalculateAsync(path, cancellationToken);
118-
}
125+
finally
126+
{
127+
_calculationSemaphore.Release();
128+
}
129+
}, cancellationToken).ConfigureAwait(false);
119130
}
120131

121132
private async Task<ulong> CalculateWithEverythingAsync(string path, CancellationToken cancellationToken)
@@ -147,13 +158,21 @@ private async Task<ulong> CalculateWithEverythingAsync(string path, Cancellation
147158
{
148159
try
149160
{
150-
var size = await Task.Run(() => _sdk3Service.GetFolderSize(path), cancellationToken);
161+
// Use a timeout for SDK3 queries to prevent hanging
162+
using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
163+
cts.CancelAfter(TimeSpan.FromSeconds(2)); // 2 second timeout
164+
165+
var size = await Task.Run(() => _sdk3Service.GetFolderSize(path), cts.Token);
151166
if (size > 0)
152167
{
153168
App.Logger?.LogInformation($"[EverythingSizeProvider SDK3] Got folder size for {path}: {size} bytes");
154169
return size;
155170
}
156171
}
172+
catch (OperationCanceledException)
173+
{
174+
App.Logger?.LogWarning($"[EverythingSizeProvider SDK3] Timeout getting folder size for {path}");
175+
}
157176
catch (Exception ex)
158177
{
159178
App.Logger?.LogError(ex, $"[EverythingSizeProvider SDK3] Error getting folder size for {path}");
@@ -326,7 +345,11 @@ async Task<ulong> CalculateRecursive(string currentPath, CancellationToken ct, i
326345

327346
public bool TryGetSize(string path, out ulong size) => sizes.TryGetValue(path, out size);
328347

329-
public void Dispose() { }
348+
public void Dispose()
349+
{
350+
_calculationSemaphore?.Dispose();
351+
_sdk3Service?.Dispose();
352+
}
330353

331354
private void RaiseSizeChanged(string path, ulong newSize, SizeChangedValueState valueState)
332355
=> SizeChanged?.Invoke(this, new SizeChangedEventArgs(path, newSize, valueState));

src/Files.App/Utils/Storage/Enumerators/Win32StorageEnumerator.cs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,18 @@ Func<List<ListedItem>, Task> intermediateAction
7878
folder.FileSizeBytes = (long)size;
7979
folder.FileSize = size.ToSizeString();
8080
}
81-
82-
_ = folderSizeProvider.UpdateAsync(folder.ItemPath, cancellationToken);
81+
else
82+
{
83+
// Fire and forget - calculate size in background without blocking
84+
_ = Task.Run(async () =>
85+
{
86+
try
87+
{
88+
await folderSizeProvider.UpdateAsync(folder.ItemPath, cancellationToken);
89+
}
90+
catch { /* Ignore errors in background size calculation */ }
91+
});
92+
}
8393
}
8494
}
8595
}

0 commit comments

Comments
 (0)