diff --git a/pkgs/watcher/CHANGELOG.md b/pkgs/watcher/CHANGELOG.md index 30a19756e..fd65b4abd 100644 --- a/pkgs/watcher/CHANGELOG.md +++ b/pkgs/watcher/CHANGELOG.md @@ -1,3 +1,9 @@ +## 1.1.4-wip + +- Improve handling of subdirectories on Linux: ignore `PathNotFoundException` + due to subdirectory deletion during watch setup, instead of raising it on the + event stream. + ## 1.1.3 - Improve handling of @@ -6,7 +12,7 @@ events. But, the restart would sometimes silently fail. Now, it is more reliable. - Improving handling of directories that are created then immediately deleted on - Windows. Previously, that could cause a `PathNotfoundException` to be thrown. + Windows. Previously, that could cause a `PathNotFoundException` to be thrown. ## 1.1.2 diff --git a/pkgs/watcher/lib/src/directory_watcher/linux.dart b/pkgs/watcher/lib/src/directory_watcher/linux.dart index cb1d07781..f696a89b5 100644 --- a/pkgs/watcher/lib/src/directory_watcher/linux.dart +++ b/pkgs/watcher/lib/src/directory_watcher/linux.dart @@ -136,10 +136,17 @@ class _LinuxDirectoryWatcher // top-level clients such as barback as well, and could be implemented with // a wrapper similar to how listening/canceling works now. - // TODO(nweiz): Catch any errors here that indicate that the directory in - // question doesn't exist and silently stop watching it instead of - // propagating the errors. - var stream = Directory(path).watch(); + var stream = Directory(path).watch().transform( + StreamTransformer.fromHandlers( + handleError: (error, st, sink) { + // Directory might no longer exist at the point where we try to + // start the watcher. Simply ignore this error and let the stream + // close. + if (error is! PathNotFoundException) { + sink.addError(error, st); + } + }, + )); _subdirStreams[path] = stream; _nativeEvents.add(stream); } diff --git a/pkgs/watcher/pubspec.yaml b/pkgs/watcher/pubspec.yaml index 16af27bc2..80a8abbfd 100644 --- a/pkgs/watcher/pubspec.yaml +++ b/pkgs/watcher/pubspec.yaml @@ -1,5 +1,5 @@ name: watcher -version: 1.1.3 +version: 1.1.4-wip description: >- A file system watcher. It monitors changes to contents of directories and sends notifications when files have been added, removed, or modified. diff --git a/pkgs/watcher/test/directory_watcher/shared.dart b/pkgs/watcher/test/directory_watcher/shared.dart index 1ebc78d4b..a1d2239c6 100644 --- a/pkgs/watcher/test/directory_watcher/shared.dart +++ b/pkgs/watcher/test/directory_watcher/shared.dart @@ -1,8 +1,11 @@ // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. +import 'dart:io' as io; +import 'dart:isolate'; import 'package:test/test.dart'; +import 'package:test_descriptor/test_descriptor.dart' as d; import 'package:watcher/src/utils.dart'; import '../utils.dart'; @@ -340,5 +343,31 @@ void sharedTests() { events.add(isRemoveEvent('dir/sub')); await inAnyOrder(events); }); + + test('subdirectory watching is robust against races', () async { + // Make sandboxPath accessible to child isolates created by Isolate.run. + final sandboxPath = d.sandbox; + final dirNames = [for (var i = 0; i < 50; i++) 'dir$i']; + await startWatcher(); + + // Repeatedly create and delete subdirectories in attempt to trigger + // a race. + for (var i = 0; i < 10; i++) { + for (var dir in dirNames) { + createDir(dir); + } + await Isolate.run(() async { + await Future.wait([ + for (var dir in dirNames) + io.Directory('$sandboxPath/$dir').delete(), + ]); + }); + } + + writeFile('a/b/c/d/file.txt'); + await inAnyOrder([ + isAddEvent('a/b/c/d/file.txt'), + ]); + }); }); }