Skip to content

Commit 8c2d0b1

Browse files
authored
Improve performance of watcher, operations and props loading (#6028)
1 parent 472cab3 commit 8c2d0b1

11 files changed

+408
-412
lines changed

Files/DataModels/FilesystemItemsOperationDataModel.cs

+13-10
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using Files.Helpers;
33
using Files.ViewModels.Dialogs;
44
using System;
5+
using System.Collections.Concurrent;
56
using System.Collections.Generic;
67
using System.Linq;
78
using System.Threading.Tasks;
@@ -59,16 +60,16 @@ public FilesystemItemsOperationDataModel(FilesystemOperationType operationType,
5960

6061
public async Task<List<FilesystemOperationItemViewModel>> ToItems(Action updatePrimaryButtonEnabled, Action optionGenerateNewName, Action optionReplaceExisting, Action optionSkip)
6162
{
62-
List<FilesystemOperationItemViewModel> items = new List<FilesystemOperationItemViewModel>();
63+
ConcurrentBag<(int Index, FilesystemOperationItemViewModel Model)> items = new ConcurrentBag<(int Index, FilesystemOperationItemViewModel Model)>();
6364

6465
List<FilesystemItemsOperationItemModel> nonConflictingItems = IncomingItems.Except(ConflictingItems).ToList();
6566

6667
// Add conflicting items first
67-
foreach (var item in ConflictingItems)
68+
await Task.WhenAll(ConflictingItems.Select(async (item, index) =>
6869
{
6970
var iconData = await FileThumbnailHelper.LoadIconFromPathAsync(item.SourcePath, 64u, Windows.Storage.FileProperties.ThumbnailMode.ListView);
7071

71-
items.Add(new FilesystemOperationItemViewModel(updatePrimaryButtonEnabled, optionGenerateNewName, optionReplaceExisting, optionSkip)
72+
items.Add((index, new FilesystemOperationItemViewModel(updatePrimaryButtonEnabled, optionGenerateNewName, optionReplaceExisting, optionSkip)
7273
{
7374
IsConflict = true,
7475
ItemIcon = iconData != null ? await iconData.ToBitmapAsync() : null,
@@ -78,15 +79,17 @@ public async Task<List<FilesystemOperationItemViewModel>> ToItems(Action updateP
7879
ConflictResolveOption = FileNameConflictResolveOptionType.GenerateNewName,
7980
ItemOperation = item.OperationType,
8081
ActionTaken = false
81-
});
82-
}
82+
}));
83+
}));
84+
85+
var baseIndex = ConflictingItems.Count;
8386

8487
// Then add non-conflicting items
85-
foreach (var item in nonConflictingItems)
88+
await Task.WhenAll(nonConflictingItems.Select(async (item, index) =>
8689
{
8790
var iconData = await FileThumbnailHelper.LoadIconFromPathAsync(item.SourcePath, 64u, Windows.Storage.FileProperties.ThumbnailMode.ListView);
8891

89-
items.Add(new FilesystemOperationItemViewModel(updatePrimaryButtonEnabled, optionGenerateNewName, optionReplaceExisting, optionSkip)
92+
items.Add((baseIndex + index, new FilesystemOperationItemViewModel(updatePrimaryButtonEnabled, optionGenerateNewName, optionReplaceExisting, optionSkip)
9093
{
9194
IsConflict = false,
9295
ItemIcon = iconData != null ? await iconData.ToBitmapAsync() : null,
@@ -96,10 +99,10 @@ public async Task<List<FilesystemOperationItemViewModel>> ToItems(Action updateP
9699
ConflictResolveOption = FileNameConflictResolveOptionType.NotAConflict,
97100
ItemOperation = item.OperationType,
98101
ActionTaken = true
99-
});
100-
}
102+
}));
103+
}));
101104

102-
return items;
105+
return items.OrderBy(i => i.Index).Select(i => i.Model).ToList();
103106
}
104107

105108
private string GetOperationIconGlyph(FilesystemOperationType operationType)

Files/Files.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,6 @@
266266
<Compile Include="Helpers\Extension.cs" />
267267
<Compile Include="Helpers\FileListCache\FileListCacheController.cs" />
268268
<Compile Include="Helpers\FileListCache\IFileListCache.cs" />
269-
<Compile Include="Helpers\FileListCache\PersistentSQLiteCacheAdapter.cs" />
270269
<Compile Include="Helpers\ExtensionManager.cs" />
271270
<Compile Include="Helpers\IntervalSampler.cs" />
272271
<Compile Include="Helpers\QuickLookHelpers.cs" />
@@ -275,6 +274,7 @@
275274
<Compile Include="Helpers\SidebarHelpers.cs" />
276275
<Compile Include="Helpers\XamlHelpers\DependencyObjectHelpers.cs" />
277276
<Compile Include="Helpers\XamlHelpers\SystemTypeToXaml.cs" />
277+
<Compile Include="Helpers\AsyncManualResetEvent.cs" />
278278
<Compile Include="IBaseLayout.cs" />
279279
<Compile Include="Interacts\BaseLayoutCommandImplementationModel.cs" />
280280
<Compile Include="Interacts\BaseLayoutCommandsViewModel.cs" />

Files/Filesystem/ListedItem.cs

+11-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
using System.Collections.ObjectModel;
1414
using System.IO;
1515
using System.Linq;
16+
using System.Threading;
1617
using Windows.Storage;
1718
using Windows.UI.Xaml.Media.Imaging;
1819

@@ -22,7 +23,16 @@ public class ListedItem : ObservableObject, IGroupableItem
2223
{
2324
public bool IsHiddenItem { get; set; } = false;
2425
public StorageItemTypes PrimaryItemAttribute { get; set; }
25-
public bool ItemPropertiesInitialized { get; set; } = false;
26+
27+
private volatile int itemPropertiesInitialized = 0;
28+
public bool ItemPropertiesInitialized
29+
{
30+
get => itemPropertiesInitialized == 1;
31+
set
32+
{
33+
Interlocked.Exchange(ref itemPropertiesInitialized, value ? 1 : 0);
34+
}
35+
}
2636

2737
public string ItemTooltipText
2838
{
+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading;
6+
using System.Threading.Tasks;
7+
8+
namespace Files.Helpers
9+
{
10+
public class AsyncManualResetEvent
11+
{
12+
private volatile TaskCompletionSource<bool> m_tcs = new TaskCompletionSource<bool>();
13+
14+
public async Task WaitAsync(CancellationToken cancellationToken = default)
15+
{
16+
var tcs = m_tcs;
17+
var cancelTcs = new TaskCompletionSource<bool>();
18+
19+
cancellationToken.Register(
20+
s => ((TaskCompletionSource<bool>)s).TrySetCanceled(), cancelTcs);
21+
22+
await await Task.WhenAny(tcs.Task, cancelTcs.Task);
23+
}
24+
25+
private async Task<bool> Delay(int milliseconds)
26+
{
27+
await Task.Delay(milliseconds);
28+
return false;
29+
}
30+
31+
public async Task<bool> WaitAsync(int milliseconds, CancellationToken cancellationToken = default)
32+
{
33+
var tcs = m_tcs;
34+
var cancelTcs = new TaskCompletionSource<bool>();
35+
36+
cancellationToken.Register(
37+
s => ((TaskCompletionSource<bool>)s).TrySetCanceled(), cancelTcs);
38+
39+
return await await Task.WhenAny(tcs.Task, cancelTcs.Task, Delay(milliseconds));
40+
}
41+
42+
public void Set()
43+
{
44+
var tcs = m_tcs;
45+
Task.Factory.StartNew(s => ((TaskCompletionSource<bool>)s).TrySetResult(true),
46+
tcs, CancellationToken.None, TaskCreationOptions.PreferFairness, TaskScheduler.Default);
47+
tcs.Task.Wait();
48+
}
49+
50+
public void Reset()
51+
{
52+
while (true)
53+
{
54+
var tcs = m_tcs;
55+
if (!tcs.Task.IsCompleted ||
56+
Interlocked.CompareExchange(ref m_tcs, new TaskCompletionSource<bool>(), tcs) == tcs)
57+
return;
58+
}
59+
}
60+
}
61+
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
using Files.Common;
2-
using System.Collections.Generic;
1+
using System.Collections.Concurrent;
32
using System.Threading;
43
using System.Threading.Tasks;
54

@@ -14,40 +13,31 @@ public static FileListCacheController GetInstance()
1413
return instance ??= new FileListCacheController();
1514
}
1615

17-
private readonly IFileListCache persistentAdapter;
18-
1916
private FileListCacheController()
2017
{
21-
persistentAdapter = new PersistentSQLiteCacheAdapter();
2218
}
2319

24-
private readonly Dictionary<string, object> fileNamesCache = new Dictionary<string, object>();
20+
private readonly ConcurrentDictionary<string, string> fileNamesCache = new ConcurrentDictionary<string, string>();
2521

26-
public async Task<string> ReadFileDisplayNameFromCache(string path, CancellationToken cancellationToken)
22+
public Task<string> ReadFileDisplayNameFromCache(string path, CancellationToken cancellationToken)
2723
{
28-
var displayName = fileNamesCache.Get(path, (string)null);
29-
if (displayName == null)
24+
if (fileNamesCache.TryGetValue(path, out var displayName))
3025
{
31-
displayName = await persistentAdapter.ReadFileDisplayNameFromCache(path, cancellationToken);
32-
if (displayName != null)
33-
{
34-
fileNamesCache[path] = displayName;
35-
}
26+
return Task.FromResult(displayName);
3627
}
37-
return displayName;
28+
29+
return Task.FromResult<string>(null);
3830
}
3931

4032
public Task SaveFileDisplayNameToCache(string path, string displayName)
4133
{
4234
if (displayName == null)
4335
{
44-
fileNamesCache.Remove(path);
45-
return persistentAdapter.SaveFileDisplayNameToCache(path, displayName);
36+
fileNamesCache.TryRemove(path, out _);
4637
}
47-
fileNamesCache[path] = displayName;
4838

49-
// save entry to persistent cache in background
50-
return persistentAdapter.SaveFileDisplayNameToCache(path, displayName);
39+
fileNamesCache[path] = displayName;
40+
return Task.CompletedTask;
5141
}
5242
}
5343
}

Files/Helpers/FileListCache/PersistentSQLiteCacheAdapter.cs

-128
This file was deleted.

0 commit comments

Comments
 (0)