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

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: Tested and updated after changes from 6670081 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 // Deletion of the FilePathWatcher will call Cancel() to dispose of this
84 // object in the right thread. In that case, |this| is already cleaned up when
85 // the dtor enters.
86 // However, it can happen that the file thread exits before Cancel() is called,
87 // and by then Cancel()'s posted Task will never execute. Therefore we observe
88 // the thread's MessageLoop and cleanup early, if necessary.
89 class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate,
90 public MessageLoop::DestructionObserver {
84 public: 91 public:
85 FilePathWatcherImpl(); 92 FilePathWatcherImpl();
86 93
87 // Called for each event coming from the watch. |fired_watch| identifies the 94 // 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 95 // 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 96 // 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 97 // the object appears, and |is_directory| is set when the event refers to a
91 // directory. 98 // directory.
92 void OnFilePathChanged(InotifyReader::Watch fired_watch, 99 void OnFilePathChanged(InotifyReader::Watch fired_watch,
93 const FilePath::StringType& child, 100 const FilePath::StringType& child,
94 bool created, 101 bool created,
95 bool is_directory); 102 bool is_directory);
96 103
97 // Start watching |path| for changes and notify |delegate| on each change. 104 // Start watching |path| for changes and notify |delegate| on each change.
98 // Returns true if watch for |path| has been added successfully. 105 // Returns true if watch for |path| has been added successfully.
99 virtual bool Watch(const FilePath& path, 106 virtual bool Watch(const FilePath& path,
100 FilePathWatcher::Delegate* delegate, 107 FilePathWatcher::Delegate* delegate,
101 base::MessageLoopProxy* loop) OVERRIDE; 108 base::MessageLoopProxy* loop) OVERRIDE;
102 109
103 // Cancel the watch. This unregisters the instance with InotifyReader. 110 // Cancel the watch. This unregisters the instance with InotifyReader.
104 virtual void Cancel() OVERRIDE; 111 virtual void Cancel() OVERRIDE;
105 112
113 // MessageLoop::DestructionObserver overrides.
114 // This allows cleaning up when the |message_loop_| thread is
115 // shutting down.
116 virtual void WillDestroyCurrentMessageLoop();
117
106 private: 118 private:
107 virtual ~FilePathWatcherImpl() {} 119 virtual ~FilePathWatcherImpl();
120
121 // Cleans up and stops observing the |message_loop_| thread.
122 void CancelInternal();
108 123
109 // Inotify watches are installed for all directory components of |target_|. A 124 // Inotify watches are installed for all directory components of |target_|. A
110 // WatchEntry instance holds the watch descriptor for a component and the 125 // WatchEntry instance holds the watch descriptor for a component and the
111 // subdirectory for that identifies the next component. 126 // subdirectory for that identifies the next component.
112 struct WatchEntry { 127 struct WatchEntry {
113 WatchEntry(InotifyReader::Watch watch, const FilePath::StringType& subdir) 128 WatchEntry(InotifyReader::Watch watch, const FilePath::StringType& subdir)
114 : watch_(watch), 129 : watch_(watch),
115 subdir_(subdir) {} 130 subdir_(subdir) {}
116 131
117 InotifyReader::Watch watch_; 132 InotifyReader::Watch watch_;
(...skipping 238 matching lines...) Expand 10 before | Expand all | Expand 10 after
356 371
357 bool FilePathWatcherImpl::Watch(const FilePath& path, 372 bool FilePathWatcherImpl::Watch(const FilePath& path,
358 FilePathWatcher::Delegate* delegate, 373 FilePathWatcher::Delegate* delegate,
359 base::MessageLoopProxy*) { 374 base::MessageLoopProxy*) {
360 DCHECK(target_.empty()); 375 DCHECK(target_.empty());
361 DCHECK(MessageLoopForIO::current()); 376 DCHECK(MessageLoopForIO::current());
362 377
363 set_message_loop(base::MessageLoopProxy::CreateForCurrentThread()); 378 set_message_loop(base::MessageLoopProxy::CreateForCurrentThread());
364 delegate_ = delegate; 379 delegate_ = delegate;
365 target_ = path; 380 target_ = path;
381 MessageLoop::current()->AddDestructionObserver(this);
382
366 std::vector<FilePath::StringType> comps; 383 std::vector<FilePath::StringType> comps;
367 target_.GetComponents(&comps); 384 target_.GetComponents(&comps);
368 DCHECK(!comps.empty()); 385 DCHECK(!comps.empty());
369 for (std::vector<FilePath::StringType>::const_iterator comp(++comps.begin()); 386 for (std::vector<FilePath::StringType>::const_iterator comp(++comps.begin());
370 comp != comps.end(); ++comp) { 387 comp != comps.end(); ++comp) {
371 watches_.push_back(WatchEntry(InotifyReader::kInvalidWatch, *comp)); 388 watches_.push_back(WatchEntry(InotifyReader::kInvalidWatch, *comp));
372 } 389 }
373 watches_.push_back(WatchEntry(InotifyReader::kInvalidWatch, 390 watches_.push_back(WatchEntry(InotifyReader::kInvalidWatch,
374 FilePath::StringType())); 391 FilePath::StringType()));
375 return UpdateWatches(); 392 return UpdateWatches();
376 } 393 }
377 394
395 FilePathWatcherImpl::~FilePathWatcherImpl() {
396 // This covers the case when the Task is posted on Cancel(), but is not
397 // executed during shutdown, and |this| is just Released.
Mattias Nissler (ping if slow) 2011/03/21 16:07:45 But shouldn't we then see the WillDestroyCurrentMe
Joao da Silva 2011/03/22 10:10:08 I saw a code path that actually triggers this, tho
398 if (delegate_)
399 CancelInternal();
400 }
401
378 void FilePathWatcherImpl::Cancel() { 402 void FilePathWatcherImpl::Cancel() {
379 if (!message_loop().get()) { 403 if (!delegate_) {
380 // Watch was never called, so exit. 404 // Watch was never called, or the |message_loop_| thread is already gone.
381 return; 405 return;
382 } 406 }
383 407
384 // Switch to the message_loop_ if necessary so we can access |watches_|. 408 // Switch to the message_loop_ if necessary so we can access |watches_|.
385 if (!message_loop()->BelongsToCurrentThread()) { 409 if (!message_loop()->BelongsToCurrentThread()) {
386 message_loop()->PostTask(FROM_HERE, 410 message_loop()->PostTask(FROM_HERE,
387 NewRunnableMethod(this, &FilePathWatcherImpl::Cancel)); 411 NewRunnableMethod(this, &FilePathWatcherImpl::CancelInternal));
388 return; 412 } else {
413 CancelInternal();
389 } 414 }
415 }
416
417 void FilePathWatcherImpl::CancelInternal() {
418 MessageLoop::current()->RemoveDestructionObserver(this);
390 419
391 for (WatchVector::iterator watch_entry(watches_.begin()); 420 for (WatchVector::iterator watch_entry(watches_.begin());
392 watch_entry != watches_.end(); ++watch_entry) { 421 watch_entry != watches_.end(); ++watch_entry) {
393 if (watch_entry->watch_ != InotifyReader::kInvalidWatch) 422 if (watch_entry->watch_ != InotifyReader::kInvalidWatch)
394 g_inotify_reader.Get().RemoveWatch(watch_entry->watch_, this); 423 g_inotify_reader.Get().RemoveWatch(watch_entry->watch_, this);
395 } 424 }
396 watches_.clear(); 425 watches_.clear();
397 delegate_ = NULL; 426 delegate_ = NULL;
398 target_.clear(); 427 target_.clear();
399 } 428 }
400 429
430 void FilePathWatcherImpl::WillDestroyCurrentMessageLoop() {
431 CancelInternal();
432 }
433
401 bool FilePathWatcherImpl::UpdateWatches() { 434 bool FilePathWatcherImpl::UpdateWatches() {
402 // Ensure this runs on the message_loop_ exclusively in order to avoid 435 // Ensure this runs on the message_loop_ exclusively in order to avoid
403 // concurrency issues. 436 // concurrency issues.
404 DCHECK(message_loop()->BelongsToCurrentThread()); 437 DCHECK(message_loop()->BelongsToCurrentThread());
405 438
406 // Walk the list of watches and update them as we go. 439 // Walk the list of watches and update them as we go.
407 FilePath path(FILE_PATH_LITERAL("/")); 440 FilePath path(FILE_PATH_LITERAL("/"));
408 bool path_valid = true; 441 bool path_valid = true;
409 for (WatchVector::iterator watch_entry(watches_.begin()); 442 for (WatchVector::iterator watch_entry(watches_.begin());
410 watch_entry != watches_.end(); ++watch_entry) { 443 watch_entry != watches_.end(); ++watch_entry) {
(...skipping 14 matching lines...) Expand all
425 } 458 }
426 459
427 return true; 460 return true;
428 } 461 }
429 462
430 } // namespace 463 } // namespace
431 464
432 FilePathWatcher::FilePathWatcher() { 465 FilePathWatcher::FilePathWatcher() {
433 impl_ = new FilePathWatcherImpl(); 466 impl_ = new FilePathWatcherImpl();
434 } 467 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698