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

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

Issue 71353011: Roll forward r30205. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: code review Created 7 years, 1 month 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
« no previous file with comments | « pkg/pkg.status ('k') | pkg/watcher/test/directory_watcher/mac_os_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2013, 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 4
5 library watcher.directory_watcher.mac_os; 5 library watcher.directory_watcher.mac_os;
6 6
7 import 'dart:async'; 7 import 'dart:async';
8 import 'dart:io'; 8 import 'dart:io';
9 9
10 import 'package:path/path.dart' as p;
11
10 import '../constructable_file_system_event.dart'; 12 import '../constructable_file_system_event.dart';
11 import '../path_set.dart'; 13 import '../path_set.dart';
12 import '../utils.dart'; 14 import '../utils.dart';
13 import '../watch_event.dart'; 15 import '../watch_event.dart';
14 import 'resubscribable.dart'; 16 import 'resubscribable.dart';
15 17
16 /// Uses the FSEvents subsystem to watch for filesystem events. 18 /// Uses the FSEvents subsystem to watch for filesystem events.
17 /// 19 ///
18 /// FSEvents has two main idiosyncrasies that this class works around. First, it 20 /// FSEvents has two main idiosyncrasies that this class works around. First, it
19 /// will occasionally report events that occurred before the filesystem watch 21 /// will occasionally report events that occurred before the filesystem watch
20 /// was initiated. Second, if multiple events happen to the same file in close 22 /// was initiated. Second, if multiple events happen to the same file in close
21 /// succession, it won't report them in the order they occurred. See issue 23 /// succession, it won't report them in the order they occurred. See issue
22 /// 14373. 24 /// 14373.
23 /// 25 ///
24 /// This also works around issues 14793, 14806, and 14849 in the implementation 26 /// This also works around issues 14793, 14806, and 14849 in the implementation
25 /// of [Directory.watch]. 27 /// of [Directory.watch].
26 class MacOSDirectoryWatcher extends ResubscribableDirectoryWatcher { 28 class MacOSDirectoryWatcher extends ResubscribableDirectoryWatcher {
29 // TODO(nweiz): remove this when issue 15042 is fixed.
30 static bool logDebugInfo = false;
31
27 MacOSDirectoryWatcher(String directory) 32 MacOSDirectoryWatcher(String directory)
28 : super(directory, () => new _MacOSDirectoryWatcher(directory)); 33 : super(directory, () => new _MacOSDirectoryWatcher(directory));
29 } 34 }
30 35
31 class _MacOSDirectoryWatcher implements ManuallyClosedDirectoryWatcher { 36 class _MacOSDirectoryWatcher implements ManuallyClosedDirectoryWatcher {
32 final String directory; 37 final String directory;
33 38
34 Stream<WatchEvent> get events => _eventsController.stream; 39 Stream<WatchEvent> get events => _eventsController.stream;
35 final _eventsController = new StreamController<WatchEvent>.broadcast(); 40 final _eventsController = new StreamController<WatchEvent>.broadcast();
36 41
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
71 _MacOSDirectoryWatcher(String directory) 76 _MacOSDirectoryWatcher(String directory)
72 : directory = directory, 77 : directory = directory,
73 _files = new PathSet(directory) { 78 _files = new PathSet(directory) {
74 _startWatch(); 79 _startWatch();
75 80
76 _listen(new Directory(directory).list(recursive: true), 81 _listen(new Directory(directory).list(recursive: true),
77 (entity) { 82 (entity) {
78 if (entity is! Directory) _files.add(entity.path); 83 if (entity is! Directory) _files.add(entity.path);
79 }, 84 },
80 onError: _emitError, 85 onError: _emitError,
81 onDone: _readyCompleter.complete, 86 onDone: () {
87 if (MacOSDirectoryWatcher.logDebugInfo) {
88 print("watcher is ready");
89 }
90 _readyCompleter.complete();
91 },
82 cancelOnError: true); 92 cancelOnError: true);
83 } 93 }
84 94
85 void close() { 95 void close() {
86 for (var subscription in _subscriptions) { 96 for (var subscription in _subscriptions) {
87 subscription.cancel(); 97 subscription.cancel();
88 } 98 }
89 _subscriptions.clear(); 99 _subscriptions.clear();
90 if (_watchSubscription != null) _watchSubscription.cancel(); 100 if (_watchSubscription != null) _watchSubscription.cancel();
91 _watchSubscription = null; 101 _watchSubscription = null;
92 _eventsController.close(); 102 _eventsController.close();
93 } 103 }
94 104
95 /// The callback that's run when [Directory.watch] emits a batch of events. 105 /// The callback that's run when [Directory.watch] emits a batch of events.
96 void _onBatch(List<FileSystemEvent> batch) { 106 void _onBatch(List<FileSystemEvent> batch) {
107 if (MacOSDirectoryWatcher.logDebugInfo) {
108 print("======== batch:");
109 for (var event in batch) {
110 print(" ${_formatEvent(event)}");
111 }
112
113 print("known files:");
114 for (var foo in _files.toSet()) {
115 print(" ${p.relative(foo, from: directory)}");
116 }
117 }
118
97 batches++; 119 batches++;
98 120
99 _sortEvents(batch).forEach((path, events) { 121 _sortEvents(batch).forEach((path, events) {
122 var relativePath = p.relative(path, from: directory);
123 if (MacOSDirectoryWatcher.logDebugInfo) {
124 print("events for $relativePath:\n");
125 for (var event in events) {
126 print(" ${_formatEvent(event)}");
127 }
128 }
129
100 var canonicalEvent = _canonicalEvent(events); 130 var canonicalEvent = _canonicalEvent(events);
101 events = canonicalEvent == null ? 131 events = canonicalEvent == null ?
102 _eventsBasedOnFileSystem(path) : [canonicalEvent]; 132 _eventsBasedOnFileSystem(path) : [canonicalEvent];
133 if (MacOSDirectoryWatcher.logDebugInfo) {
134 print("canonical event for $relativePath: "
135 "${_formatEvent(canonicalEvent)}");
136 print("actionable events for $relativePath: "
137 "${events.map(_formatEvent)}");
138 }
103 139
104 for (var event in events) { 140 for (var event in events) {
105 if (event is FileSystemCreateEvent) { 141 if (event is FileSystemCreateEvent) {
106 if (!event.isDirectory) { 142 if (!event.isDirectory) {
107 _emitEvent(ChangeType.ADD, path); 143 _emitEvent(ChangeType.ADD, path);
108 _files.add(path); 144 _files.add(path);
109 continue; 145 continue;
110 } 146 }
111 147
112 _listen(new Directory(path).list(recursive: true), (entity) { 148 _listen(new Directory(path).list(recursive: true), (entity) {
113 if (entity is Directory) return; 149 if (entity is Directory) return;
114 _emitEvent(ChangeType.ADD, entity.path); 150 _emitEvent(ChangeType.ADD, entity.path);
115 _files.add(entity.path); 151 _files.add(entity.path);
116 }, onError: _emitError, cancelOnError: true); 152 }, onError: (e, stackTrace) {
153 if (MacOSDirectoryWatcher.logDebugInfo) {
154 print("got error listing $relativePath: $e");
155 }
156 _emitError(e, stackTrace);
157 }, cancelOnError: true);
117 } else if (event is FileSystemModifyEvent) { 158 } else if (event is FileSystemModifyEvent) {
118 assert(!event.isDirectory); 159 assert(!event.isDirectory);
119 _emitEvent(ChangeType.MODIFY, path); 160 _emitEvent(ChangeType.MODIFY, path);
120 } else { 161 } else {
121 assert(event is FileSystemDeleteEvent); 162 assert(event is FileSystemDeleteEvent);
122 for (var removedPath in _files.remove(path)) { 163 for (var removedPath in _files.remove(path)) {
123 _emitEvent(ChangeType.REMOVE, removedPath); 164 _emitEvent(ChangeType.REMOVE, removedPath);
124 } 165 }
125 } 166 }
126 } 167 }
127 }); 168 });
169
170 if (MacOSDirectoryWatcher.logDebugInfo) {
171 print("========");
172 }
128 } 173 }
129 174
130 /// Sort all the events in a batch into sets based on their path. 175 /// Sort all the events in a batch into sets based on their path.
131 /// 176 ///
132 /// A single input event may result in multiple events in the returned map; 177 /// A single input event may result in multiple events in the returned map;
133 /// for example, a MOVE event becomes a DELETE event for the source and a 178 /// for example, a MOVE event becomes a DELETE event for the source and a
134 /// CREATE event for the destination. 179 /// CREATE event for the destination.
135 /// 180 ///
136 /// The returned events won't contain any [FileSystemMoveEvent]s, nor will it 181 /// The returned events won't contain any [FileSystemMoveEvent]s, nor will it
137 /// contain any events relating to [directory]. 182 /// contain any events relating to [directory].
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after
254 /// This returns a list whose order should be reflected in the events emitted 299 /// This returns a list whose order should be reflected in the events emitted
255 /// to the user, unlike the batched events from [Directory.watch]. The 300 /// to the user, unlike the batched events from [Directory.watch]. The
256 /// returned list may be empty, indicating that no changes occurred to [path] 301 /// returned list may be empty, indicating that no changes occurred to [path]
257 /// (probably indicating that it was created and then immediately deleted). 302 /// (probably indicating that it was created and then immediately deleted).
258 List<FileSystemEvent> _eventsBasedOnFileSystem(String path) { 303 List<FileSystemEvent> _eventsBasedOnFileSystem(String path) {
259 var fileExisted = _files.contains(path); 304 var fileExisted = _files.contains(path);
260 var dirExisted = _files.containsDir(path); 305 var dirExisted = _files.containsDir(path);
261 var fileExists = new File(path).existsSync(); 306 var fileExists = new File(path).existsSync();
262 var dirExists = new Directory(path).existsSync(); 307 var dirExists = new Directory(path).existsSync();
263 308
309 if (MacOSDirectoryWatcher.logDebugInfo) {
310 print("file existed: $fileExisted");
311 print("dir existed: $dirExisted");
312 print("file exists: $fileExists");
313 print("dir exists: $dirExists");
314 }
315
264 var events = []; 316 var events = [];
265 if (fileExisted) { 317 if (fileExisted) {
266 if (fileExists) { 318 if (fileExists) {
267 events.add(new ConstructableFileSystemModifyEvent(path, false, false)); 319 events.add(new ConstructableFileSystemModifyEvent(path, false, false));
268 } else { 320 } else {
269 events.add(new ConstructableFileSystemDeleteEvent(path, false)); 321 events.add(new ConstructableFileSystemDeleteEvent(path, false));
270 } 322 }
271 } else if (dirExisted) { 323 } else if (dirExisted) {
272 if (dirExists) { 324 if (dirExists) {
273 // If we got contradictory events for a directory that used to exist and 325 // If we got contradictory events for a directory that used to exist and
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
323 375
324 /// Emit an event with the given [type] and [path]. 376 /// Emit an event with the given [type] and [path].
325 void _emitEvent(ChangeType type, String path) { 377 void _emitEvent(ChangeType type, String path) {
326 if (!isReady) return; 378 if (!isReady) return;
327 379
328 // Don't emit ADD events for files that we already know about. Such an event 380 // Don't emit ADD events for files that we already know about. Such an event
329 // probably comes from FSEvents reporting an add that happened prior to the 381 // probably comes from FSEvents reporting an add that happened prior to the
330 // watch beginning. 382 // watch beginning.
331 if (type == ChangeType.ADD && _files.contains(path)) return; 383 if (type == ChangeType.ADD && _files.contains(path)) return;
332 384
385 if (MacOSDirectoryWatcher.logDebugInfo) {
386 print("emitting $type ${p.relative(path, from: directory)}");
387 }
388
333 _eventsController.add(new WatchEvent(type, path)); 389 _eventsController.add(new WatchEvent(type, path));
334 } 390 }
335 391
336 /// Emit an error, then close the watcher. 392 /// Emit an error, then close the watcher.
337 void _emitError(error, StackTrace stackTrace) { 393 void _emitError(error, StackTrace stackTrace) {
338 _eventsController.addError(error, stackTrace); 394 _eventsController.addError(error, stackTrace);
339 close(); 395 close();
340 } 396 }
341 397
342 /// Like [Stream.listen], but automatically adds the subscription to 398 /// Like [Stream.listen], but automatically adds the subscription to
343 /// [_subscriptions] so that it can be canceled when [close] is called. 399 /// [_subscriptions] so that it can be canceled when [close] is called.
344 void _listen(Stream stream, void onData(event), {Function onError, 400 void _listen(Stream stream, void onData(event), {Function onError,
345 void onDone(), bool cancelOnError}) { 401 void onDone(), bool cancelOnError}) {
346 var subscription; 402 var subscription;
347 subscription = stream.listen(onData, onError: onError, onDone: () { 403 subscription = stream.listen(onData, onError: onError, onDone: () {
348 _subscriptions.remove(subscription); 404 _subscriptions.remove(subscription);
349 if (onDone != null) onDone(); 405 if (onDone != null) onDone();
350 }, cancelOnError: cancelOnError); 406 }, cancelOnError: cancelOnError);
351 _subscriptions.add(subscription); 407 _subscriptions.add(subscription);
352 } 408 }
409
410 // TODO(nweiz): remove this when issue 15042 is fixed.
411 /// Return a human-friendly string representation of [event].
412 String _formatEvent(FileSystemEvent event) {
413 if (event == null) return 'null';
414
415 var path = p.relative(event.path, from: directory);
416 var type = event.isDirectory ? 'directory' : 'file';
417 if (event is FileSystemCreateEvent) {
418 return "create $type $path";
419 } else if (event is FileSystemDeleteEvent) {
420 return "delete $type $path";
421 } else if (event is FileSystemModifyEvent) {
422 return "modify $type $path";
423 } else if (event is FileSystemMoveEvent) {
424 return "move $type $path to "
425 "${p.relative(event.destination, from: directory)}";
426 }
427 }
353 } 428 }
OLDNEW
« no previous file with comments | « pkg/pkg.status ('k') | pkg/watcher/test/directory_watcher/mac_os_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698