Skip to content

Commit db0a50e

Browse files
authored
Correctly cancel all directory watchers (#7900)
1 parent 7e03306 commit db0a50e

File tree

1 file changed

+40
-37
lines changed

1 file changed

+40
-37
lines changed

src/Files/ViewModels/ItemViewModel.cs

Lines changed: 40 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,7 @@ public class ItemViewModel : ObservableObject, IDisposable
5151
private readonly ConcurrentQueue<(uint Action, string FileName)> operationQueue;
5252
private readonly ConcurrentDictionary<string, bool> itemLoadQueue;
5353
private readonly AsyncManualResetEvent operationEvent;
54-
private IntPtr hWatchDir;
55-
private IAsyncAction aWatcherAction;
54+
private IAsyncAction aProcessQueueAction;
5655

5756
// files and folders list for manipulating
5857
private List<ListedItem> filesAndFolders;
@@ -68,7 +67,7 @@ public class ItemViewModel : ObservableObject, IDisposable
6867
private bool shouldDisplayFileExtensions = false;
6968
public ListedItem CurrentFolder { get; private set; }
7069
public CollectionViewSource viewSource;
71-
private CancellationTokenSource addFilesCTS, semaphoreCTS, loadPropsCTS;
70+
private CancellationTokenSource addFilesCTS, semaphoreCTS, loadPropsCTS, watcherCTS;
7271

7372
public event EventHandler DirectoryInfoUpdated;
7473

@@ -359,6 +358,7 @@ public ItemViewModel(FolderSettingsViewModel folderSettingsViewModel)
359358
addFilesCTS = new CancellationTokenSource();
360359
semaphoreCTS = new CancellationTokenSource();
361360
loadPropsCTS = new CancellationTokenSource();
361+
watcherCTS = new CancellationTokenSource();
362362
operationEvent = new AsyncManualResetEvent();
363363
enumFolderSemaphore = new SemaphoreSlim(1, 1);
364364
shouldDisplayFileExtensions = UserSettingsService.PreferencesSettingsService.ShowFileExtensions;
@@ -1338,25 +1338,14 @@ private void AssignDefaultIcons()
13381338

13391339
public void CloseWatcher()
13401340
{
1341-
if (aWatcherAction != null)
1341+
if (aProcessQueueAction != null)
13421342
{
1343-
aWatcherAction?.Cancel();
1344-
1345-
if (aWatcherAction.Status != AsyncStatus.Started)
1346-
{
1347-
aWatcherAction = null; // Prevent duplicate execution of this block
1348-
Debug.WriteLine("watcher canceled");
1349-
CancelIoEx(hWatchDir, IntPtr.Zero);
1350-
Debug.WriteLine("watcher handle closed");
1351-
CloseHandle(hWatchDir);
1352-
}
1353-
}
1354-
if (watchedItemsOperation != null)
1355-
{
1356-
itemQueryResult.ContentsChanged -= ItemQueryResult_ContentsChanged;
1357-
watchedItemsOperation?.Cancel();
1358-
watchedItemsOperation = null;
1343+
aProcessQueueAction?.Cancel();
1344+
aProcessQueueAction = null; // Prevent duplicate execution of this block
1345+
Debug.WriteLine("process queue canceled");
13591346
}
1347+
watcherCTS?.Cancel();
1348+
watcherCTS = new CancellationTokenSource();
13601349
}
13611350

13621351
public async Task EnumerateItemsFromSpecialFolderAsync(string path)
@@ -1743,8 +1732,6 @@ private async Task<CloudDriveSyncStatus> CheckCloudDriveSyncStatusAsync(IStorage
17431732

17441733
private StorageItemQueryResult itemQueryResult;
17451734

1746-
private IAsyncOperation<IReadOnlyList<IStorageItem>> watchedItemsOperation;
1747-
17481735
private async void WatchForStorageFolderChanges(BaseStorageFolder rootFolder)
17491736
{
17501737
if (rootFolder == null)
@@ -1764,7 +1751,12 @@ await Task.Run(() =>
17641751
{
17651752
itemQueryResult = rootFolder.CreateItemQueryWithOptions(options).ToStorageItemQueryResult();
17661753
itemQueryResult.ContentsChanged += ItemQueryResult_ContentsChanged;
1767-
watchedItemsOperation = itemQueryResult.GetItemsAsync(0, 1); // Just get one item to start getting notifications
1754+
var watchedItemsOperation = itemQueryResult.GetItemsAsync(0, 1); // Just get one item to start getting notifications
1755+
watcherCTS.Token.Register(() =>
1756+
{
1757+
itemQueryResult.ContentsChanged -= ItemQueryResult_ContentsChanged;
1758+
watchedItemsOperation?.Cancel();
1759+
});
17681760
}
17691761
});
17701762
}
@@ -1790,8 +1782,8 @@ await CoreApplication.MainView.DispatcherQueue.EnqueueAsync(() =>
17901782

17911783
private void WatchForDirectoryChanges(string path, CloudDriveSyncStatus syncStatus)
17921784
{
1793-
Debug.WriteLine("WatchForDirectoryChanges: {0}", path);
1794-
hWatchDir = NativeFileOperationsHelper.CreateFileFromApp(path, 1, 1 | 2 | 4,
1785+
Debug.WriteLine($"WatchForDirectoryChanges: {path}");
1786+
var hWatchDir = NativeFileOperationsHelper.CreateFileFromApp(path, 1, 1 | 2 | 4,
17951787
IntPtr.Zero, 3, (uint)NativeFileOperationsHelper.File_Attributes.BackupSemantics | (uint)NativeFileOperationsHelper.File_Attributes.Overlapped, IntPtr.Zero);
17961788
if (hWatchDir.ToInt64() == -1)
17971789
{
@@ -1800,10 +1792,12 @@ private void WatchForDirectoryChanges(string path, CloudDriveSyncStatus syncStat
18001792

18011793
var hasSyncStatus = syncStatus != CloudDriveSyncStatus.NotSynced && syncStatus != CloudDriveSyncStatus.Unknown;
18021794

1803-
var cts = new CancellationTokenSource();
1804-
_ = Windows.System.Threading.ThreadPool.RunAsync((x) => ProcessOperationQueue(cts.Token, hasSyncStatus));
1795+
if (aProcessQueueAction == null) // Only start one ProcessOperationQueue
1796+
{
1797+
aProcessQueueAction = Windows.System.Threading.ThreadPool.RunAsync((x) => ProcessOperationQueue(x, hasSyncStatus));
1798+
}
18051799

1806-
aWatcherAction = Windows.System.Threading.ThreadPool.RunAsync((x) =>
1800+
var aWatcherAction = Windows.System.Threading.ThreadPool.RunAsync((x) =>
18071801
{
18081802
byte[] buff = new byte[4096];
18091803
var rand = Guid.NewGuid();
@@ -1883,14 +1877,23 @@ private void WatchForDirectoryChanges(string path, CloudDriveSyncStatus syncStat
18831877
}
18841878
CloseHandle(overlapped.hEvent);
18851879
operationQueue.Clear();
1886-
cts.Cancel();
18871880
Debug.WriteLine("aWatcherAction done: {0}", rand);
18881881
});
18891882

1890-
Debug.WriteLine("Task exiting...");
1883+
watcherCTS.Token.Register(() =>
1884+
{
1885+
if (aWatcherAction != null)
1886+
{
1887+
aWatcherAction?.Cancel();
1888+
aWatcherAction = null; // Prevent duplicate execution of this block
1889+
Debug.WriteLine("watcher canceled");
1890+
}
1891+
CancelIoEx(hWatchDir, IntPtr.Zero);
1892+
CloseHandle(hWatchDir);
1893+
});
18911894
}
18921895

1893-
private async void ProcessOperationQueue(CancellationToken cancellationToken, bool hasSyncStatus)
1896+
private async void ProcessOperationQueue(IAsyncAction action, bool hasSyncStatus)
18941897
{
18951898
ApplicationDataContainer localSettings = ApplicationData.Current.LocalSettings;
18961899
string returnformat = Enum.Parse<TimeStyle>(localSettings.Values[Constants.LocalSettings.DateTimeFormat].ToString()) == TimeStyle.Application ? "D" : "g";
@@ -1910,15 +1913,15 @@ private async void ProcessOperationQueue(CancellationToken cancellationToken, bo
19101913

19111914
try
19121915
{
1913-
while (!cancellationToken.IsCancellationRequested)
1916+
while (action.Status != AsyncStatus.Canceled)
19141917
{
1915-
if (await operationEvent.WaitAsync(200, cancellationToken))
1918+
if (await operationEvent.WaitAsync(200))
19161919
{
19171920
operationEvent.Reset();
19181921

19191922
while (operationQueue.TryDequeue(out var operation))
19201923
{
1921-
if (cancellationToken.IsCancellationRequested) break;
1924+
if (action.Status == AsyncStatus.Canceled) break;
19221925
try
19231926
{
19241927
switch (operation.Action)
@@ -1927,7 +1930,7 @@ private async void ProcessOperationQueue(CancellationToken cancellationToken, bo
19271930
lastItemAdded = await AddFileOrFolderAsync(operation.FileName, returnformat);
19281931
if (lastItemAdded != null)
19291932
{
1930-
anyEdits = true;
1933+
anyEdits = true;
19311934
}
19321935
break;
19331936

@@ -1992,8 +1995,8 @@ private async void ProcessOperationQueue(CancellationToken cancellationToken, bo
19921995
{
19931996
await OrderFilesAndFoldersAsync();
19941997
await ApplyFilesAndFoldersChangesAsync();
1995-
if (lastItemAdded != null)
1996-
{
1998+
if (lastItemAdded != null)
1999+
{
19972000
await NotifyListedItemAddedAsync(lastItemAdded);
19982001
}
19992002
anyEdits = false;

0 commit comments

Comments
 (0)