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

Side by Side Diff: runtime/bin/file_patch.dart

Issue 98773002: Rewrite file-system-watcher to better handle the different system APIs. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Last few fixes found by tests 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
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, 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 patch class _File { 5 patch class _File {
6 /* patch */ static _exists(String path) native "File_Exists"; 6 /* patch */ static _exists(String path) native "File_Exists";
7 /* patch */ static _create(String path) native "File_Create"; 7 /* patch */ static _create(String path) native "File_Create";
8 /* patch */ static _createLink(String path, String target) 8 /* patch */ static _createLink(String path, String target)
9 native "File_CreateLink"; 9 native "File_CreateLink";
10 /* patch */ static _linkTarget(String path) native "File_LinkTarget"; 10 /* patch */ static _linkTarget(String path) native "File_LinkTarget";
11 /* patch */ static _deleteNative(String path) native "File_Delete"; 11 /* patch */ static _deleteNative(String path) native "File_Delete";
12 /* patch */ static _deleteLinkNative(String path) native "File_DeleteLink"; 12 /* patch */ static _deleteLinkNative(String path) native "File_DeleteLink";
13 /* patch */ static _rename(String oldPath, String newPath) 13 /* patch */ static _rename(String oldPath, String newPath)
14 native "File_Rename"; 14 native "File_Rename";
15 /* patch */ static _renameLink(String oldPath, String newPath) 15 /* patch */ static _renameLink(String oldPath, String newPath)
16 native "File_RenameLink"; 16 native "File_RenameLink";
17 /* patch */ static _lengthFromPath(String path) native "File_LengthFromPath"; 17 /* patch */ static _lengthFromPath(String path) native "File_LengthFromPath";
18 /* patch */ static _lastModified(String path) native "File_LastModified"; 18 /* patch */ static _lastModified(String path) native "File_LastModified";
19 /* patch */ static _open(String path, int mode) native "File_Open"; 19 /* patch */ static _open(String path, int mode) native "File_Open";
20 /* patch */ static int _openStdio(int fd) native "File_OpenStdio"; 20 /* patch */ static int _openStdio(int fd) native "File_OpenStdio";
21 } 21 }
22 22
23
23 patch class _RandomAccessFile { 24 patch class _RandomAccessFile {
24 /* patch */ static int _close(int id) native "File_Close"; 25 /* patch */ static int _close(int id) native "File_Close";
25 /* patch */ static _readByte(int id) native "File_ReadByte"; 26 /* patch */ static _readByte(int id) native "File_ReadByte";
26 /* patch */ static _read(int id, int bytes) native "File_Read"; 27 /* patch */ static _read(int id, int bytes) native "File_Read";
27 /* patch */ static _readInto(int id, List<int> buffer, int start, int end) 28 /* patch */ static _readInto(int id, List<int> buffer, int start, int end)
28 native "File_ReadInto"; 29 native "File_ReadInto";
29 /* patch */ static _writeByte(int id, int value) native "File_WriteByte"; 30 /* patch */ static _writeByte(int id, int value) native "File_WriteByte";
30 /* patch */ static _writeFrom(int id, List<int> buffer, int start, int end) 31 /* patch */ static _writeFrom(int id, List<int> buffer, int start, int end)
31 native "File_WriteFrom"; 32 native "File_WriteFrom";
32 /* patch */ static _position(int id) native "File_Position"; 33 /* patch */ static _position(int id) native "File_Position";
33 /* patch */ static _setPosition(int id, int position) 34 /* patch */ static _setPosition(int id, int position)
34 native "File_SetPosition"; 35 native "File_SetPosition";
35 /* patch */ static _truncate(int id, int length) native "File_Truncate"; 36 /* patch */ static _truncate(int id, int length) native "File_Truncate";
36 /* patch */ static _length(int id) native "File_Length"; 37 /* patch */ static _length(int id) native "File_Length";
37 /* patch */ static _flush(int id) native "File_Flush"; 38 /* patch */ static _flush(int id) native "File_Flush";
38 } 39 }
39 40
40 patch class _FileSystemWatcher {
41 /* patch */ factory _FileSystemWatcher(
42 String path, int events, bool recursive)
43 => new _FileSystemWatcherImpl(path, events, recursive);
44 41
45 /* patch */ static bool get isSupported => _FileSystemWatcherImpl.isSupported; 42 class _WatcherPath {
43 final int pathId;
44 final String path;
45 final int events;
46 int count = 0;
47 _WatcherPath(this.pathId, this.path, this.events);
46 } 48 }
47 49
48 class _FileSystemWatcherImpl 50
49 extends NativeFieldWrapperClass1 51 patch class _FileSystemWatcher {
50 implements _FileSystemWatcher { 52 static int _id;
53 static final Map<int, _FileSystemWatcherPath> _idMap = {};
54
51 final String _path; 55 final String _path;
52 final int _events; 56 final int _events;
53 final bool _recursive; 57 final bool _recursive;
54 58
55 StreamController _controller; 59 _WatcherPath _watcherPath;
56 StreamSubscription _subscription;
57 60
58 _FileSystemWatcherImpl(this._path, this._events, this._recursive) { 61 StreamController _broadcastController;
62
63 /* patch */ static Stream<FileSystemEvent> watch(
64 String path, int events, bool recursive) {
65 if (Platform.isLinux) {
66 return new _InotifyFileSystemWatcher(path, events, recursive).stream;
67 }
68 if (Platform.isWindows) {
69 return new _Win32FileSystemWatcher(path, events, recursive).stream;
70 }
71 if (Platform.isMacOS) {
72 return new _FSEventStreamFileSystemWatcher(
73 path, events, recursive).stream;
74 }
75 throw new FileSystemException(
76 "File system watching is not supported on this platform");
77 }
78
79 _FileSystemWatcher._(this._path, this._events, this._recursive) {
59 if (!isSupported) { 80 if (!isSupported) {
60 throw new FileSystemException( 81 throw new FileSystemException(
61 "File system watching is not supported on this system", 82 "File system watching is not supported on this platform",
62 _path); 83 _path);
63 } 84 }
64 _controller = new StreamController.broadcast(onListen: _listen, 85 _broadcastController = new StreamController.broadcast(onListen: _listen,
65 onCancel: _cancel); 86 onCancel: _cancel);
66 } 87 }
67 88
89 Stream get stream => _broadcastController.stream;
90
68 void _listen() { 91 void _listen() {
69 int socketId; 92 if (_id == null) {
93 try {
94 _id = _initWatcher();
95 _newWatcher();
96 } catch (e) {
97 _broadcastController.addError(new FileSystemException(
98 "Failed to initialize file system entity watcher"));
99 _broadcastController.close();
100 return;
101 }
102 }
103 var pathId;
70 try { 104 try {
71 socketId = _watchPath(_path, _events, identical(true, _recursive)); 105 pathId = _watchPath(_id, _path, _events, _recursive);
72 } catch (e) { 106 } catch (e) {
73 _controller.addError(new FileSystemException( 107 _broadcastController.addError(new FileSystemException(
74 "Failed to watch path", _path, e)); 108 "Failed to watch path", _path, e));
75 _controller.close(); 109 _broadcastController.close();
76 return; 110 return;
77 } 111 }
112 if (!_idMap.containsKey(pathId)) {
113 _idMap[pathId] = new _WatcherPath(pathId, _path, _events);
114 }
115 _watcherPath = _idMap[pathId];
116 _watcherPath.count++;
117 _pathWatched().pipe(_broadcastController);
118 }
119
120 void _cancel() {
121 if (_watcherPath != null) {
122 assert(_watcherPath.count > 0);
123 _watcherPath.count--;
124 if (_watcherPath.count == 0) {
125 _pathWatchedEnd();
126 _unwatchPath(_id, _watcherPath.pathId);
127 _idMap.remove(_watcherPath.pathId);
128 }
129 _watcherPath = null;
130 }
131 if (_idMap.isEmpty && _id != null) {
132 _closeWatcher(_id);
133 _doneWatcher();
134 _id = null;
135 }
136 }
137
Søren Gjesse 2013/12/03 14:42:19 Please add a comment here on the lifecycle and how
Anders Johnsen 2013/12/04 10:30:52 Done.
138 void _newWatcher();
139 void _doneWatcher();
140 Stream _pathWatched();
141 void _donePathWatched();
142
143 static _WatcherPath _pathFromPathId(int pathId) {
144 return _idMap[pathId];
145 }
146
147 static Stream _listenOnSocket(int socketId, int id, int pathId) {
78 var socket = new _RawSocket(new _NativeSocket.watch(socketId)); 148 var socket = new _RawSocket(new _NativeSocket.watch(socketId));
79 _subscription = socket.expand((event) { 149 return socket.expand((event) {
80 bool stop = false; 150 var stops = [];
81 var events = []; 151 var events = [];
82 var pair = {}; 152 var pair = {};
83 if (event == RawSocketEvent.READ) { 153 if (event == RawSocketEvent.READ) {
84 String getPath(event) { 154 String getPath(event) {
85 var path = _path; 155 var path = _pathFromPathId(event[4]).path;
86 if (event[2] != null && event[2].isNotEmpty) { 156 if (event[2] != null && event[2].isNotEmpty) {
87 path += Platform.pathSeparator; 157 path += Platform.pathSeparator;
88 path += event[2]; 158 path += event[2];
89 } 159 }
90 return path; 160 return path;
91 } 161 }
92 bool getIsDir(event) { 162 bool getIsDir(event) {
93 if (Platform.isWindows) { 163 if (Platform.isWindows) {
94 // Windows does not get 'isDir' as part of the event. 164 // Windows does not get 'isDir' as part of the event.
95 return FileSystemEntity.isDirectorySync(getPath(event)); 165 return FileSystemEntity.isDirectorySync(getPath(event));
96 } 166 }
97 return (event[0] & FileSystemEvent._IS_DIR) != 0; 167 return (event[0] & FileSystemEvent._IS_DIR) != 0;
98 } 168 }
99 void add(event) { 169 void add(id, event) {
100 if ((event.type & _events) == 0) return; 170 if ((event.type & _pathFromPathId(id).events) == 0) return;
101 events.add(event); 171 events.add([id, event]);
102 } 172 }
103 void rewriteMove(event, isDir) { 173 void rewriteMove(event, isDir) {
104 if (event[3]) { 174 if (event[3]) {
105 add(new FileSystemCreateEvent._(getPath(event), isDir)); 175 add(event[4], new FileSystemCreateEvent._(getPath(event), isDir));
106 } else { 176 } else {
107 add(new FileSystemDeleteEvent._(getPath(event), isDir)); 177 add(event[4], new FileSystemDeleteEvent._(getPath(event), isDir));
108 } 178 }
109 } 179 }
110 while (socket.available() > 0) { 180 while (socket.available() > 0) {
111 for (var event in _readEvents()) { 181 for (var event in _readEvents(id, pathId)) {
112 if (event == null) continue; 182 if (event == null) continue;
183 int pathId = event[4];
113 bool isDir = getIsDir(event); 184 bool isDir = getIsDir(event);
114 var path = getPath(event); 185 var path = getPath(event);
115 if ((event[0] & FileSystemEvent.CREATE) != 0) { 186 if ((event[0] & FileSystemEvent.CREATE) != 0) {
116 add(new FileSystemCreateEvent._(path, isDir)); 187 add(event[4], new FileSystemCreateEvent._(path, isDir));
117 } 188 }
118 if ((event[0] & FileSystemEvent.MODIFY) != 0) { 189 if ((event[0] & FileSystemEvent.MODIFY) != 0) {
119 add(new FileSystemModifyEvent._(path, isDir, true)); 190 add(event[4], new FileSystemModifyEvent._(path, isDir, true));
120 } 191 }
121 if ((event[0] & FileSystemEvent._MODIFY_ATTRIBUTES) != 0) { 192 if ((event[0] & FileSystemEvent._MODIFY_ATTRIBUTES) != 0) {
122 add(new FileSystemModifyEvent._(path, isDir, false)); 193 add(event[4], new FileSystemModifyEvent._(path, isDir, false));
123 } 194 }
124 if ((event[0] & FileSystemEvent.MOVE) != 0) { 195 if ((event[0] & FileSystemEvent.MOVE) != 0) {
125 int link = event[1]; 196 int link = event[1];
126 if (link > 0) { 197 if (link > 0) {
127 if (pair.containsKey(link)) { 198 pair.putIfAbsent(pathId, () => {});
128 events.add(new FileSystemMoveEvent._( 199 if (pair[pathId].containsKey(link)) {
129 getPath(pair[link]), isDir, path)); 200 add(event[4],
130 pair.remove(link); 201 new FileSystemMoveEvent._(
202 getPath(pair[pathId][link]), isDir, path));
203 pair[pathId].remove(link);
131 } else { 204 } else {
132 pair[link] = event; 205 pair[pathId][link] = event;
133 } 206 }
134 } else { 207 } else {
135 rewriteMove(event, isDir); 208 rewriteMove(event, isDir);
136 } 209 }
137 } 210 }
138 if ((event[0] & FileSystemEvent.DELETE) != 0) { 211 if ((event[0] & FileSystemEvent.DELETE) != 0) {
139 add(new FileSystemDeleteEvent._(path, isDir)); 212 add(event[4], new FileSystemDeleteEvent._(path, isDir));
140 } 213 }
141 if ((event[0] & FileSystemEvent._DELETE_SELF) != 0) { 214 if ((event[0] & FileSystemEvent._DELETE_SELF) != 0) {
142 add(new FileSystemDeleteEvent._(path, isDir)); 215 add(event[4], new FileSystemDeleteEvent._(path, isDir));
143 stop = true; 216 // Signal done event.
144 } 217 stops.add([event[4], null]);
145 } 218 }
146 } 219 }
147 for (var event in pair.values) { 220 }
148 rewriteMove(event, getIsDir(event)); 221 for (var map in pair.values) {
222 for (var event in map.values) {
223 rewriteMove(event, getIsDir(event));
224 }
149 } 225 }
150 } else if (event == RawSocketEvent.CLOSED) { 226 } else if (event == RawSocketEvent.CLOSED) {
151 } else if (event == RawSocketEvent.READ_CLOSED) { 227 } else if (event == RawSocketEvent.READ_CLOSED) {
152 } else { 228 } else {
153 assert(false); 229 assert(false);
154 } 230 }
155 if (stop) socket.close(); 231 events.addAll(stops);
156 return events; 232 return events;
157 }) 233 });
158 .listen(_controller.add, onDone: _cancel); 234 }
159 } 235
160 236 /* patch */ static bool get isSupported
161 void _cancel() { 237 native "FileSystemWatcher_IsSupported";
162 if (_subscription != null) { 238
163 _unwatchPath(); 239 static int _initWatcher() native "FileSystemWatcher_InitWatcher";
164 _subscription.cancel(); 240 static void _closeWatcher(int id) native "FileSystemWatcher_CloseWatcher";
165 _subscription = null; 241
242 static int _watchPath(int id, String path, int events, bool recursive)
243 native "FileSystemWatcher_WatchPath";
244 static void _unwatchPath(int id, int path_id)
245 native "FileSystemWatcher_UnwatchPath";
246 static List _readEvents(int id, int path_id)
247 native "FileSystemWatcher_ReadEvents";
248 static int _getSocketId(int id, int path_id)
249 native "FileSystemWatcher_GetSocketId";
250 }
251
252
253 class _InotifyFileSystemWatcher extends _FileSystemWatcher {
254 static final Map<int, StreamController> _idMap = {};
255 static StreamSubscription _subscription;
256
257 _InotifyFileSystemWatcher(path, events, recursive)
258 : super._(path, events, recursive);
259
260 void _newWatcher() {
261 int id = _FileSystemWatcher._id;
262 _subscription = _FileSystemWatcher._listenOnSocket(id, id, 0)
263 .listen((event) {
264 if (_idMap.containsKey(event[0])) {
265 if (event[1] != null) {
266 _idMap[event[0]].add(event[1]);
267 } else {
268 _idMap[event[0]].close();
269 }
270 }
271 });
272 }
273
274 void _doneWatcher() {
Søren Gjesse 2013/12/03 14:42:19 Also close the inotify fd here?
Anders Johnsen 2013/12/04 10:30:52 canceling a one-way stream will close the fd.
275 _subscription.cancel();
276 }
277
278 Stream _pathWatched() {
279 var pathId = _watcherPath.pathId;
280 if (!_idMap.containsKey(pathId)) {
281 _idMap[pathId] = new StreamController.broadcast();
166 } 282 }
283 return _idMap[pathId].stream;
284 }
285
286 void _pathWatchedEnd() {
287 var pathId = _watcherPath.pathId;
288 if (!_idMap.containsKey(pathId)) return;
289 _idMap[pathId].close();
290 _idMap.remove(pathId);
291 }
292 }
293
294
295 class _Win32FileSystemWatcher extends _FileSystemWatcher {
296 StreamSubscription _subscription;
297 StreamController _controller;
298
299 _Win32FileSystemWatcher(path, events, recursive)
300 : super._(path, events, recursive);
301
302 void _newWatcher() {
Søren Gjesse 2013/12/03 14:42:19 You can move the closing curly one line up if you
Anders Johnsen 2013/12/04 10:30:52 Done.
303 }
304
305 void _doneWatcher() {
306 }
307
308 Stream _pathWatched() {
309 var pathId = _watcherPath.pathId;
310 _controller = new StreamController();
311 _subscription = _FileSystemWatcher._listenOnSocket(pathId, 0, pathId)
312 .listen((event) {
313 assert(event[0] == pathId);
314 if (event[1] != null) {
315 _controller.add(event[1]);
316 } else {
317 _controller.close();
318 }
319 });
320 return _controller.stream;
321 }
322
323 void _pathWatchedEnd() {
324 _subscription.cancel();
167 _controller.close(); 325 _controller.close();
168 } 326 }
169 327 }
170 Stream<FileSystemEvent> get stream => _controller.stream; 328
171 329
172 static bool get isSupported native "FileSystemWatcher_IsSupported"; 330 class _FSEventStreamFileSystemWatcher extends _FileSystemWatcher {
173 331 StreamSubscription _subscription;
174 int _watchPath(String path, int events, bool recursive) 332 StreamController _controller;
175 native "FileSystemWatcher_WatchPath"; 333
176 void _unwatchPath() native "FileSystemWatcher_UnwatchPath"; 334 _FSEventStreamFileSystemWatcher(path, events, recursive)
177 List _readEvents() native "FileSystemWatcher_ReadEvents"; 335 : super._(path, events, recursive);
178 } 336
337 void _newWatcher() {
338 }
339
340 void _doneWatcher() {
341 }
342
343 Stream _pathWatched() {
344 var pathId = _watcherPath.pathId;
345 var socketId = _FileSystemWatcher._getSocketId(0, pathId);
346 _controller = new StreamController();
347 _subscription = _FileSystemWatcher._listenOnSocket(socketId, 0, pathId)
348 .listen((event) {
349 if (event[1] != null) {
350 _controller.add(event[1]);
351 } else {
352 _controller.close();
353 }
354 });
355 return _controller.stream;
356 }
357
358 void _pathWatchedEnd() {
359 _subscription.cancel();
360 _controller.close();
361 }
362 }
363
179 364
180 Uint8List _makeUint8ListView(Uint8List source, int offsetInBytes, int length) { 365 Uint8List _makeUint8ListView(Uint8List source, int offsetInBytes, int length) {
181 return new Uint8List.view(source.buffer, offsetInBytes, length); 366 return new Uint8List.view(source.buffer, offsetInBytes, length);
182 } 367 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698