Skip to content

Commit c973156

Browse files
committed
Fix occasional PathNotFoundException for transient directories on Windows.
1 parent eca3c3c commit c973156

File tree

2 files changed

+54
-5
lines changed

2 files changed

+54
-5
lines changed

pkgs/watcher/lib/src/directory_watcher/windows.dart

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class WindowsDirectoryWatcher extends ResubscribableWatcher
2121
String get directory => path;
2222

2323
WindowsDirectoryWatcher(String directory)
24-
: super(directory, () => _WindowsDirectoryWatcher(directory));
24+
: super(directory, () => _WindowsDirectoryWatcher(directory));
2525
}
2626

2727
class _EventBatcher {
@@ -169,9 +169,10 @@ class _WindowsDirectoryWatcher
169169
void _onBatch(List<FileSystemEvent> batch) {
170170
_sortEvents(batch).forEach((path, eventSet) {
171171
var canonicalEvent = _canonicalEvent(eventSet);
172-
var events = canonicalEvent == null
173-
? _eventsBasedOnFileSystem(path)
174-
: [canonicalEvent];
172+
var events =
173+
canonicalEvent == null
174+
? _eventsBasedOnFileSystem(path)
175+
: [canonicalEvent];
175176

176177
for (var event in events) {
177178
if (event is FileSystemCreateEvent) {
@@ -198,7 +199,9 @@ class _WindowsDirectoryWatcher
198199
});
199200
subscription.onError((Object e, StackTrace stackTrace) {
200201
_listSubscriptions.remove(subscription);
201-
_emitError(e, stackTrace);
202+
if (e is! PathNotFoundException) {
203+
_emitError(e, stackTrace);
204+
}
202205
});
203206
_listSubscriptions.add(subscription);
204207
} else if (event is FileSystemModifyEvent) {

pkgs/watcher/test/directory_watcher/windows_test.dart

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,52 @@ void main() {
6666
},
6767
);
6868

69+
// Regression test for https://github.com/dart-lang/tools/issues/2152:
70+
// watcher can throws if a directory is created then quickly deleted.
71+
group('Transient directory', () {
72+
late StreamSubscription<Object> subscription;
73+
late Directory temp;
74+
late Watcher watcher;
75+
late int errorsSeen;
76+
77+
setUp(() async {
78+
temp = Directory.systemTemp.createTempSync();
79+
watcher = DirectoryWatcher(temp.path);
80+
errorsSeen = 0;
81+
subscription = watcher.events.listen(
82+
(e) {},
83+
onError: (Object e, _) {
84+
print('Event stream error: $e');
85+
++errorsSeen;
86+
},
87+
);
88+
await watcher.ready;
89+
});
90+
91+
tearDown(() {
92+
subscription.cancel();
93+
});
94+
95+
test('does not break watching', () async {
96+
// Iterate creating 10 directories and deleting 1-10 of them. This means
97+
// the directories will exist for different lengths of times, exploring
98+
// possible race conditions in directory handling.
99+
for (var i = 0; i != 50; ++i) {
100+
for (var j = 0; j != 10; ++j) {
101+
File('${temp.path}\\$j\\file').createSync(recursive: true);
102+
}
103+
await Future<void>.delayed(const Duration(milliseconds: 1));
104+
for (var j = 0; j != i % 10 + 1; ++j) {
105+
final d = Directory('${temp.path}\\$j');
106+
d.deleteSync(recursive: true);
107+
}
108+
await Future<void>.delayed(const Duration(milliseconds: 1));
109+
}
110+
111+
expect(errorsSeen, 0);
112+
});
113+
});
114+
69115
// The Windows native watcher has a buffer that gets exhausted if events are
70116
// not handled quickly enough. Then, it throws an error and stops watching.
71117
// The exhaustion is reliably triggered if enough events arrive during a sync

0 commit comments

Comments
 (0)