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

Side by Side Diff: content/common/file_path_watcher/file_path_watcher_inotify.cc

Issue 6825063: Patch for bug 74983 (among others) to be applied to M11 696 branch. (Closed) Base URL: svn://svn.chromium.org/chrome/branches/696/src
Patch Set: intentionally disabled mac tests. tested by hand. Created 9 years, 8 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) 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 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
72 73
73 // Use self-pipe trick to unblock select during shutdown. 74 // Use self-pipe trick to unblock select during shutdown.
74 int shutdown_pipe_[2]; 75 int shutdown_pipe_[2];
75 76
76 // Flag set to true when startup was successful. 77 // Flag set to true when startup was successful.
77 bool valid_; 78 bool valid_;
78 79
79 DISALLOW_COPY_AND_ASSIGN(InotifyReader); 80 DISALLOW_COPY_AND_ASSIGN(InotifyReader);
80 }; 81 };
81 82
82 class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate { 83 class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate,
84 public MessageLoop::DestructionObserver {
83 public: 85 public:
84 FilePathWatcherImpl(); 86 FilePathWatcherImpl();
85 87
86 // Called for each event coming from the watch. |fired_watch| identifies the 88 // Called for each event coming from the watch. |fired_watch| identifies the
87 // watch that fired, |child| indicates what has changed, and is relative to 89 // watch that fired, |child| indicates what has changed, and is relative to
88 // the currently watched path for |fired_watch|. The flag |created| is true if 90 // 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 91 // the object appears, and |is_directory| is set when the event refers to a
90 // directory. 92 // directory.
91 void OnFilePathChanged(InotifyReader::Watch fired_watch, 93 void OnFilePathChanged(InotifyReader::Watch fired_watch,
92 const FilePath::StringType& child, 94 const FilePath::StringType& child,
93 bool created, 95 bool created,
94 bool is_directory); 96 bool is_directory);
95 97
96 // Start watching |path| for changes and notify |delegate| on each change. 98 // Start watching |path| for changes and notify |delegate| on each change.
97 // Returns true if watch for |path| has been added successfully. 99 // Returns true if watch for |path| has been added successfully.
98 virtual bool Watch(const FilePath& path, FilePathWatcher::Delegate* delegate); 100 virtual bool Watch(const FilePath& path,
101 FilePathWatcher::Delegate* delegate) 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;
105
106 // Deletion of the FilePathWatcher will call Cancel() to dispose of this
107 // object in the right thread. This also observes destruction of the required
108 // cleanup thread, in case it quits before Cancel() is called.
109 virtual void WillDestroyCurrentMessageLoop() OVERRIDE;
102 110
103 private: 111 private:
104 virtual ~FilePathWatcherImpl() {} 112 virtual ~FilePathWatcherImpl() {}
105 113
114 // Cleans up and stops observing the |message_loop_| thread.
115 void CancelOnMessageLoopThread() OVERRIDE;
116
106 // Inotify watches are installed for all directory components of |target_|. A 117 // Inotify watches are installed for all directory components of |target_|. A
107 // WatchEntry instance holds the watch descriptor for a component and the 118 // WatchEntry instance holds the watch descriptor for a component and the
108 // subdirectory for that identifies the next component. 119 // subdirectory for that identifies the next component.
109 struct WatchEntry { 120 struct WatchEntry {
110 WatchEntry(InotifyReader::Watch watch, const FilePath::StringType& subdir) 121 WatchEntry(InotifyReader::Watch watch, const FilePath::StringType& subdir)
111 : watch_(watch), 122 : watch_(watch),
112 subdir_(subdir) {} 123 subdir_(subdir) {}
113 124
114 InotifyReader::Watch watch_; 125 InotifyReader::Watch watch_;
115 FilePath::StringType subdir_; 126 FilePath::StringType subdir_;
(...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after
273 void InotifyReader::OnInotifyEvent(const inotify_event* event) { 284 void InotifyReader::OnInotifyEvent(const inotify_event* event) {
274 if (event->mask & IN_IGNORED) 285 if (event->mask & IN_IGNORED)
275 return; 286 return;
276 287
277 FilePath::StringType child(event->len ? event->name : FILE_PATH_LITERAL("")); 288 FilePath::StringType child(event->len ? event->name : FILE_PATH_LITERAL(""));
278 base::AutoLock auto_lock(lock_); 289 base::AutoLock auto_lock(lock_);
279 290
280 for (WatcherSet::iterator watcher = watchers_[event->wd].begin(); 291 for (WatcherSet::iterator watcher = watchers_[event->wd].begin();
281 watcher != watchers_[event->wd].end(); 292 watcher != watchers_[event->wd].end();
282 ++watcher) { 293 ++watcher) {
283 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 294 (*watcher)->OnFilePathChanged(event->wd,
284 NewRunnableMethod(*watcher, 295 child,
285 &FilePathWatcherImpl::OnFilePathChanged, 296 event->mask & (IN_CREATE | IN_MOVED_TO),
286 event->wd, 297 event->mask & IN_ISDIR);
287 child,
288 event->mask & (IN_CREATE | IN_MOVED_TO),
289 event->mask & IN_ISDIR));
290 } 298 }
291 } 299 }
292 300
293 FilePathWatcherImpl::FilePathWatcherImpl() 301 FilePathWatcherImpl::FilePathWatcherImpl()
294 : delegate_(NULL) { 302 : delegate_(NULL) {
295 } 303 }
296 304
297 void FilePathWatcherImpl::OnFilePathChanged(InotifyReader::Watch fired_watch, 305 void FilePathWatcherImpl::OnFilePathChanged(
298 const FilePath::StringType& child, 306 InotifyReader::Watch fired_watch,
299 bool created, 307 const FilePath::StringType& child,
300 bool is_directory) { 308 bool created,
301 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 309 bool is_directory) {
310
311 if (!message_loop()->BelongsToCurrentThread()) {
312 // Switch to message_loop_ to access watches_ safely.
313 message_loop()->PostTask(FROM_HERE,
314 NewRunnableMethod(this,
315 &FilePathWatcherImpl::OnFilePathChanged,
316 fired_watch,
317 child,
318 created,
319 is_directory));
320 return;
321 }
322
323 DCHECK(MessageLoopForIO::current());
302 324
303 // Find the entry in |watches_| that corresponds to |fired_watch|. 325 // Find the entry in |watches_| that corresponds to |fired_watch|.
304 WatchVector::const_iterator watch_entry(watches_.begin()); 326 WatchVector::const_iterator watch_entry(watches_.begin());
305 for ( ; watch_entry != watches_.end(); ++watch_entry) { 327 for ( ; watch_entry != watches_.end(); ++watch_entry) {
306 if (fired_watch == watch_entry->watch_) 328 if (fired_watch == watch_entry->watch_)
307 break; 329 break;
308 } 330 }
309 331
310 // If this notification is from a previous generation of watches or the watch 332 // If this notification is from a previous generation of watches or the watch
311 // has been cancelled (|watches_| is empty then), bail out. 333 // has been cancelled (|watches_| is empty then), bail out.
312 if (watch_entry == watches_.end()) 334 if (watch_entry == watches_.end())
313 return; 335 return;
314 336
315 // Check whether a path component of |target_| changed. 337 // Check whether a path component of |target_| changed.
316 bool change_on_target_path = child.empty() || child == watch_entry->subdir_; 338 bool change_on_target_path = child.empty() || child == watch_entry->subdir_;
317 339
318 // Check whether the change references |target_| or a direct child. 340 // Check whether the change references |target_| or a direct child.
319 DCHECK(watch_entry->subdir_.empty() || (watch_entry + 1) != watches_.end()); 341 DCHECK(watch_entry->subdir_.empty() || (watch_entry + 1) != watches_.end());
320 bool target_changed = watch_entry->subdir_.empty() || 342 bool target_changed = watch_entry->subdir_.empty() ||
321 (watch_entry->subdir_ == child && (++watch_entry)->subdir_.empty()); 343 (watch_entry->subdir_ == child && (++watch_entry)->subdir_.empty());
322 344
323 // Update watches if a directory component of the |target_| path (dis)appears. 345 // Update watches if a directory component of the |target_| path (dis)appears.
324 if (is_directory && change_on_target_path && !UpdateWatches()) { 346 if (is_directory && change_on_target_path && !UpdateWatches()) {
325 delegate_->OnError(); 347 delegate_->OnFilePathError(target_);
326 return; 348 return;
327 } 349 }
328 350
329 // Report the following events: 351 // Report the following events:
330 // - The target or a direct child of the target got changed (in case the 352 // - The target or a direct child of the target got changed (in case the
331 // watched path refers to a directory). 353 // watched path refers to a directory).
332 // - One of the parent directories got moved or deleted, since the target 354 // - One of the parent directories got moved or deleted, since the target
333 // disappears in this case. 355 // disappears in this case.
334 // - One of the parent directories appears. The event corresponding to the 356 // - One of the parent directories appears. The event corresponding to the
335 // target appearing might have been missed in this case, so recheck. 357 // target appearing might have been missed in this case, so recheck.
336 if (target_changed || 358 if (target_changed ||
337 (change_on_target_path && !created) || 359 (change_on_target_path && !created) ||
338 (change_on_target_path && file_util::PathExists(target_))) { 360 (change_on_target_path && file_util::PathExists(target_))) {
339 delegate_->OnFilePathChanged(target_); 361 delegate_->OnFilePathChanged(target_);
340 } 362 }
341 } 363 }
342 364
343 bool FilePathWatcherImpl::Watch(const FilePath& path, 365 bool FilePathWatcherImpl::Watch(const FilePath& path,
344 FilePathWatcher::Delegate* delegate) { 366 FilePathWatcher::Delegate* delegate) {
345 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
346 DCHECK(target_.empty()); 367 DCHECK(target_.empty());
368 DCHECK(MessageLoopForIO::current());
347 369
370 set_message_loop(base::MessageLoopProxy::CreateForCurrentThread());
348 delegate_ = delegate; 371 delegate_ = delegate;
349 target_ = path; 372 target_ = path;
373 MessageLoop::current()->AddDestructionObserver(this);
374
350 std::vector<FilePath::StringType> comps; 375 std::vector<FilePath::StringType> comps;
351 target_.GetComponents(&comps); 376 target_.GetComponents(&comps);
352 DCHECK(!comps.empty()); 377 DCHECK(!comps.empty());
353 for (std::vector<FilePath::StringType>::const_iterator comp(++comps.begin()); 378 for (std::vector<FilePath::StringType>::const_iterator comp(++comps.begin());
354 comp != comps.end(); ++comp) { 379 comp != comps.end(); ++comp) {
355 watches_.push_back(WatchEntry(InotifyReader::kInvalidWatch, *comp)); 380 watches_.push_back(WatchEntry(InotifyReader::kInvalidWatch, *comp));
356 } 381 }
357 watches_.push_back(WatchEntry(InotifyReader::kInvalidWatch, 382 watches_.push_back(WatchEntry(InotifyReader::kInvalidWatch,
358 FilePath::StringType())); 383 FilePath::StringType()));
359 return UpdateWatches(); 384 return UpdateWatches();
360 } 385 }
361 386
362 void FilePathWatcherImpl::Cancel() { 387 void FilePathWatcherImpl::Cancel() {
363 // Switch to the file thread if necessary so we can access |watches_|. 388 if (!delegate_) {
364 if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) { 389 // Watch was never called, or the |message_loop_| thread is already gone.
365 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 390 set_cancelled();
366 NewRunnableMethod(this, &FilePathWatcherImpl::Cancel));
367 return; 391 return;
368 } 392 }
369 393
370 for (WatchVector::iterator watch_entry(watches_.begin()); 394 // Switch to the message_loop_ if necessary so we can access |watches_|.
371 watch_entry != watches_.end(); ++watch_entry) { 395 if (!message_loop()->BelongsToCurrentThread()) {
372 if (watch_entry->watch_ != InotifyReader::kInvalidWatch) 396 message_loop()->PostTask(FROM_HERE,
373 g_inotify_reader.Get().RemoveWatch(watch_entry->watch_, this); 397 new FilePathWatcher::CancelTask(this));
398 } else {
399 CancelOnMessageLoopThread();
374 } 400 }
375 watches_.clear(); 401 }
376 delegate_ = NULL; 402
377 target_.clear(); 403 void FilePathWatcherImpl::CancelOnMessageLoopThread() {
404 if (!is_cancelled()) {
405 set_cancelled();
406 MessageLoop::current()->RemoveDestructionObserver(this);
407
408 for (WatchVector::iterator watch_entry(watches_.begin());
409 watch_entry != watches_.end(); ++watch_entry) {
410 if (watch_entry->watch_ != InotifyReader::kInvalidWatch)
411 g_inotify_reader.Get().RemoveWatch(watch_entry->watch_, this);
412 }
413 watches_.clear();
414 delegate_ = NULL;
415 target_.clear();
416 }
417 }
418
419 void FilePathWatcherImpl::WillDestroyCurrentMessageLoop() {
420 CancelOnMessageLoopThread();
378 } 421 }
379 422
380 bool FilePathWatcherImpl::UpdateWatches() { 423 bool FilePathWatcherImpl::UpdateWatches() {
381 // Ensure this runs on the file thread exclusively in order to avoid 424 // Ensure this runs on the message_loop_ exclusively in order to avoid
382 // concurrency issues. 425 // concurrency issues.
383 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 426 DCHECK(message_loop()->BelongsToCurrentThread());
384 427
385 // Walk the list of watches and update them as we go. 428 // Walk the list of watches and update them as we go.
386 FilePath path(FILE_PATH_LITERAL("/")); 429 FilePath path(FILE_PATH_LITERAL("/"));
387 bool path_valid = true; 430 bool path_valid = true;
388 for (WatchVector::iterator watch_entry(watches_.begin()); 431 for (WatchVector::iterator watch_entry(watches_.begin());
389 watch_entry != watches_.end(); ++watch_entry) { 432 watch_entry != watches_.end(); ++watch_entry) {
390 InotifyReader::Watch old_watch = watch_entry->watch_; 433 InotifyReader::Watch old_watch = watch_entry->watch_;
391 if (path_valid) { 434 if (path_valid) {
392 watch_entry->watch_ = g_inotify_reader.Get().AddWatch(path, this); 435 watch_entry->watch_ = g_inotify_reader.Get().AddWatch(path, this);
393 if (watch_entry->watch_ == InotifyReader::kInvalidWatch) { 436 if (watch_entry->watch_ == InotifyReader::kInvalidWatch) {
(...skipping 10 matching lines...) Expand all
404 } 447 }
405 448
406 return true; 449 return true;
407 } 450 }
408 451
409 } // namespace 452 } // namespace
410 453
411 FilePathWatcher::FilePathWatcher() { 454 FilePathWatcher::FilePathWatcher() {
412 impl_ = new FilePathWatcherImpl(); 455 impl_ = new FilePathWatcherImpl();
413 } 456 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698