OLD | NEW |
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 "chrome/browser/file_path_watcher/file_path_watcher.h" | 5 #include "content/common/file_path_watcher/file_path_watcher.h" |
6 | 6 |
7 #include <errno.h> | 7 #include <errno.h> |
8 #include <string.h> | 8 #include <string.h> |
9 #include <sys/inotify.h> | 9 #include <sys/inotify.h> |
10 #include <sys/ioctl.h> | 10 #include <sys/ioctl.h> |
11 #include <sys/select.h> | 11 #include <sys/select.h> |
12 #include <unistd.h> | 12 #include <unistd.h> |
13 | 13 |
14 #include <algorithm> | 14 #include <algorithm> |
15 #include <set> | 15 #include <set> |
16 #include <utility> | 16 #include <utility> |
17 #include <vector> | 17 #include <vector> |
18 | 18 |
19 #include "base/eintr_wrapper.h" | 19 #include "base/eintr_wrapper.h" |
20 #include "base/file_path.h" | 20 #include "base/file_path.h" |
21 #include "base/file_util.h" | 21 #include "base/file_util.h" |
22 #include "base/hash_tables.h" | 22 #include "base/hash_tables.h" |
23 #include "base/lazy_instance.h" | 23 #include "base/lazy_instance.h" |
24 #include "base/logging.h" | 24 #include "base/logging.h" |
25 #include "base/message_loop.h" | 25 #include "base/message_loop.h" |
| 26 #include "base/message_loop_proxy.h" |
26 #include "base/scoped_ptr.h" | 27 #include "base/scoped_ptr.h" |
27 #include "base/synchronization/lock.h" | 28 #include "base/synchronization/lock.h" |
28 #include "base/task.h" | 29 #include "base/task.h" |
29 #include "base/threading/thread.h" | 30 #include "base/threading/thread.h" |
30 | 31 |
31 namespace { | 32 namespace { |
32 | 33 |
33 class FilePathWatcherImpl; | 34 class FilePathWatcherImpl; |
34 | 35 |
35 // Singleton to manage all inotify watches. | 36 // Singleton to manage all inotify watches. |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
88 // the currently watched path for |fired_watch|. The flag |created| is true if | 89 // the currently watched path for |fired_watch|. The flag |created| is true if |
89 // the object appears, and |is_directory| is set when the event refers to a | 90 // the object appears, and |is_directory| is set when the event refers to a |
90 // directory. | 91 // directory. |
91 void OnFilePathChanged(InotifyReader::Watch fired_watch, | 92 void OnFilePathChanged(InotifyReader::Watch fired_watch, |
92 const FilePath::StringType& child, | 93 const FilePath::StringType& child, |
93 bool created, | 94 bool created, |
94 bool is_directory); | 95 bool is_directory); |
95 | 96 |
96 // Start watching |path| for changes and notify |delegate| on each change. | 97 // Start watching |path| for changes and notify |delegate| on each change. |
97 // Returns true if watch for |path| has been added successfully. | 98 // Returns true if watch for |path| has been added successfully. |
98 virtual bool Watch(const FilePath& path, FilePathWatcher::Delegate* delegate); | 99 virtual bool Watch(const FilePath& path, |
| 100 FilePathWatcher::Delegate* delegate, |
| 101 base::MessageLoopProxy* loop) OVERRIDE; |
99 | 102 |
100 // Cancel the watch. This unregisters the instance with InotifyReader. | 103 // Cancel the watch. This unregisters the instance with InotifyReader. |
101 virtual void Cancel(); | 104 virtual void Cancel() OVERRIDE; |
102 | 105 |
103 private: | 106 private: |
104 virtual ~FilePathWatcherImpl() {} | 107 virtual ~FilePathWatcherImpl() {} |
105 | 108 |
106 // Inotify watches are installed for all directory components of |target_|. A | 109 // Inotify watches are installed for all directory components of |target_|. A |
107 // WatchEntry instance holds the watch descriptor for a component and the | 110 // WatchEntry instance holds the watch descriptor for a component and the |
108 // subdirectory for that identifies the next component. | 111 // subdirectory for that identifies the next component. |
109 struct WatchEntry { | 112 struct WatchEntry { |
110 WatchEntry(InotifyReader::Watch watch, const FilePath::StringType& subdir) | 113 WatchEntry(InotifyReader::Watch watch, const FilePath::StringType& subdir) |
111 : watch_(watch), | 114 : watch_(watch), |
(...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
273 void InotifyReader::OnInotifyEvent(const inotify_event* event) { | 276 void InotifyReader::OnInotifyEvent(const inotify_event* event) { |
274 if (event->mask & IN_IGNORED) | 277 if (event->mask & IN_IGNORED) |
275 return; | 278 return; |
276 | 279 |
277 FilePath::StringType child(event->len ? event->name : FILE_PATH_LITERAL("")); | 280 FilePath::StringType child(event->len ? event->name : FILE_PATH_LITERAL("")); |
278 base::AutoLock auto_lock(lock_); | 281 base::AutoLock auto_lock(lock_); |
279 | 282 |
280 for (WatcherSet::iterator watcher = watchers_[event->wd].begin(); | 283 for (WatcherSet::iterator watcher = watchers_[event->wd].begin(); |
281 watcher != watchers_[event->wd].end(); | 284 watcher != watchers_[event->wd].end(); |
282 ++watcher) { | 285 ++watcher) { |
283 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, | 286 (*watcher)->OnFilePathChanged(event->wd, |
284 NewRunnableMethod(*watcher, | 287 child, |
285 &FilePathWatcherImpl::OnFilePathChanged, | 288 event->mask & (IN_CREATE | IN_MOVED_TO), |
286 event->wd, | 289 event->mask & IN_ISDIR); |
287 child, | |
288 event->mask & (IN_CREATE | IN_MOVED_TO), | |
289 event->mask & IN_ISDIR)); | |
290 } | 290 } |
291 } | 291 } |
292 | 292 |
293 FilePathWatcherImpl::FilePathWatcherImpl() | 293 FilePathWatcherImpl::FilePathWatcherImpl() |
294 : delegate_(NULL) { | 294 : delegate_(NULL) { |
295 } | 295 } |
296 | 296 |
297 void FilePathWatcherImpl::OnFilePathChanged(InotifyReader::Watch fired_watch, | 297 void FilePathWatcherImpl::OnFilePathChanged( |
298 const FilePath::StringType& child, | 298 InotifyReader::Watch fired_watch, |
299 bool created, | 299 const FilePath::StringType& child, |
300 bool is_directory) { | 300 bool created, |
301 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 301 bool is_directory) { |
| 302 |
| 303 if (!message_loop()->BelongsToCurrentThread()) { |
| 304 // Switch to message_loop_ to access watches_ safely. |
| 305 message_loop()->PostTask(FROM_HERE, |
| 306 NewRunnableMethod(this, |
| 307 &FilePathWatcherImpl::OnFilePathChanged, |
| 308 fired_watch, |
| 309 child, |
| 310 created, |
| 311 is_directory)); |
| 312 return; |
| 313 } |
| 314 |
| 315 DCHECK(MessageLoopForIO::current()); |
302 | 316 |
303 // Find the entry in |watches_| that corresponds to |fired_watch|. | 317 // Find the entry in |watches_| that corresponds to |fired_watch|. |
304 WatchVector::const_iterator watch_entry(watches_.begin()); | 318 WatchVector::const_iterator watch_entry(watches_.begin()); |
305 for ( ; watch_entry != watches_.end(); ++watch_entry) { | 319 for ( ; watch_entry != watches_.end(); ++watch_entry) { |
306 if (fired_watch == watch_entry->watch_) | 320 if (fired_watch == watch_entry->watch_) |
307 break; | 321 break; |
308 } | 322 } |
309 | 323 |
310 // If this notification is from a previous generation of watches or the watch | 324 // If this notification is from a previous generation of watches or the watch |
311 // has been cancelled (|watches_| is empty then), bail out. | 325 // has been cancelled (|watches_| is empty then), bail out. |
(...skipping 22 matching lines...) Expand all Loading... |
334 // - One of the parent directories appears. The event corresponding to the | 348 // - One of the parent directories appears. The event corresponding to the |
335 // target appearing might have been missed in this case, so recheck. | 349 // target appearing might have been missed in this case, so recheck. |
336 if (target_changed || | 350 if (target_changed || |
337 (change_on_target_path && !created) || | 351 (change_on_target_path && !created) || |
338 (change_on_target_path && file_util::PathExists(target_))) { | 352 (change_on_target_path && file_util::PathExists(target_))) { |
339 delegate_->OnFilePathChanged(target_); | 353 delegate_->OnFilePathChanged(target_); |
340 } | 354 } |
341 } | 355 } |
342 | 356 |
343 bool FilePathWatcherImpl::Watch(const FilePath& path, | 357 bool FilePathWatcherImpl::Watch(const FilePath& path, |
344 FilePathWatcher::Delegate* delegate) { | 358 FilePathWatcher::Delegate* delegate, |
345 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 359 base::MessageLoopProxy*) { |
346 DCHECK(target_.empty()); | 360 DCHECK(target_.empty()); |
| 361 DCHECK(MessageLoopForIO::current()); |
347 | 362 |
| 363 set_message_loop(base::MessageLoopProxy::CreateForCurrentThread()); |
348 delegate_ = delegate; | 364 delegate_ = delegate; |
349 target_ = path; | 365 target_ = path; |
350 std::vector<FilePath::StringType> comps; | 366 std::vector<FilePath::StringType> comps; |
351 target_.GetComponents(&comps); | 367 target_.GetComponents(&comps); |
352 DCHECK(!comps.empty()); | 368 DCHECK(!comps.empty()); |
353 for (std::vector<FilePath::StringType>::const_iterator comp(++comps.begin()); | 369 for (std::vector<FilePath::StringType>::const_iterator comp(++comps.begin()); |
354 comp != comps.end(); ++comp) { | 370 comp != comps.end(); ++comp) { |
355 watches_.push_back(WatchEntry(InotifyReader::kInvalidWatch, *comp)); | 371 watches_.push_back(WatchEntry(InotifyReader::kInvalidWatch, *comp)); |
356 } | 372 } |
357 watches_.push_back(WatchEntry(InotifyReader::kInvalidWatch, | 373 watches_.push_back(WatchEntry(InotifyReader::kInvalidWatch, |
358 FilePath::StringType())); | 374 FilePath::StringType())); |
359 return UpdateWatches(); | 375 return UpdateWatches(); |
360 } | 376 } |
361 | 377 |
362 void FilePathWatcherImpl::Cancel() { | 378 void FilePathWatcherImpl::Cancel() { |
363 // Switch to the file thread if necessary so we can access |watches_|. | 379 if (!message_loop().get()) { |
364 if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) { | 380 // Watch was never called, so exit. |
365 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, | 381 return; |
| 382 } |
| 383 |
| 384 // Switch to the message_loop_ if necessary so we can access |watches_|. |
| 385 if (!message_loop()->BelongsToCurrentThread()) { |
| 386 message_loop()->PostTask(FROM_HERE, |
366 NewRunnableMethod(this, &FilePathWatcherImpl::Cancel)); | 387 NewRunnableMethod(this, &FilePathWatcherImpl::Cancel)); |
367 return; | 388 return; |
368 } | 389 } |
369 | 390 |
370 for (WatchVector::iterator watch_entry(watches_.begin()); | 391 for (WatchVector::iterator watch_entry(watches_.begin()); |
371 watch_entry != watches_.end(); ++watch_entry) { | 392 watch_entry != watches_.end(); ++watch_entry) { |
372 if (watch_entry->watch_ != InotifyReader::kInvalidWatch) | 393 if (watch_entry->watch_ != InotifyReader::kInvalidWatch) |
373 g_inotify_reader.Get().RemoveWatch(watch_entry->watch_, this); | 394 g_inotify_reader.Get().RemoveWatch(watch_entry->watch_, this); |
374 } | 395 } |
375 watches_.clear(); | 396 watches_.clear(); |
376 delegate_ = NULL; | 397 delegate_ = NULL; |
377 target_.clear(); | 398 target_.clear(); |
378 } | 399 } |
379 | 400 |
380 bool FilePathWatcherImpl::UpdateWatches() { | 401 bool FilePathWatcherImpl::UpdateWatches() { |
381 // Ensure this runs on the file thread exclusively in order to avoid | 402 // Ensure this runs on the message_loop_ exclusively in order to avoid |
382 // concurrency issues. | 403 // concurrency issues. |
383 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | 404 DCHECK(message_loop()->BelongsToCurrentThread()); |
384 | 405 |
385 // Walk the list of watches and update them as we go. | 406 // Walk the list of watches and update them as we go. |
386 FilePath path(FILE_PATH_LITERAL("/")); | 407 FilePath path(FILE_PATH_LITERAL("/")); |
387 bool path_valid = true; | 408 bool path_valid = true; |
388 for (WatchVector::iterator watch_entry(watches_.begin()); | 409 for (WatchVector::iterator watch_entry(watches_.begin()); |
389 watch_entry != watches_.end(); ++watch_entry) { | 410 watch_entry != watches_.end(); ++watch_entry) { |
390 InotifyReader::Watch old_watch = watch_entry->watch_; | 411 InotifyReader::Watch old_watch = watch_entry->watch_; |
391 if (path_valid) { | 412 if (path_valid) { |
392 watch_entry->watch_ = g_inotify_reader.Get().AddWatch(path, this); | 413 watch_entry->watch_ = g_inotify_reader.Get().AddWatch(path, this); |
393 if (watch_entry->watch_ == InotifyReader::kInvalidWatch) { | 414 if (watch_entry->watch_ == InotifyReader::kInvalidWatch) { |
(...skipping 10 matching lines...) Expand all Loading... |
404 } | 425 } |
405 | 426 |
406 return true; | 427 return true; |
407 } | 428 } |
408 | 429 |
409 } // namespace | 430 } // namespace |
410 | 431 |
411 FilePathWatcher::FilePathWatcher() { | 432 FilePathWatcher::FilePathWatcher() { |
412 impl_ = new FilePathWatcherImpl(); | 433 impl_ = new FilePathWatcherImpl(); |
413 } | 434 } |
OLD | NEW |