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

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

Issue 6697020: Fixed shutdown concurrency issues in FilePathWatcher. (Closed) Base URL: http://git.chromium.org/git/chromium.git@trunk
Patch Set: Introduced CancelTask to simplify cleanup. Created 9 years, 9 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) 2011 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 "content/common/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>
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after
73 73
74 // Use self-pipe trick to unblock select during shutdown. 74 // Use self-pipe trick to unblock select during shutdown.
75 int shutdown_pipe_[2]; 75 int shutdown_pipe_[2];
76 76
77 // Flag set to true when startup was successful. 77 // Flag set to true when startup was successful.
78 bool valid_; 78 bool valid_;
79 79
80 DISALLOW_COPY_AND_ASSIGN(InotifyReader); 80 DISALLOW_COPY_AND_ASSIGN(InotifyReader);
81 }; 81 };
82 82
83 class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate { 83 class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate,
84 public MessageLoop::DestructionObserver {
84 public: 85 public:
85 FilePathWatcherImpl(); 86 FilePathWatcherImpl();
86 87
87 // 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
88 // 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
89 // 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
90 // 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
91 // directory. 92 // directory.
92 void OnFilePathChanged(InotifyReader::Watch fired_watch, 93 void OnFilePathChanged(InotifyReader::Watch fired_watch,
93 const FilePath::StringType& child, 94 const FilePath::StringType& child,
94 bool created, 95 bool created,
95 bool is_directory); 96 bool is_directory);
96 97
97 // Start watching |path| for changes and notify |delegate| on each change. 98 // Start watching |path| for changes and notify |delegate| on each change.
98 // Returns true if watch for |path| has been added successfully. 99 // Returns true if watch for |path| has been added successfully.
99 virtual bool Watch(const FilePath& path, 100 virtual bool Watch(const FilePath& path,
100 FilePathWatcher::Delegate* delegate, 101 FilePathWatcher::Delegate* delegate,
101 base::MessageLoopProxy* loop) OVERRIDE; 102 base::MessageLoopProxy* loop) OVERRIDE;
102 103
103 // Cancel the watch. This unregisters the instance with InotifyReader. 104 // Cancel the watch. This unregisters the instance with InotifyReader.
104 virtual void Cancel() OVERRIDE; 105 virtual void Cancel() OVERRIDE;
105 106
107 // Deletion of the FilePathWatcher will call Cancel() to dispose of this
108 // object in the right thread. This also observes destruction of the required
109 // cleanup thread, in case it quits before Cancel() is called.
110 virtual void WillDestroyCurrentMessageLoop() OVERRIDE;
111
106 private: 112 private:
107 virtual ~FilePathWatcherImpl() {} 113 virtual ~FilePathWatcherImpl() {}
108 114
115 // Cleans up and stops observing the |message_loop_| thread.
116 void CancelOnMessageLoopThread() OVERRIDE;
117
109 // Inotify watches are installed for all directory components of |target_|. A 118 // Inotify watches are installed for all directory components of |target_|. A
110 // WatchEntry instance holds the watch descriptor for a component and the 119 // WatchEntry instance holds the watch descriptor for a component and the
111 // subdirectory for that identifies the next component. 120 // subdirectory for that identifies the next component.
112 struct WatchEntry { 121 struct WatchEntry {
113 WatchEntry(InotifyReader::Watch watch, const FilePath::StringType& subdir) 122 WatchEntry(InotifyReader::Watch watch, const FilePath::StringType& subdir)
114 : watch_(watch), 123 : watch_(watch),
115 subdir_(subdir) {} 124 subdir_(subdir) {}
116 125
117 InotifyReader::Watch watch_; 126 InotifyReader::Watch watch_;
118 FilePath::StringType subdir_; 127 FilePath::StringType subdir_;
(...skipping 237 matching lines...) Expand 10 before | Expand all | Expand 10 after
356 365
357 bool FilePathWatcherImpl::Watch(const FilePath& path, 366 bool FilePathWatcherImpl::Watch(const FilePath& path,
358 FilePathWatcher::Delegate* delegate, 367 FilePathWatcher::Delegate* delegate,
359 base::MessageLoopProxy*) { 368 base::MessageLoopProxy*) {
360 DCHECK(target_.empty()); 369 DCHECK(target_.empty());
361 DCHECK(MessageLoopForIO::current()); 370 DCHECK(MessageLoopForIO::current());
362 371
363 set_message_loop(base::MessageLoopProxy::CreateForCurrentThread()); 372 set_message_loop(base::MessageLoopProxy::CreateForCurrentThread());
364 delegate_ = delegate; 373 delegate_ = delegate;
365 target_ = path; 374 target_ = path;
375 MessageLoop::current()->AddDestructionObserver(this);
376
366 std::vector<FilePath::StringType> comps; 377 std::vector<FilePath::StringType> comps;
367 target_.GetComponents(&comps); 378 target_.GetComponents(&comps);
368 DCHECK(!comps.empty()); 379 DCHECK(!comps.empty());
369 for (std::vector<FilePath::StringType>::const_iterator comp(++comps.begin()); 380 for (std::vector<FilePath::StringType>::const_iterator comp(++comps.begin());
370 comp != comps.end(); ++comp) { 381 comp != comps.end(); ++comp) {
371 watches_.push_back(WatchEntry(InotifyReader::kInvalidWatch, *comp)); 382 watches_.push_back(WatchEntry(InotifyReader::kInvalidWatch, *comp));
372 } 383 }
373 watches_.push_back(WatchEntry(InotifyReader::kInvalidWatch, 384 watches_.push_back(WatchEntry(InotifyReader::kInvalidWatch,
374 FilePath::StringType())); 385 FilePath::StringType()));
375 return UpdateWatches(); 386 return UpdateWatches();
376 } 387 }
377 388
378 void FilePathWatcherImpl::Cancel() { 389 void FilePathWatcherImpl::Cancel() {
379 if (!message_loop().get()) { 390 if (!delegate_) {
380 // Watch was never called, so exit. 391 // Watch was never called, or the |message_loop_| thread is already gone.
392 set_cancelled();
381 return; 393 return;
382 } 394 }
383 395
384 // Switch to the message_loop_ if necessary so we can access |watches_|. 396 // Switch to the message_loop_ if necessary so we can access |watches_|.
385 if (!message_loop()->BelongsToCurrentThread()) { 397 if (!message_loop()->BelongsToCurrentThread()) {
386 message_loop()->PostTask(FROM_HERE, 398 message_loop()->PostTask(FROM_HERE,
387 NewRunnableMethod(this, &FilePathWatcherImpl::Cancel)); 399 new FilePathWatcher::CancelTask(this));
388 return; 400 } else {
401 CancelOnMessageLoopThread();
389 } 402 }
403 }
390 404
391 for (WatchVector::iterator watch_entry(watches_.begin()); 405 void FilePathWatcherImpl::CancelOnMessageLoopThread() {
392 watch_entry != watches_.end(); ++watch_entry) { 406 if (!is_cancelled()) {
393 if (watch_entry->watch_ != InotifyReader::kInvalidWatch) 407 set_cancelled();
394 g_inotify_reader.Get().RemoveWatch(watch_entry->watch_, this); 408 MessageLoop::current()->RemoveDestructionObserver(this);
409
410 for (WatchVector::iterator watch_entry(watches_.begin());
411 watch_entry != watches_.end(); ++watch_entry) {
412 if (watch_entry->watch_ != InotifyReader::kInvalidWatch)
413 g_inotify_reader.Get().RemoveWatch(watch_entry->watch_, this);
414 }
415 watches_.clear();
416 delegate_ = NULL;
417 target_.clear();
395 } 418 }
396 watches_.clear(); 419 }
397 delegate_ = NULL; 420
398 target_.clear(); 421 void FilePathWatcherImpl::WillDestroyCurrentMessageLoop() {
422 CancelOnMessageLoopThread();
399 } 423 }
400 424
401 bool FilePathWatcherImpl::UpdateWatches() { 425 bool FilePathWatcherImpl::UpdateWatches() {
402 // Ensure this runs on the message_loop_ exclusively in order to avoid 426 // Ensure this runs on the message_loop_ exclusively in order to avoid
403 // concurrency issues. 427 // concurrency issues.
404 DCHECK(message_loop()->BelongsToCurrentThread()); 428 DCHECK(message_loop()->BelongsToCurrentThread());
405 429
406 // Walk the list of watches and update them as we go. 430 // Walk the list of watches and update them as we go.
407 FilePath path(FILE_PATH_LITERAL("/")); 431 FilePath path(FILE_PATH_LITERAL("/"));
408 bool path_valid = true; 432 bool path_valid = true;
(...skipping 16 matching lines...) Expand all
425 } 449 }
426 450
427 return true; 451 return true;
428 } 452 }
429 453
430 } // namespace 454 } // namespace
431 455
432 FilePathWatcher::FilePathWatcher() { 456 FilePathWatcher::FilePathWatcher() {
433 impl_ = new FilePathWatcherImpl(); 457 impl_ = new FilePathWatcherImpl();
434 } 458 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698