@@ -126,30 +126,34 @@ class _WindowsDirectoryWatcher
126126 // Check if [path] is already the root directory.
127127 if (FileSystemEntity .identicalSync (parent, path)) return ;
128128 var parentStream = Directory (parent).watch (recursive: false );
129- _parentWatchSubscription = parentStream.listen ((event) {
130- // Only look at events for 'directory'.
131- if (p.basename (event.path) != p.basename (absoluteDir)) return ;
132- // Test if the directory is removed. FileSystemEntity.typeSync will
133- // return NOT_FOUND if it's unable to decide upon the type, including
134- // access denied issues, which may happen when the directory is deleted.
135- // FileSystemMoveEvent and FileSystemDeleteEvent events will always mean
136- // the directory is now gone.
137- if (event is FileSystemMoveEvent ||
138- event is FileSystemDeleteEvent ||
139- (FileSystemEntity .typeSync (path) == FileSystemEntityType .notFound)) {
140- for (var path in _files.paths) {
141- _emitEvent (ChangeType .REMOVE , path);
129+ _parentWatchSubscription = parentStream.listen (
130+ (event) {
131+ // Only look at events for 'directory'.
132+ if (p.basename (event.path) != p.basename (absoluteDir)) return ;
133+ // Test if the directory is removed. FileSystemEntity.typeSync will
134+ // return NOT_FOUND if it's unable to decide upon the type, including
135+ // access denied issues, which may happen when the directory is deleted.
136+ // FileSystemMoveEvent and FileSystemDeleteEvent events will always mean
137+ // the directory is now gone.
138+ if (event is FileSystemMoveEvent ||
139+ event is FileSystemDeleteEvent ||
140+ (FileSystemEntity .typeSync (path) ==
141+ FileSystemEntityType .notFound)) {
142+ for (var path in _files.paths) {
143+ _emitEvent (ChangeType .REMOVE , path);
144+ }
145+ _files.clear ();
146+ close ();
142147 }
143- _files.clear ();
144- close ();
145- }
146- }, onError: (error) {
147- // Ignore errors, simply close the stream. The user listens on
148- // [directory], and while it can fail to listen on the parent, we may
149- // still be able to listen on the path requested.
150- _parentWatchSubscription? .cancel ();
151- _parentWatchSubscription = null ;
152- });
148+ },
149+ onError: (error) {
150+ // Ignore errors, simply close the stream. The user listens on
151+ // [directory], and while it can fail to listen on the parent, we may
152+ // still be able to listen on the path requested.
153+ _parentWatchSubscription? .cancel ();
154+ _parentWatchSubscription = null ;
155+ },
156+ );
153157 }
154158
155159 void _onEvent (FileSystemEvent event) {
@@ -225,16 +229,18 @@ class _WindowsDirectoryWatcher
225229 // Events within directories that already have events are superfluous; the
226230 // directory's full contents will be examined anyway, so we ignore such
227231 // events. Emitting them could cause useless or out-of-order events.
228- var directories = unionAll (batch.map ((event) {
229- if (! event.isDirectory) return < String > {};
230- if (event is FileSystemMoveEvent ) {
231- var destination = event.destination;
232- if (destination != null ) {
233- return {event.path, destination};
232+ var directories = unionAll (
233+ batch.map ((event) {
234+ if (! event.isDirectory) return < String > {};
235+ if (event is FileSystemMoveEvent ) {
236+ var destination = event.destination;
237+ if (destination != null ) {
238+ return {event.path, destination};
239+ }
234240 }
235- }
236- return {event.path};
237- }) );
241+ return {event.path};
242+ }),
243+ );
238244
239245 bool isInModifiedDirectory (String path) =>
240246 directories.any ((dir) => path != dir && p.isWithin (dir, path));
@@ -285,9 +291,11 @@ class _WindowsDirectoryWatcher
285291 // REMOVE; otherwise there will also be a REMOVE or CREATE event
286292 // (respectively) that will be contradictory.
287293 if (event is FileSystemModifyEvent ) continue ;
288- assert (event is FileSystemCreateEvent ||
289- event is FileSystemDeleteEvent ||
290- event is FileSystemMoveEvent );
294+ assert (
295+ event is FileSystemCreateEvent ||
296+ event is FileSystemDeleteEvent ||
297+ event is FileSystemMoveEvent ,
298+ );
291299
292300 // If we previously thought this was a MODIFY, we now consider it to be a
293301 // CREATE or REMOVE event. This is safe for the same reason as above.
@@ -297,9 +305,11 @@ class _WindowsDirectoryWatcher
297305 }
298306
299307 // A CREATE event contradicts a REMOVE event and vice versa.
300- assert (type == FileSystemEvent .create ||
301- type == FileSystemEvent .delete ||
302- type == FileSystemEvent .move);
308+ assert (
309+ type == FileSystemEvent .create ||
310+ type == FileSystemEvent .delete ||
311+ type == FileSystemEvent .move,
312+ );
303313 if (type != event.type) return null ;
304314 }
305315
@@ -383,21 +393,31 @@ class _WindowsDirectoryWatcher
383393 void _startWatch () {
384394 // Note: "watcher closed" exceptions do not get sent over the stream
385395 // returned by watch, and must be caught via a zone handler.
386- runZonedGuarded (() {
387- var innerStream = Directory (path).watch (recursive: true );
388- _watchSubscription = innerStream.listen (_onEvent,
389- onError: _eventsController.addError, onDone: _onDone);
390- }, (error, stackTrace) {
391- if (error is FileSystemException &&
392- error.message.startsWith ('Directory watcher closed unexpectedly' )) {
393- _watchSubscription? .cancel ();
394- _eventsController.addError (error, stackTrace);
395- _startWatch ();
396- } else {
397- // ignore: only_throw_errors
398- throw error;
399- }
400- });
396+ runZonedGuarded (
397+ () {
398+ var innerStream = Directory (path).watch (recursive: true );
399+ _watchSubscription = innerStream.listen (
400+ _onEvent,
401+ onError: _eventsController.addError,
402+ onDone: _onDone,
403+ );
404+ },
405+ (error, stackTrace) async {
406+ if (error is FileSystemException &&
407+ error.message.startsWith ('Directory watcher closed unexpectedly' )) {
408+ // Wait to work around https://github.com/dart-lang/sdk/issues/61378.
409+ // Give the VM time to reset state after the error. See the issue for
410+ // more discussion of the workaround.
411+ await _watchSubscription? .cancel ();
412+ await Future <void >.delayed (const Duration (milliseconds: 1 ));
413+ _eventsController.addError (error, stackTrace);
414+ _startWatch ();
415+ } else {
416+ // ignore: only_throw_errors
417+ throw error;
418+ }
419+ },
420+ );
401421 }
402422
403423 /// Starts or restarts listing the watched directory to get an initial picture
@@ -413,8 +433,12 @@ class _WindowsDirectoryWatcher
413433 if (entity is ! Directory ) _files.add (entity.path);
414434 }
415435
416- _initialListSubscription = stream.listen (handleEntity,
417- onError: _emitError, onDone: completer.complete, cancelOnError: true );
436+ _initialListSubscription = stream.listen (
437+ handleEntity,
438+ onError: _emitError,
439+ onDone: completer.complete,
440+ cancelOnError: true ,
441+ );
418442 return completer.future;
419443 }
420444
0 commit comments