Update watcher overflow detection (#2182)

Current versions of Dart SDK contain a bug: when watcher overflows on Windows it throws a synchronous exception instead of emitting an error into the stream. Newer versions of Dart SDK will fix this bug - but this
means watcher restart code needs to be updated to be prepared for this.
diff --git a/pkgs/watcher/CHANGELOG.md b/pkgs/watcher/CHANGELOG.md
index 01755b4..b4c823e 100644
--- a/pkgs/watcher/CHANGELOG.md
+++ b/pkgs/watcher/CHANGELOG.md
@@ -3,6 +3,9 @@
 - Improve handling of subdirectories: ignore `PathNotFoundException` due to
   subdirectory deletion racing with watcher internals, instead of raising
   it on the event stream.
+- Improve handling of watcher overflow on Windows: prepare for future versions
+  of SDK, which will properly forward `FileSystemException` into the stream
+  returned by the watcher.
 
 ## 1.1.3
 
diff --git a/pkgs/watcher/lib/src/directory_watcher/windows.dart b/pkgs/watcher/lib/src/directory_watcher/windows.dart
index 9b17f8d..87eca0f 100644
--- a/pkgs/watcher/lib/src/directory_watcher/windows.dart
+++ b/pkgs/watcher/lib/src/directory_watcher/windows.dart
@@ -405,35 +405,42 @@
 
   /// Start or restart the underlying [Directory.watch] stream.
   void _startWatch() {
-    // Note: "watcher closed" exceptions do not get sent over the stream
-    // returned by watch, and must be caught via a zone handler.
+    // Note: in older SDKs "watcher closed" exceptions might not get sent over
+    // the stream returned by watch, and must be caught via a zone handler.
     runZonedGuarded(
       () {
         var innerStream = Directory(path).watch(recursive: true);
         _watchSubscription = innerStream.listen(
           _onEvent,
-          onError: _eventsController.addError,
+          onError: _restartWatchOnOverflowOr(_eventsController.addError),
           onDone: _onDone,
         );
       },
-      (error, stackTrace) async {
-        if (error is FileSystemException &&
-            error.message.startsWith('Directory watcher closed unexpectedly')) {
-          // Wait to work around https://github.com/dart-lang/sdk/issues/61378.
-          // Give the VM time to reset state after the error. See the issue for
-          // more discussion of the workaround.
-          await _watchSubscription?.cancel();
-          await Future<void>.delayed(const Duration(milliseconds: 1));
-          _eventsController.addError(error, stackTrace);
-          _startWatch();
-        } else {
-          // ignore: only_throw_errors
-          throw error;
-        }
-      },
+      _restartWatchOnOverflowOr((error, stackTrace) {
+        // ignore: only_throw_errors
+        throw error;
+      }),
     );
   }
 
+  void Function(Object, StackTrace) _restartWatchOnOverflowOr(
+      void Function(Object, StackTrace) otherwise) {
+    return (Object error, StackTrace stackTrace) async {
+      if (error is FileSystemException &&
+          error.message.startsWith('Directory watcher closed unexpectedly')) {
+        // Wait to work around https://github.com/dart-lang/sdk/issues/61378.
+        // Give the VM time to reset state after the error. See the issue for
+        // more discussion of the workaround.
+        await _watchSubscription?.cancel();
+        await Future<void>.delayed(const Duration(milliseconds: 1));
+        _eventsController.addError(error, stackTrace);
+        _startWatch();
+      } else {
+        otherwise(error, stackTrace);
+      }
+    };
+  }
+
   /// Starts or restarts listing the watched directory to get an initial picture
   /// of its state.
   Future<void> _listDir() {