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

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