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

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

Issue 107403002: Add debugging prints to the Mac OS directory watcher. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: code review Created 7 years 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 | « no previous file | 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;
10 import 'package:stack_trace/stack_trace.dart'; 11 import 'package:stack_trace/stack_trace.dart';
11 12
12 import '../constructable_file_system_event.dart'; 13 import '../constructable_file_system_event.dart';
13 import '../path_set.dart'; 14 import '../path_set.dart';
14 import '../utils.dart'; 15 import '../utils.dart';
15 import '../watch_event.dart'; 16 import '../watch_event.dart';
16 import 'resubscribable.dart'; 17 import 'resubscribable.dart';
17 18
18 /// Uses the FSEvents subsystem to watch for filesystem events. 19 /// Uses the FSEvents subsystem to watch for filesystem events.
19 /// 20 ///
20 /// FSEvents has two main idiosyncrasies that this class works around. First, it 21 /// FSEvents has two main idiosyncrasies that this class works around. First, it
21 /// will occasionally report events that occurred before the filesystem watch 22 /// will occasionally report events that occurred before the filesystem watch
22 /// was initiated. Second, if multiple events happen to the same file in close 23 /// was initiated. Second, if multiple events happen to the same file in close
23 /// succession, it won't report them in the order they occurred. See issue 24 /// succession, it won't report them in the order they occurred. See issue
24 /// 14373. 25 /// 14373.
25 /// 26 ///
26 /// This also works around issues 15458 and 14849 in the implementation of 27 /// This also works around issues 15458 and 14849 in the implementation of
27 /// [Directory.watch]. 28 /// [Directory.watch].
28 class MacOSDirectoryWatcher extends ResubscribableDirectoryWatcher { 29 class MacOSDirectoryWatcher extends ResubscribableDirectoryWatcher {
30 // TODO(nweiz): remove these when issue 15042 is fixed.
31 static var logDebugInfo = false;
32 static var _count = 0;
33
29 MacOSDirectoryWatcher(String directory) 34 MacOSDirectoryWatcher(String directory)
30 : super(directory, () => new _MacOSDirectoryWatcher(directory)); 35 : super(directory, () => new _MacOSDirectoryWatcher(directory, _count++));
31 } 36 }
32 37
33 class _MacOSDirectoryWatcher implements ManuallyClosedDirectoryWatcher { 38 class _MacOSDirectoryWatcher implements ManuallyClosedDirectoryWatcher {
39 // TODO(nweiz): remove these when issue 15042 is fixed.
40 static var _count = 0;
41 final String _id;
42
34 final String directory; 43 final String directory;
35 44
36 Stream<WatchEvent> get events => _eventsController.stream; 45 Stream<WatchEvent> get events => _eventsController.stream;
37 final _eventsController = new StreamController<WatchEvent>.broadcast(); 46 final _eventsController = new StreamController<WatchEvent>.broadcast();
38 47
39 bool get isReady => _readyCompleter.isCompleted; 48 bool get isReady => _readyCompleter.isCompleted;
40 49
41 Future get ready => _readyCompleter.future; 50 Future get ready => _readyCompleter.future;
42 final _readyCompleter = new Completer(); 51 final _readyCompleter = new Completer();
43 52
(...skipping 19 matching lines...) Expand all
63 /// This is separate from [_subscriptions] because this stream occasionally 72 /// This is separate from [_subscriptions] because this stream occasionally
64 /// needs to be resubscribed in order to work around issue 14849. 73 /// needs to be resubscribed in order to work around issue 14849.
65 StreamSubscription<FileSystemEvent> _watchSubscription; 74 StreamSubscription<FileSystemEvent> _watchSubscription;
66 75
67 /// A set of subscriptions that this watcher subscribes to. 76 /// A set of subscriptions that this watcher subscribes to.
68 /// 77 ///
69 /// These are gathered together so that they may all be canceled when the 78 /// These are gathered together so that they may all be canceled when the
70 /// watcher is closed. This does not include [_watchSubscription]. 79 /// watcher is closed. This does not include [_watchSubscription].
71 final _subscriptions = new Set<StreamSubscription>(); 80 final _subscriptions = new Set<StreamSubscription>();
72 81
73 _MacOSDirectoryWatcher(String directory) 82 _MacOSDirectoryWatcher(String directory, int parentId)
74 : directory = directory, 83 : directory = directory,
75 _files = new PathSet(directory) { 84 _files = new PathSet(directory),
85 _id = "$parentId/${_count++}" {
76 _startWatch(); 86 _startWatch();
77 87
78 _listen(Chain.track(new Directory(directory).list(recursive: true)), 88 _listen(Chain.track(new Directory(directory).list(recursive: true)),
79 (entity) { 89 (entity) {
80 if (entity is! Directory) _files.add(entity.path); 90 if (entity is! Directory) _files.add(entity.path);
81 }, 91 },
82 onError: _emitError, 92 onError: _emitError,
83 onDone: _readyCompleter.complete, 93 onDone: () {
94 if (MacOSDirectoryWatcher.logDebugInfo) {
95 print("[$_id] watcher is ready, known files:");
96 for (var file in _files.toSet()) {
97 print("[$_id] ${p.relative(file, from: directory)}");
98 }
99 }
100 _readyCompleter.complete();
101 },
84 cancelOnError: true); 102 cancelOnError: true);
85 } 103 }
86 104
87 void close() { 105 void close() {
106 if (MacOSDirectoryWatcher.logDebugInfo) {
107 print("[$_id] watcher is closed");
108 }
88 for (var subscription in _subscriptions) { 109 for (var subscription in _subscriptions) {
89 subscription.cancel(); 110 subscription.cancel();
90 } 111 }
91 _subscriptions.clear(); 112 _subscriptions.clear();
92 if (_watchSubscription != null) _watchSubscription.cancel(); 113 if (_watchSubscription != null) _watchSubscription.cancel();
93 _watchSubscription = null; 114 _watchSubscription = null;
94 _eventsController.close(); 115 _eventsController.close();
95 } 116 }
96 117
97 /// The callback that's run when [Directory.watch] emits a batch of events. 118 /// The callback that's run when [Directory.watch] emits a batch of events.
98 void _onBatch(List<FileSystemEvent> batch) { 119 void _onBatch(List<FileSystemEvent> batch) {
120 if (MacOSDirectoryWatcher.logDebugInfo) {
121 print("[$_id] ======== batch:");
122 for (var event in batch) {
123 print("[$_id] ${_formatEvent(event)}");
124 }
125
126 print("[$_id] known files:");
127 for (var file in _files.toSet()) {
128 print("[$_id] ${p.relative(file, from: directory)}");
129 }
130 }
131
99 batches++; 132 batches++;
100 133
101 _sortEvents(batch).forEach((path, events) { 134 _sortEvents(batch).forEach((path, events) {
135 var relativePath = p.relative(path, from: directory);
136 if (MacOSDirectoryWatcher.logDebugInfo) {
137 print("[$_id] events for $relativePath:\n");
138 for (var event in events) {
139 print("[$_id] ${_formatEvent(event)}");
140 }
141 }
142
102 var canonicalEvent = _canonicalEvent(events); 143 var canonicalEvent = _canonicalEvent(events);
103 events = canonicalEvent == null ? 144 events = canonicalEvent == null ?
104 _eventsBasedOnFileSystem(path) : [canonicalEvent]; 145 _eventsBasedOnFileSystem(path) : [canonicalEvent];
146 if (MacOSDirectoryWatcher.logDebugInfo) {
147 print("[$_id] canonical event for $relativePath: "
148 "${_formatEvent(canonicalEvent)}");
149 print("[$_id] actionable events for $relativePath: "
150 "${events.map(_formatEvent)}");
151 }
105 152
106 for (var event in events) { 153 for (var event in events) {
107 if (event is FileSystemCreateEvent) { 154 if (event is FileSystemCreateEvent) {
108 if (!event.isDirectory) { 155 if (!event.isDirectory) {
109 // Don't emit ADD events for files or directories that we already 156 // Don't emit ADD events for files or directories that we already
110 // know about. Such an event comes from FSEvents reporting an add 157 // know about. Such an event comes from FSEvents reporting an add
111 // that happened prior to the watch beginning. 158 // that happened prior to the watch beginning.
112 if (_files.contains(path)) continue; 159 if (_files.contains(path)) continue;
113 160
114 _emitEvent(ChangeType.ADD, path); 161 _emitEvent(ChangeType.ADD, path);
115 _files.add(path); 162 _files.add(path);
116 continue; 163 continue;
117 } 164 }
118 165
119 if (_files.containsDir(path)) continue; 166 if (_files.containsDir(path)) continue;
120 167
121 _listen(Chain.track(new Directory(path).list(recursive: true)), 168 _listen(Chain.track(new Directory(path).list(recursive: true)),
122 (entity) { 169 (entity) {
123 if (entity is Directory) return; 170 if (entity is Directory) return;
124 _emitEvent(ChangeType.ADD, entity.path); 171 _emitEvent(ChangeType.ADD, entity.path);
125 _files.add(entity.path); 172 _files.add(entity.path);
126 }, onError: _emitError, cancelOnError: true); 173 }, onError: (e, stackTrace) {
174 if (MacOSDirectoryWatcher.logDebugInfo) {
175 print("[$_id] got error listing $relativePath: $e");
176 }
177 _emitError(e, stackTrace);
178 }, cancelOnError: true);
127 } else if (event is FileSystemModifyEvent) { 179 } else if (event is FileSystemModifyEvent) {
128 assert(!event.isDirectory); 180 assert(!event.isDirectory);
129 _emitEvent(ChangeType.MODIFY, path); 181 _emitEvent(ChangeType.MODIFY, path);
130 } else { 182 } else {
131 assert(event is FileSystemDeleteEvent); 183 assert(event is FileSystemDeleteEvent);
132 for (var removedPath in _files.remove(path)) { 184 for (var removedPath in _files.remove(path)) {
133 _emitEvent(ChangeType.REMOVE, removedPath); 185 _emitEvent(ChangeType.REMOVE, removedPath);
134 } 186 }
135 } 187 }
136 } 188 }
137 }); 189 });
190
191 if (MacOSDirectoryWatcher.logDebugInfo) {
192 print("[$_id] ======== batch complete");
193 }
138 } 194 }
139 195
140 /// Sort all the events in a batch into sets based on their path. 196 /// Sort all the events in a batch into sets based on their path.
141 /// 197 ///
142 /// A single input event may result in multiple events in the returned map; 198 /// A single input event may result in multiple events in the returned map;
143 /// for example, a MOVE event becomes a DELETE event for the source and a 199 /// for example, a MOVE event becomes a DELETE event for the source and a
144 /// CREATE event for the destination. 200 /// CREATE event for the destination.
145 /// 201 ///
146 /// The returned events won't contain any [FileSystemMoveEvent]s, nor will it 202 /// The returned events won't contain any [FileSystemMoveEvent]s, nor will it
147 /// contain any events relating to [directory]. 203 /// contain any events relating to [directory].
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after
256 /// This returns a list whose order should be reflected in the events emitted 312 /// This returns a list whose order should be reflected in the events emitted
257 /// to the user, unlike the batched events from [Directory.watch]. The 313 /// to the user, unlike the batched events from [Directory.watch]. The
258 /// returned list may be empty, indicating that no changes occurred to [path] 314 /// returned list may be empty, indicating that no changes occurred to [path]
259 /// (probably indicating that it was created and then immediately deleted). 315 /// (probably indicating that it was created and then immediately deleted).
260 List<FileSystemEvent> _eventsBasedOnFileSystem(String path) { 316 List<FileSystemEvent> _eventsBasedOnFileSystem(String path) {
261 var fileExisted = _files.contains(path); 317 var fileExisted = _files.contains(path);
262 var dirExisted = _files.containsDir(path); 318 var dirExisted = _files.containsDir(path);
263 var fileExists = new File(path).existsSync(); 319 var fileExists = new File(path).existsSync();
264 var dirExists = new Directory(path).existsSync(); 320 var dirExists = new Directory(path).existsSync();
265 321
322 if (MacOSDirectoryWatcher.logDebugInfo) {
323 print("[$_id] file existed: $fileExisted");
324 print("[$_id] dir existed: $dirExisted");
325 print("[$_id] file exists: $fileExists");
326 print("[$_id] dir exists: $dirExists");
327 }
328
266 var events = []; 329 var events = [];
267 if (fileExisted) { 330 if (fileExisted) {
268 if (fileExists) { 331 if (fileExists) {
269 events.add(new ConstructableFileSystemModifyEvent(path, false, false)); 332 events.add(new ConstructableFileSystemModifyEvent(path, false, false));
270 } else { 333 } else {
271 events.add(new ConstructableFileSystemDeleteEvent(path, false)); 334 events.add(new ConstructableFileSystemDeleteEvent(path, false));
272 } 335 }
273 } else if (dirExisted) { 336 } else if (dirExisted) {
274 if (dirExists) { 337 if (dirExists) {
275 // If we got contradictory events for a directory that used to exist and 338 // If we got contradictory events for a directory that used to exist and
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
321 .transform(new BatchedStreamTransformer<FileSystemEvent>()); 384 .transform(new BatchedStreamTransformer<FileSystemEvent>());
322 _watchSubscription = innerStream.listen(_onBatch, 385 _watchSubscription = innerStream.listen(_onBatch,
323 onError: _eventsController.addError, 386 onError: _eventsController.addError,
324 onDone: _onDone); 387 onDone: _onDone);
325 } 388 }
326 389
327 /// Emit an event with the given [type] and [path]. 390 /// Emit an event with the given [type] and [path].
328 void _emitEvent(ChangeType type, String path) { 391 void _emitEvent(ChangeType type, String path) {
329 if (!isReady) return; 392 if (!isReady) return;
330 393
394 if (MacOSDirectoryWatcher.logDebugInfo) {
395 print("[$_id] emitting $type ${p.relative(path, from: directory)}");
396 }
397
331 _eventsController.add(new WatchEvent(type, path)); 398 _eventsController.add(new WatchEvent(type, path));
332 } 399 }
333 400
334 /// Emit an error, then close the watcher. 401 /// Emit an error, then close the watcher.
335 void _emitError(error, StackTrace stackTrace) { 402 void _emitError(error, StackTrace stackTrace) {
336 _eventsController.addError(error, stackTrace); 403 _eventsController.addError(error, stackTrace);
337 close(); 404 close();
338 } 405 }
339 406
340 /// Like [Stream.listen], but automatically adds the subscription to 407 /// Like [Stream.listen], but automatically adds the subscription to
341 /// [_subscriptions] so that it can be canceled when [close] is called. 408 /// [_subscriptions] so that it can be canceled when [close] is called.
342 void _listen(Stream stream, void onData(event), {Function onError, 409 void _listen(Stream stream, void onData(event), {Function onError,
343 void onDone(), bool cancelOnError}) { 410 void onDone(), bool cancelOnError}) {
344 var subscription; 411 var subscription;
345 subscription = stream.listen(onData, onError: onError, onDone: () { 412 subscription = stream.listen(onData, onError: onError, onDone: () {
346 _subscriptions.remove(subscription); 413 _subscriptions.remove(subscription);
347 if (onDone != null) onDone(); 414 if (onDone != null) onDone();
348 }, cancelOnError: cancelOnError); 415 }, cancelOnError: cancelOnError);
349 _subscriptions.add(subscription); 416 _subscriptions.add(subscription);
350 } 417 }
418
419 // TODO(nweiz): remove this when issue 15042 is fixed.
420 /// Return a human-friendly string representation of [event].
421 String _formatEvent(FileSystemEvent event) {
422 if (event == null) return 'null';
423
424 var path = p.relative(event.path, from: directory);
425 var type = event.isDirectory ? 'directory' : 'file';
426 if (event is FileSystemCreateEvent) {
427 return "create $type $path";
428 } else if (event is FileSystemDeleteEvent) {
429 return "delete $type $path";
430 } else if (event is FileSystemModifyEvent) {
431 return "modify $type $path";
432 } else if (event is FileSystemMoveEvent) {
433 return "move $type $path to "
434 "${p.relative(event.destination, from: directory)}";
435 }
436 }
351 } 437 }
OLDNEW
« no previous file with comments | « no previous file | pkg/watcher/test/directory_watcher/mac_os_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698