OLD | NEW |
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 Loading... |
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 Loading... |
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 Loading... |
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 } |
OLD | NEW |