@@ -13,9 +13,11 @@ internal sealed class PollingDirectoryWatcher : IDirectoryWatcher
1313 private readonly DirectoryInfo _watchedDirectory ;
1414 private readonly bool _includeSubdirectories ;
1515
16- private Dictionary < string , FileMeta > _knownFiles = new ( PathUtilities . OSSpecificPathComparer ) ;
17- private Dictionary < string , FileMeta > _tempDictionary = new ( PathUtilities . OSSpecificPathComparer ) ;
18- private readonly Dictionary < string , ChangeKind > _changes = new ( PathUtilities . OSSpecificPathComparer ) ;
16+ private Dictionary < string , DateTime > _currentSnapshot = new ( PathUtilities . OSSpecificPathComparer ) ;
17+
18+ // The following are sets that are used to calculate new snapshot and cleared on eached use (pooled):
19+ private Dictionary < string , DateTime > _snapshotBuilder = new ( PathUtilities . OSSpecificPathComparer ) ;
20+ private readonly Dictionary < string , ChangeKind > _changesBuilder = new ( PathUtilities . OSSpecificPathComparer ) ;
1921
2022 private Thread _pollingThread ;
2123 private bool _raiseEvents ;
@@ -42,7 +44,7 @@ public PollingDirectoryWatcher(string watchedDirectory, bool includeSubdirectori
4244 Name = nameof ( PollingDirectoryWatcher )
4345 } ;
4446
45- CreateKnownFilesSnapshot ( ) ;
47+ CaptureInitialSnapshot ( ) ;
4648
4749 _pollingThread . Start ( ) ;
4850 }
@@ -91,67 +93,53 @@ private void PollingLoop()
9193 stopwatch . Stop ( ) ;
9294 }
9395
94- private void CreateKnownFilesSnapshot ( )
96+ private void CaptureInitialSnapshot ( )
9597 {
96- _knownFiles . Clear ( ) ;
98+ Debug . Assert ( _currentSnapshot . Count == 0 ) ;
9799
98- ForeachEntityInDirectory ( _watchedDirectory , fileInfo =>
100+ ForeachEntityInDirectory ( _watchedDirectory , ( filePath , writeTime ) =>
99101 {
100- _knownFiles . Add ( fileInfo . FullName , new FileMeta ( fileInfo , foundAgain : false ) ) ;
102+ _currentSnapshot . Add ( filePath , writeTime ) ;
101103 } ) ;
102104 }
103105
104106 private void CheckForChangedFiles ( )
105107 {
106- _changes . Clear ( ) ;
108+ Debug . Assert ( _changesBuilder . Count == 0 ) ;
109+ Debug . Assert ( _snapshotBuilder . Count == 0 ) ;
107110
108- ForeachEntityInDirectory ( _watchedDirectory , fileInfo =>
111+ ForeachEntityInDirectory ( _watchedDirectory , ( filePath , currentWriteTime ) =>
109112 {
110- var fullFilePath = fileInfo . FullName ;
111-
112- if ( _knownFiles . TryGetValue ( fullFilePath , out var fileMeta ) )
113+ if ( ! _currentSnapshot . TryGetValue ( filePath , out var snapshotWriteTime ) )
113114 {
114- try
115- {
116- if ( fileMeta . FileInfo . LastWriteTime != fileInfo . LastWriteTime )
117- {
118- // File changed
119- _changes . TryAdd ( fullFilePath , ChangeKind . Update ) ;
120- }
121-
122- _knownFiles [ fullFilePath ] = new FileMeta ( fileMeta . FileInfo , foundAgain : true ) ;
123- }
124- catch ( FileNotFoundException )
125- {
126- _knownFiles [ fullFilePath ] = new FileMeta ( fileMeta . FileInfo , foundAgain : false ) ;
127- }
115+ _changesBuilder . TryAdd ( filePath , ChangeKind . Add ) ;
128116 }
129- else
117+ else if ( snapshotWriteTime != currentWriteTime )
130118 {
131- // File added
132- _changes . TryAdd ( fullFilePath , ChangeKind . Add ) ;
119+ _changesBuilder . TryAdd ( filePath , ChangeKind . Update ) ;
133120 }
134121
135- _tempDictionary . Add ( fileInfo . FullName , new FileMeta ( fileInfo , foundAgain : false ) ) ;
122+ _snapshotBuilder . Add ( filePath , currentWriteTime ) ;
136123 } ) ;
137124
138- foreach ( var ( fullPath , fileMeta ) in _knownFiles )
125+ foreach ( var ( filePath , _ ) in _currentSnapshot )
139126 {
140- if ( ! fileMeta . FoundAgain )
127+ if ( ! _snapshotBuilder . ContainsKey ( filePath ) )
141128 {
142- // File deleted
143- _changes . TryAdd ( fullPath , ChangeKind . Delete ) ;
129+ _changesBuilder . TryAdd ( filePath , ChangeKind . Delete ) ;
144130 }
145131 }
146132
147- NotifyChanges ( ) ;
133+ NotifyChanges ( _changesBuilder ) ;
148134
149135 // Swap the two dictionaries
150- ( _tempDictionary , _knownFiles ) = ( _knownFiles , _tempDictionary ) ;
151- _tempDictionary . Clear ( ) ;
136+ ( _snapshotBuilder , _currentSnapshot ) = ( _currentSnapshot , _snapshotBuilder ) ;
137+
138+ _changesBuilder . Clear ( ) ;
139+ _snapshotBuilder . Clear ( ) ;
152140 }
153141
154- private void ForeachEntityInDirectory ( DirectoryInfo dirInfo , Action < FileSystemInfo > fileAction )
142+ private void ForeachEntityInDirectory ( DirectoryInfo dirInfo , Action < string , DateTime > fileAction )
155143 {
156144 if ( ! dirInfo . Exists )
157145 {
@@ -180,14 +168,26 @@ private void ForeachEntityInDirectory(DirectoryInfo dirInfo, Action<FileSystemIn
180168 }
181169 else
182170 {
183- fileAction ( entity ) ;
171+ string filePath ;
172+ DateTime currentWriteTime ;
173+ try
174+ {
175+ filePath = entity . FullName ;
176+ currentWriteTime = entity . LastWriteTimeUtc ;
177+ }
178+ catch ( FileNotFoundException )
179+ {
180+ continue ;
181+ }
182+
183+ fileAction ( filePath , currentWriteTime ) ;
184184 }
185185 }
186186 }
187187
188- private void NotifyChanges ( )
188+ private void NotifyChanges ( Dictionary < string , ChangeKind > changes )
189189 {
190- foreach ( var ( path , kind ) in _changes )
190+ foreach ( var ( path , kind ) in changes )
191191 {
192192 if ( _disposed || ! _raiseEvents )
193193 {
@@ -197,11 +197,5 @@ private void NotifyChanges()
197197 OnFileChange ? . Invoke ( this , new ChangedPath ( path , kind ) ) ;
198198 }
199199 }
200-
201- private readonly struct FileMeta ( FileSystemInfo fileInfo , bool foundAgain )
202- {
203- public readonly FileSystemInfo FileInfo = fileInfo ;
204- public readonly bool FoundAgain = foundAgain ;
205- }
206200 }
207201}
0 commit comments