Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1)

Side by Side Diff: pkg/watcher/lib/src/directory_watcher/windows.dart

Issue 313353003: Clean up the Windows watcher. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 // TODO(rnystrom): Merge with mac_os version. 4 // TODO(rnystrom): Merge with mac_os version.
5 5
6 library watcher.directory_watcher.windows; 6 library watcher.directory_watcher.windows;
7 7
8 import 'dart:async'; 8 import 'dart:async';
9 import 'dart:collection'; 9 import 'dart:collection';
10 import 'dart:io'; 10 import 'dart:io';
(...skipping 10 matching lines...) Expand all
21 class WindowsDirectoryWatcher extends ResubscribableDirectoryWatcher { 21 class WindowsDirectoryWatcher extends ResubscribableDirectoryWatcher {
22 WindowsDirectoryWatcher(String directory) 22 WindowsDirectoryWatcher(String directory)
23 : super(directory, () => new _WindowsDirectoryWatcher(directory)); 23 : super(directory, () => new _WindowsDirectoryWatcher(directory));
24 } 24 }
25 25
26 class _EventBatcher { 26 class _EventBatcher {
27 static const Duration _BATCH_DELAY = const Duration(milliseconds: 100); 27 static const Duration _BATCH_DELAY = const Duration(milliseconds: 100);
28 final List<FileSystemEvent> events = []; 28 final List<FileSystemEvent> events = [];
29 Timer timer; 29 Timer timer;
30 30
31 void addEvent(FileSystemEvent event) { 31 void addEvent(FileSystemEvent event, void callback()) {
32 events.add(event); 32 events.add(event);
33 }
34
35 void startTimer(void callback()) {
36 if (timer != null) { 33 if (timer != null) {
37 timer.cancel(); 34 timer.cancel();
38 } 35 }
39 timer = new Timer(_BATCH_DELAY, callback); 36 timer = new Timer(_BATCH_DELAY, callback);
40 } 37 }
41 38
42 void cancelTimer() { 39 void cancelTimer() {
43 timer.cancel(); 40 timer.cancel();
44 } 41 }
45 } 42 }
(...skipping 25 matching lines...) Expand all
71 68
72 /// The subscription to the stream returned by [Directory.watch] of the 69 /// The subscription to the stream returned by [Directory.watch] of the
73 /// parent directory to [directory]. This is needed to detect changes to 70 /// parent directory to [directory]. This is needed to detect changes to
74 /// [directory], as they are not included on Windows. 71 /// [directory], as they are not included on Windows.
75 StreamSubscription<FileSystemEvent> _parentWatchSubscription; 72 StreamSubscription<FileSystemEvent> _parentWatchSubscription;
76 73
77 /// The subscription to the [Directory.list] call for the initial listing of 74 /// The subscription to the [Directory.list] call for the initial listing of
78 /// the directory to determine its initial state. 75 /// the directory to determine its initial state.
79 StreamSubscription<FileSystemEntity> _initialListSubscription; 76 StreamSubscription<FileSystemEntity> _initialListSubscription;
80 77
81 /// The subscriptions to the [Directory.list] call for listing the contents of 78 /// The subscriptions to the [Directory.list] calls for listing the contents
82 /// subdirectories that was moved into the watched directory. 79 /// of subdirectories that were moved into the watched directory.
83 final Set<StreamSubscription<FileSystemEntity>> _listSubscriptions 80 final Set<StreamSubscription<FileSystemEntity>> _listSubscriptions
84 = new HashSet<StreamSubscription<FileSystemEntity>>(); 81 = new HashSet<StreamSubscription<FileSystemEntity>>();
85 82
86 _WindowsDirectoryWatcher(String directory) 83 _WindowsDirectoryWatcher(String directory)
87 : directory = directory, _files = new PathSet(directory) { 84 : directory = directory, _files = new PathSet(directory) {
88 _startWatch();
89 _startParentWatcher();
90
91 // Before we're ready to emit events, wait for [_listDir] to complete. 85 // Before we're ready to emit events, wait for [_listDir] to complete.
92 _listDir().then(_readyCompleter.complete); 86 _listDir().then((_) {
87 _startWatch();
88 _startParentWatcher();
89 _readyCompleter.complete();
90 });
93 } 91 }
94 92
95 void close() { 93 void close() {
96 if (_watchSubscription != null) _watchSubscription.cancel(); 94 if (_watchSubscription != null) _watchSubscription.cancel();
97 if (_parentWatchSubscription != null) _parentWatchSubscription.cancel(); 95 if (_parentWatchSubscription != null) _parentWatchSubscription.cancel();
98 if (_initialListSubscription != null) _initialListSubscription.cancel(); 96 if (_initialListSubscription != null) _initialListSubscription.cancel();
99 for (var sub in _listSubscriptions) { 97 for (var sub in _listSubscriptions) {
100 sub.cancel(); 98 sub.cancel();
101 } 99 }
102 _listSubscriptions.clear(); 100 _listSubscriptions.clear();
103 for (var batcher in _eventBatchers.values) { 101 for (var batcher in _eventBatchers.values) {
104 batcher.cancelTimer(); 102 batcher.cancelTimer();
105 } 103 }
106 _eventBatchers.clear(); 104 _eventBatchers.clear();
107 _watchSubscription = null; 105 _watchSubscription = null;
108 _parentWatchSubscription = null; 106 _parentWatchSubscription = null;
109 _initialListSubscription = null; 107 _initialListSubscription = null;
110 _eventsController.close(); 108 _eventsController.close();
111 } 109 }
112 110
113 /// On Windows, if [directory] is deleted, we will not receive any event. 111 /// On Windows, if [directory] is deleted, we will not receive any event.
112 ///
114 /// Instead, we add a watcher on the parent folder (if any), that can notify 113 /// Instead, we add a watcher on the parent folder (if any), that can notify
115 /// us about [directory]. 114 /// us about [directory]. This also includes events such as moves.
116 /// This also includes events such as moves.
117 void _startParentWatcher() { 115 void _startParentWatcher() {
118 var absoluteDir = p.absolute(directory); 116 var absoluteDir = p.absolute(directory);
119 var parent = p.dirname(absoluteDir); 117 var parent = p.dirname(absoluteDir);
120 // Check if we [directory] is already the root directory. 118 // Check if [directory] is already the root directory.
121 if (FileSystemEntity.identicalSync(parent, directory)) return; 119 if (FileSystemEntity.identicalSync(parent, directory)) return;
122 var parentStream = Chain.track( 120 var parentStream = Chain.track(
123 new Directory(parent).watch(recursive: false)); 121 new Directory(parent).watch(recursive: false));
124 _parentWatchSubscription = parentStream.listen((event) { 122 _parentWatchSubscription = parentStream.listen((event) {
125 // Only look at events for 'directory'. 123 // Only look at events for 'directory'.
126 if (p.basename(event.path) != p.basename(absoluteDir)) return; 124 if (p.basename(event.path) != p.basename(absoluteDir)) return;
127 // Test if the directory is removed. FileSystemEntity.typeSync will 125 // Test if the directory is removed. FileSystemEntity.typeSync will
128 // return NOT_FOUND if it's unable to decide upon the type, including 126 // return NOT_FOUND if it's unable to decide upon the type, including
129 // access denied issues, which may happen when the directory is deleted. 127 // access denied issues, which may happen when the directory is deleted.
130 // FileSystemMoveEvent and FileSystemDeleteEvent events will always mean 128 // FileSystemMoveEvent and FileSystemDeleteEvent events will always mean
131 // the directory is now gone. 129 // the directory is now gone.
132 if (event is FileSystemMoveEvent || 130 if (event is FileSystemMoveEvent ||
133 event is FileSystemDeleteEvent || 131 event is FileSystemDeleteEvent ||
134 (FileSystemEntity.typeSync(directory) == 132 (FileSystemEntity.typeSync(directory) ==
135 FileSystemEntityType.NOT_FOUND)) { 133 FileSystemEntityType.NOT_FOUND)) {
136 for (var path in _files.toSet()) { 134 for (var path in _files.toSet()) {
137 _emitEvent(ChangeType.REMOVE, path); 135 _emitEvent(ChangeType.REMOVE, path);
138 } 136 }
139 _files.clear(); 137 _files.clear();
140 close(); 138 close();
141 } 139 }
142 }, onError: (error) { 140 }, onError: (error) {
143 // Ignore errors, simply close the stream. 141 // Ignore errors, simply close the stream. The user listens on
142 // [directory], and while it can fail to listen on the parent, we may
143 // still be able to listen on the path requested.
144 _parentWatchSubscription.cancel(); 144 _parentWatchSubscription.cancel();
145 _parentWatchSubscription = null; 145 _parentWatchSubscription = null;
146 }); 146 });
147 } 147 }
148 148
149 void _onEvent(FileSystemEvent event) { 149 void _onEvent(FileSystemEvent event) {
150 // If we get a event before we're ready to begin emitting events, 150 assert(isReady);
151 // ignore those events and re-list the directory. 151 final batcher = _eventBatchers.putIfAbsent(
152 if (!isReady) {
153 _listDir().then((_) {
154 _readyCompleter.complete();
155 });
156 return;
157 }
158
159 _EventBatcher batcher = _eventBatchers.putIfAbsent(
160 event.path, () => new _EventBatcher()); 152 event.path, () => new _EventBatcher());
161 batcher.addEvent(event); 153 batcher.addEvent(event, () {
162 batcher.startTimer(() {
163 _eventBatchers.remove(event.path); 154 _eventBatchers.remove(event.path);
164 _onBatch(batcher.events); 155 _onBatch(batcher.events);
165 }); 156 });
166 } 157 }
167 158
168 /// The callback that's run when [Directory.watch] emits a batch of events. 159 /// The callback that's run when [Directory.watch] emits a batch of events.
169 void _onBatch(List<FileSystemEvent> batch) { 160 void _onBatch(List<FileSystemEvent> batch) {
170 _sortEvents(batch).forEach((path, events) { 161 _sortEvents(batch).forEach((path, events) {
171 var relativePath = p.relative(path, from: directory); 162 var relativePath = p.relative(path, from: directory);
172 163
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
239 directories.any((dir) => path != dir && path.startsWith(dir)); 230 directories.any((dir) => path != dir && path.startsWith(dir));
240 231
241 addEvent(path, event) { 232 addEvent(path, event) {
242 if (isInModifiedDirectory(path)) return; 233 if (isInModifiedDirectory(path)) return;
243 var set = eventsForPaths.putIfAbsent(path, () => new Set()); 234 var set = eventsForPaths.putIfAbsent(path, () => new Set());
244 set.add(event); 235 set.add(event);
245 } 236 }
246 237
247 for (var event in batch) { 238 for (var event in batch) {
248 if (event is FileSystemMoveEvent) { 239 if (event is FileSystemMoveEvent) {
249 FileSystemMoveEvent moveEvent = event; 240 addEvent(event.destination, event);
250 addEvent(moveEvent.destination, event);
251 } 241 }
252 addEvent(event.path, event); 242 addEvent(event.path, event);
253 } 243 }
254 244
255 return eventsForPaths; 245 return eventsForPaths;
256 } 246 }
257 247
258 /// Returns the canonical event from a batch of events on the same path, if 248 /// Returns the canonical event from a batch of events on the same path, if
259 /// one exists. 249 /// one exists.
260 /// 250 ///
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after
359 349
360 return events; 350 return events;
361 } 351 }
362 352
363 /// The callback that's run when the [Directory.watch] stream is closed. 353 /// The callback that's run when the [Directory.watch] stream is closed.
364 /// Note that this is unlikely to happen on Windows, unless the system itself 354 /// Note that this is unlikely to happen on Windows, unless the system itself
365 /// closes the handle. 355 /// closes the handle.
366 void _onDone() { 356 void _onDone() {
367 _watchSubscription = null; 357 _watchSubscription = null;
368 358
369 // Emit remove-events for any remaining files. 359 // Emit remove events for any remaining files.
370 for (var file in _files.toSet()) { 360 for (var file in _files.toSet()) {
371 _emitEvent(ChangeType.REMOVE, file); 361 _emitEvent(ChangeType.REMOVE, file);
372 } 362 }
373 _files.clear(); 363 _files.clear();
374 close(); 364 close();
375 } 365 }
376 366
377 /// Start or restart the underlying [Directory.watch] stream. 367 /// Start or restart the underlying [Directory.watch] stream.
378 void _startWatch() { 368 void _startWatch() {
379 // Batch the events changes together so that we can dedup events. 369 // Batch the events together so that we can dedup events.
380 var innerStream = 370 var innerStream =
381 Chain.track(new Directory(directory).watch(recursive: true)); 371 Chain.track(new Directory(directory).watch(recursive: true));
382 _watchSubscription = innerStream.listen(_onEvent, 372 _watchSubscription = innerStream.listen(_onEvent,
383 onError: _eventsController.addError, 373 onError: _eventsController.addError,
384 onDone: _onDone); 374 onDone: _onDone);
385 } 375 }
386 376
387 /// Starts or restarts listing the watched directory to get an initial picture 377 /// Starts or restarts listing the watched directory to get an initial picture
388 /// of its state. 378 /// of its state.
389 Future _listDir() { 379 Future _listDir() {
(...skipping 20 matching lines...) Expand all
410 400
411 _eventsController.add(new WatchEvent(type, path)); 401 _eventsController.add(new WatchEvent(type, path));
412 } 402 }
413 403
414 /// Emit an error, then close the watcher. 404 /// Emit an error, then close the watcher.
415 void _emitError(error, StackTrace stackTrace) { 405 void _emitError(error, StackTrace stackTrace) {
416 _eventsController.addError(error, stackTrace); 406 _eventsController.addError(error, stackTrace);
417 close(); 407 close();
418 } 408 }
419 } 409 }
OLDNEW
« no previous file with comments | « pkg/analysis_server/test/physical_resource_provider_test.dart ('k') | pkg/watcher/test/directory_watcher/shared.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698