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

Side by Side Diff: base/files/file_path_watcher_kqueue.cc

Issue 283423003: Use FSEvents for recursive file watch on Mac (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: another Created 6 years, 7 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) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/files/file_path_watcher.h" 5 #include "base/files/file_path_watcher_kqueue.h"
6 6
7 #include <fcntl.h> 7 #include <fcntl.h>
8 #include <sys/event.h>
9 #include <sys/param.h> 8 #include <sys/param.h>
10 9
11 #include <vector>
12
13 #include "base/bind.h" 10 #include "base/bind.h"
14 #include "base/file_util.h" 11 #include "base/file_util.h"
15 #include "base/logging.h" 12 #include "base/logging.h"
16 #include "base/message_loop/message_loop.h"
17 #include "base/message_loop/message_loop_proxy.h"
18 #include "base/strings/stringprintf.h" 13 #include "base/strings/stringprintf.h"
19 14
20 // On some platforms these are not defined. 15 // On some platforms these are not defined.
21 #if !defined(EV_RECEIPT) 16 #if !defined(EV_RECEIPT)
22 #define EV_RECEIPT 0 17 #define EV_RECEIPT 0
23 #endif 18 #endif
24 #if !defined(O_EVTONLY) 19 #if !defined(O_EVTONLY)
25 #define O_EVTONLY O_RDONLY 20 #define O_EVTONLY O_RDONLY
26 #endif 21 #endif
27 22
28 namespace base { 23 namespace base {
29 24
30 namespace { 25 FilePathWatcherKQueue::FilePathWatcherKQueue() : kqueue_(-1) {}
31 26
32 // Mac-specific file watcher implementation based on kqueue. 27 FilePathWatcherKQueue::~FilePathWatcherKQueue() {}
33 // Originally it was based on FSEvents so that the semantics were equivalent
34 // on Linux, OSX and Windows where it was able to detect:
35 // - file creation/deletion/modification in a watched directory
36 // - file creation/deletion/modification for a watched file
37 // - modifications to the paths to a watched object that would affect the
38 // object such as renaming/attibute changes etc.
39 // The FSEvents version did all of the above except handling attribute changes
40 // to path components. Unfortunately FSEvents appears to have an issue where the
41 // current implementation (Mac OS X 10.6.7) sometimes drops events and doesn't
42 // send notifications. See
43 // http://code.google.com/p/chromium/issues/detail?id=54822#c31 for source that
44 // will reproduce the problem. FSEvents also required having a CFRunLoop
45 // backing the thread that it was running on, that caused added complexity
46 // in the interfaces.
47 // The kqueue implementation will handle all of the items in the list above
48 // except for detecting modifications to files in a watched directory. It will
49 // detect the creation and deletion of files, just not the modification of
50 // files. It does however detect the attribute changes that the FSEvents impl
51 // would miss.
52 class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate,
53 public MessageLoopForIO::Watcher,
54 public MessageLoop::DestructionObserver {
55 public:
56 FilePathWatcherImpl() : kqueue_(-1) {}
57 28
58 // MessageLoopForIO::Watcher overrides. 29 void FilePathWatcherKQueue::ReleaseEvent(struct kevent& event) {
59 virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE;
60 virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE;
61
62 // MessageLoop::DestructionObserver overrides.
63 virtual void WillDestroyCurrentMessageLoop() OVERRIDE;
64
65 // FilePathWatcher::PlatformDelegate overrides.
66 virtual bool Watch(const FilePath& path,
67 bool recursive,
68 const FilePathWatcher::Callback& callback) OVERRIDE;
69 virtual void Cancel() OVERRIDE;
70
71 protected:
72 virtual ~FilePathWatcherImpl() {}
73
74 private:
75 class EventData {
76 public:
77 EventData(const FilePath& path, const FilePath::StringType& subdir)
78 : path_(path), subdir_(subdir) { }
79 FilePath path_; // Full path to this item.
80 FilePath::StringType subdir_; // Path to any sub item.
81 };
82 typedef std::vector<struct kevent> EventVector;
83
84 // Can only be called on |io_message_loop_|'s thread.
85 virtual void CancelOnMessageLoopThread() OVERRIDE;
86
87 // Returns true if the kevent values are error free.
88 bool AreKeventValuesValid(struct kevent* kevents, int count);
89
90 // Respond to a change of attributes of the path component represented by
91 // |event|. Sets |target_file_affected| to true if |target_| is affected.
92 // Sets |update_watches| to true if |events_| need to be updated.
93 void HandleAttributesChange(const EventVector::iterator& event,
94 bool* target_file_affected,
95 bool* update_watches);
96
97 // Respond to a move or deletion of the path component represented by
98 // |event|. Sets |target_file_affected| to true if |target_| is affected.
99 // Sets |update_watches| to true if |events_| need to be updated.
100 void HandleDeleteOrMoveChange(const EventVector::iterator& event,
101 bool* target_file_affected,
102 bool* update_watches);
103
104 // Respond to a creation of an item in the path component represented by
105 // |event|. Sets |target_file_affected| to true if |target_| is affected.
106 // Sets |update_watches| to true if |events_| need to be updated.
107 void HandleCreateItemChange(const EventVector::iterator& event,
108 bool* target_file_affected,
109 bool* update_watches);
110
111 // Update |events_| with the current status of the system.
112 // Sets |target_file_affected| to true if |target_| is affected.
113 // Returns false if an error occurs.
114 bool UpdateWatches(bool* target_file_affected);
115
116 // Fills |events| with one kevent per component in |path|.
117 // Returns the number of valid events created where a valid event is
118 // defined as one that has a ident (file descriptor) field != -1.
119 static int EventsForPath(FilePath path, EventVector *events);
120
121 // Release a kevent generated by EventsForPath.
122 static void ReleaseEvent(struct kevent& event);
123
124 // Returns a file descriptor that will not block the system from deleting
125 // the file it references.
126 static uintptr_t FileDescriptorForPath(const FilePath& path);
127
128 static const uintptr_t kNoFileDescriptor = static_cast<uintptr_t>(-1);
129
130 // Closes |*fd| and sets |*fd| to -1.
131 static void CloseFileDescriptor(uintptr_t* fd);
132
133 // Returns true if kevent has open file descriptor.
134 static bool IsKeventFileDescriptorOpen(const struct kevent& event) {
135 return event.ident != kNoFileDescriptor;
136 }
137
138 static EventData* EventDataForKevent(const struct kevent& event) {
139 return reinterpret_cast<EventData*>(event.udata);
140 }
141
142 EventVector events_;
143 scoped_refptr<base::MessageLoopProxy> io_message_loop_;
144 MessageLoopForIO::FileDescriptorWatcher kqueue_watcher_;
145 FilePathWatcher::Callback callback_;
146 FilePath target_;
147 int kqueue_;
148
149 DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl);
150 };
151
152 void FilePathWatcherImpl::ReleaseEvent(struct kevent& event) {
153 CloseFileDescriptor(&event.ident); 30 CloseFileDescriptor(&event.ident);
154 EventData* entry = EventDataForKevent(event); 31 EventData* entry = EventDataForKevent(event);
155 delete entry; 32 delete entry;
156 event.udata = NULL; 33 event.udata = NULL;
157 } 34 }
158 35
159 int FilePathWatcherImpl::EventsForPath(FilePath path, EventVector* events) { 36 int FilePathWatcherKQueue::EventsForPath(FilePath path, EventVector* events) {
160 DCHECK(MessageLoopForIO::current()); 37 DCHECK(MessageLoopForIO::current());
161 // Make sure that we are working with a clean slate. 38 // Make sure that we are working with a clean slate.
162 DCHECK(events->empty()); 39 DCHECK(events->empty());
163 40
164 std::vector<FilePath::StringType> components; 41 std::vector<FilePath::StringType> components;
165 path.GetComponents(&components); 42 path.GetComponents(&components);
166 43
167 if (components.size() < 1) { 44 if (components.size() < 1) {
168 return -1; 45 return -1;
169 } 46 }
(...skipping 21 matching lines...) Expand all
191 EventData* data = new EventData(built_path, subdir); 68 EventData* data = new EventData(built_path, subdir);
192 struct kevent event; 69 struct kevent event;
193 EV_SET(&event, fd, EVFILT_VNODE, (EV_ADD | EV_CLEAR | EV_RECEIPT), 70 EV_SET(&event, fd, EVFILT_VNODE, (EV_ADD | EV_CLEAR | EV_RECEIPT),
194 (NOTE_DELETE | NOTE_WRITE | NOTE_ATTRIB | 71 (NOTE_DELETE | NOTE_WRITE | NOTE_ATTRIB |
195 NOTE_RENAME | NOTE_REVOKE | NOTE_EXTEND), 0, data); 72 NOTE_RENAME | NOTE_REVOKE | NOTE_EXTEND), 0, data);
196 events->push_back(event); 73 events->push_back(event);
197 } 74 }
198 return last_existing_entry; 75 return last_existing_entry;
199 } 76 }
200 77
201 uintptr_t FilePathWatcherImpl::FileDescriptorForPath(const FilePath& path) { 78 uintptr_t FilePathWatcherKQueue::FileDescriptorForPath(const FilePath& path) {
202 int fd = HANDLE_EINTR(open(path.value().c_str(), O_EVTONLY)); 79 int fd = HANDLE_EINTR(open(path.value().c_str(), O_EVTONLY));
203 if (fd == -1) 80 if (fd == -1)
204 return kNoFileDescriptor; 81 return kNoFileDescriptor;
205 return fd; 82 return fd;
206 } 83 }
207 84
208 void FilePathWatcherImpl::CloseFileDescriptor(uintptr_t* fd) { 85 void FilePathWatcherKQueue::CloseFileDescriptor(uintptr_t* fd) {
209 if (*fd == kNoFileDescriptor) { 86 if (*fd == kNoFileDescriptor) {
210 return; 87 return;
211 } 88 }
212 89
213 if (IGNORE_EINTR(close(*fd)) != 0) { 90 if (IGNORE_EINTR(close(*fd)) != 0) {
214 DPLOG(ERROR) << "close"; 91 DPLOG(ERROR) << "close";
215 } 92 }
216 *fd = kNoFileDescriptor; 93 *fd = kNoFileDescriptor;
217 } 94 }
218 95
219 bool FilePathWatcherImpl::AreKeventValuesValid(struct kevent* kevents, 96 bool FilePathWatcherKQueue::AreKeventValuesValid(struct kevent* kevents,
220 int count) { 97 int count) {
221 if (count < 0) { 98 if (count < 0) {
222 DPLOG(ERROR) << "kevent"; 99 DPLOG(ERROR) << "kevent";
223 return false; 100 return false;
224 } 101 }
225 bool valid = true; 102 bool valid = true;
226 for (int i = 0; i < count; ++i) { 103 for (int i = 0; i < count; ++i) {
227 if (kevents[i].flags & EV_ERROR && kevents[i].data) { 104 if (kevents[i].flags & EV_ERROR && kevents[i].data) {
228 // Find the kevent in |events_| that matches the kevent with the error. 105 // Find the kevent in |events_| that matches the kevent with the error.
229 EventVector::iterator event = events_.begin(); 106 EventVector::iterator event = events_.begin();
(...skipping 13 matching lines...) Expand all
243 path_name = base::StringPrintf( 120 path_name = base::StringPrintf(
244 "fd %ld", reinterpret_cast<long>(&kevents[i].ident)); 121 "fd %ld", reinterpret_cast<long>(&kevents[i].ident));
245 } 122 }
246 DLOG(ERROR) << "Error: " << kevents[i].data << " for " << path_name; 123 DLOG(ERROR) << "Error: " << kevents[i].data << " for " << path_name;
247 valid = false; 124 valid = false;
248 } 125 }
249 } 126 }
250 return valid; 127 return valid;
251 } 128 }
252 129
253 void FilePathWatcherImpl::HandleAttributesChange( 130 void FilePathWatcherKQueue::HandleAttributesChange(
254 const EventVector::iterator& event, 131 const EventVector::iterator& event,
255 bool* target_file_affected, 132 bool* target_file_affected,
256 bool* update_watches) { 133 bool* update_watches) {
257 EventVector::iterator next_event = event + 1; 134 EventVector::iterator next_event = event + 1;
258 EventData* next_event_data = EventDataForKevent(*next_event); 135 EventData* next_event_data = EventDataForKevent(*next_event);
259 // Check to see if the next item in path is still accessible. 136 // Check to see if the next item in path is still accessible.
260 uintptr_t have_access = FileDescriptorForPath(next_event_data->path_); 137 uintptr_t have_access = FileDescriptorForPath(next_event_data->path_);
261 if (have_access == kNoFileDescriptor) { 138 if (have_access == kNoFileDescriptor) {
262 *target_file_affected = true; 139 *target_file_affected = true;
263 *update_watches = true; 140 *update_watches = true;
264 EventVector::iterator local_event(event); 141 EventVector::iterator local_event(event);
265 for (; local_event != events_.end(); ++local_event) { 142 for (; local_event != events_.end(); ++local_event) {
266 // Close all nodes from the event down. This has the side effect of 143 // Close all nodes from the event down. This has the side effect of
267 // potentially rendering other events in |updates| invalid. 144 // potentially rendering other events in |updates| invalid.
268 // There is no need to remove the events from |kqueue_| because this 145 // There is no need to remove the events from |kqueue_| because this
269 // happens as a side effect of closing the file descriptor. 146 // happens as a side effect of closing the file descriptor.
270 CloseFileDescriptor(&local_event->ident); 147 CloseFileDescriptor(&local_event->ident);
271 } 148 }
272 } else { 149 } else {
273 CloseFileDescriptor(&have_access); 150 CloseFileDescriptor(&have_access);
274 } 151 }
275 } 152 }
276 153
277 void FilePathWatcherImpl::HandleDeleteOrMoveChange( 154 void FilePathWatcherKQueue::HandleDeleteOrMoveChange(
278 const EventVector::iterator& event, 155 const EventVector::iterator& event,
279 bool* target_file_affected, 156 bool* target_file_affected,
280 bool* update_watches) { 157 bool* update_watches) {
281 *target_file_affected = true; 158 *target_file_affected = true;
282 *update_watches = true; 159 *update_watches = true;
283 EventVector::iterator local_event(event); 160 EventVector::iterator local_event(event);
284 for (; local_event != events_.end(); ++local_event) { 161 for (; local_event != events_.end(); ++local_event) {
285 // Close all nodes from the event down. This has the side effect of 162 // Close all nodes from the event down. This has the side effect of
286 // potentially rendering other events in |updates| invalid. 163 // potentially rendering other events in |updates| invalid.
287 // There is no need to remove the events from |kqueue_| because this 164 // There is no need to remove the events from |kqueue_| because this
288 // happens as a side effect of closing the file descriptor. 165 // happens as a side effect of closing the file descriptor.
289 CloseFileDescriptor(&local_event->ident); 166 CloseFileDescriptor(&local_event->ident);
290 } 167 }
291 } 168 }
292 169
293 void FilePathWatcherImpl::HandleCreateItemChange( 170 void FilePathWatcherKQueue::HandleCreateItemChange(
294 const EventVector::iterator& event, 171 const EventVector::iterator& event,
295 bool* target_file_affected, 172 bool* target_file_affected,
296 bool* update_watches) { 173 bool* update_watches) {
297 // Get the next item in the path. 174 // Get the next item in the path.
298 EventVector::iterator next_event = event + 1; 175 EventVector::iterator next_event = event + 1;
299 // Check to see if it already has a valid file descriptor. 176 // Check to see if it already has a valid file descriptor.
300 if (!IsKeventFileDescriptorOpen(*next_event)) { 177 if (!IsKeventFileDescriptorOpen(*next_event)) {
301 EventData* next_event_data = EventDataForKevent(*next_event); 178 EventData* next_event_data = EventDataForKevent(*next_event);
302 // If not, attempt to open a file descriptor for it. 179 // If not, attempt to open a file descriptor for it.
303 next_event->ident = FileDescriptorForPath(next_event_data->path_); 180 next_event->ident = FileDescriptorForPath(next_event_data->path_);
304 if (IsKeventFileDescriptorOpen(*next_event)) { 181 if (IsKeventFileDescriptorOpen(*next_event)) {
305 *update_watches = true; 182 *update_watches = true;
306 if (next_event_data->subdir_.empty()) { 183 if (next_event_data->subdir_.empty()) {
307 *target_file_affected = true; 184 *target_file_affected = true;
308 } 185 }
309 } 186 }
310 } 187 }
311 } 188 }
312 189
313 bool FilePathWatcherImpl::UpdateWatches(bool* target_file_affected) { 190 bool FilePathWatcherKQueue::UpdateWatches(bool* target_file_affected) {
314 // Iterate over events adding kevents for items that exist to the kqueue. 191 // Iterate over events adding kevents for items that exist to the kqueue.
315 // Then check to see if new components in the path have been created. 192 // Then check to see if new components in the path have been created.
316 // Repeat until no new components in the path are detected. 193 // Repeat until no new components in the path are detected.
317 // This is to get around races in directory creation in a watched path. 194 // This is to get around races in directory creation in a watched path.
318 bool update_watches = true; 195 bool update_watches = true;
319 while (update_watches) { 196 while (update_watches) {
320 size_t valid; 197 size_t valid;
321 for (valid = 0; valid < events_.size(); ++valid) { 198 for (valid = 0; valid < events_.size(); ++valid) {
322 if (!IsKeventFileDescriptorOpen(events_[valid])) { 199 if (!IsKeventFileDescriptorOpen(events_[valid])) {
323 break; 200 break;
(...skipping 20 matching lines...) Expand all
344 *target_file_affected = true; 221 *target_file_affected = true;
345 } 222 }
346 } else { 223 } else {
347 break; 224 break;
348 } 225 }
349 } 226 }
350 } 227 }
351 return true; 228 return true;
352 } 229 }
353 230
354 void FilePathWatcherImpl::OnFileCanReadWithoutBlocking(int fd) { 231 void FilePathWatcherKQueue::OnFileCanReadWithoutBlocking(int fd) {
355 DCHECK(MessageLoopForIO::current()); 232 DCHECK(MessageLoopForIO::current());
356 DCHECK_EQ(fd, kqueue_); 233 DCHECK_EQ(fd, kqueue_);
357 DCHECK(events_.size()); 234 DCHECK(events_.size());
358 235
359 // Request the file system update notifications that have occurred and return 236 // Request the file system update notifications that have occurred and return
360 // them in |updates|. |count| will contain the number of updates that have 237 // them in |updates|. |count| will contain the number of updates that have
361 // occurred. 238 // occurred.
362 EventVector updates(events_.size()); 239 EventVector updates(events_.size());
363 struct timespec timeout = {0, 0}; 240 struct timespec timeout = {0, 0};
364 int count = HANDLE_EINTR(kevent(kqueue_, NULL, 0, &updates[0], updates.size(), 241 int count = HANDLE_EINTR(kevent(kqueue_, NULL, 0, &updates[0], updates.size(),
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
417 callback_.Run(target_, true /* error */); 294 callback_.Run(target_, true /* error */);
418 Cancel(); 295 Cancel();
419 } 296 }
420 } 297 }
421 298
422 if (send_notification) { 299 if (send_notification) {
423 callback_.Run(target_, false); 300 callback_.Run(target_, false);
424 } 301 }
425 } 302 }
426 303
427 void FilePathWatcherImpl::OnFileCanWriteWithoutBlocking(int fd) { 304 void FilePathWatcherKQueue::OnFileCanWriteWithoutBlocking(int fd) {
428 NOTREACHED(); 305 NOTREACHED();
429 } 306 }
430 307
431 void FilePathWatcherImpl::WillDestroyCurrentMessageLoop() { 308 void FilePathWatcherKQueue::WillDestroyCurrentMessageLoop() {
432 CancelOnMessageLoopThread(); 309 CancelOnMessageLoopThread();
433 } 310 }
434 311
435 bool FilePathWatcherImpl::Watch(const FilePath& path, 312 bool FilePathWatcherKQueue::Watch(const FilePath& path,
436 bool recursive, 313 bool recursive,
437 const FilePathWatcher::Callback& callback) { 314 const FilePathWatcher::Callback& callback) {
438 DCHECK(MessageLoopForIO::current()); 315 DCHECK(MessageLoopForIO::current());
439 DCHECK(target_.value().empty()); // Can only watch one path. 316 DCHECK(target_.value().empty()); // Can only watch one path.
440 DCHECK(!callback.is_null()); 317 DCHECK(!callback.is_null());
441 DCHECK_EQ(kqueue_, -1); 318 DCHECK_EQ(kqueue_, -1);
442 319
443 if (recursive) { 320 if (recursive) {
444 // Recursive watch is not supported on this platform. 321 // Recursive watch is not supported using kqueue.
445 NOTIMPLEMENTED(); 322 NOTIMPLEMENTED();
446 return false; 323 return false;
447 } 324 }
448 325
449 callback_ = callback; 326 callback_ = callback;
450 target_ = path; 327 target_ = path;
451 328
452 MessageLoop::current()->AddDestructionObserver(this); 329 MessageLoop::current()->AddDestructionObserver(this);
453 io_message_loop_ = base::MessageLoopProxy::current(); 330 io_message_loop_ = base::MessageLoopProxy::current();
454 331
(...skipping 16 matching lines...) Expand all
471 // be long lived, and if an error has occurred, there is no reason to waste 348 // be long lived, and if an error has occurred, there is no reason to waste
472 // the file descriptors. 349 // the file descriptors.
473 Cancel(); 350 Cancel();
474 return false; 351 return false;
475 } 352 }
476 353
477 return MessageLoopForIO::current()->WatchFileDescriptor( 354 return MessageLoopForIO::current()->WatchFileDescriptor(
478 kqueue_, true, MessageLoopForIO::WATCH_READ, &kqueue_watcher_, this); 355 kqueue_, true, MessageLoopForIO::WATCH_READ, &kqueue_watcher_, this);
479 } 356 }
480 357
481 void FilePathWatcherImpl::Cancel() { 358 void FilePathWatcherKQueue::Cancel() {
482 base::MessageLoopProxy* proxy = io_message_loop_.get(); 359 base::MessageLoopProxy* proxy = io_message_loop_.get();
483 if (!proxy) { 360 if (!proxy) {
484 set_cancelled(); 361 set_cancelled();
485 return; 362 return;
486 } 363 }
487 if (!proxy->BelongsToCurrentThread()) { 364 if (!proxy->BelongsToCurrentThread()) {
488 proxy->PostTask(FROM_HERE, 365 proxy->PostTask(FROM_HERE,
489 base::Bind(&FilePathWatcherImpl::Cancel, this)); 366 base::Bind(&FilePathWatcherKQueue::Cancel, this));
490 return; 367 return;
491 } 368 }
492 CancelOnMessageLoopThread(); 369 CancelOnMessageLoopThread();
493 } 370 }
494 371
495 void FilePathWatcherImpl::CancelOnMessageLoopThread() { 372 void FilePathWatcherKQueue::CancelOnMessageLoopThread() {
496 DCHECK(MessageLoopForIO::current()); 373 DCHECK(MessageLoopForIO::current());
497 if (!is_cancelled()) { 374 if (!is_cancelled()) {
498 set_cancelled(); 375 set_cancelled();
499 kqueue_watcher_.StopWatchingFileDescriptor(); 376 kqueue_watcher_.StopWatchingFileDescriptor();
500 if (IGNORE_EINTR(close(kqueue_)) != 0) { 377 if (IGNORE_EINTR(close(kqueue_)) != 0) {
501 DPLOG(ERROR) << "close kqueue"; 378 DPLOG(ERROR) << "close kqueue";
502 } 379 }
503 kqueue_ = -1; 380 kqueue_ = -1;
504 std::for_each(events_.begin(), events_.end(), ReleaseEvent); 381 std::for_each(events_.begin(), events_.end(), ReleaseEvent);
505 events_.clear(); 382 events_.clear();
506 io_message_loop_ = NULL; 383 io_message_loop_ = NULL;
507 MessageLoop::current()->RemoveDestructionObserver(this); 384 MessageLoop::current()->RemoveDestructionObserver(this);
508 callback_.Reset(); 385 callback_.Reset();
509 } 386 }
510 } 387 }
511 388
512 } // namespace
513
514 FilePathWatcher::FilePathWatcher() {
515 impl_ = new FilePathWatcherImpl();
516 }
517
518 } // namespace base 389 } // namespace base
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698