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

Side by Side Diff: content/common/file_path_watcher/file_path_watcher_win.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 "base/file_path.h" 7 #include "base/file_path.h"
8 #include "base/file_util.h" 8 #include "base/file_util.h"
9 #include "base/logging.h" 9 #include "base/logging.h"
10 #include "base/message_loop_proxy.h" 10 #include "base/message_loop_proxy.h"
11 #include "base/ref_counted.h" 11 #include "base/ref_counted.h"
12 #include "base/time.h" 12 #include "base/time.h"
13 #include "base/win/object_watcher.h" 13 #include "base/win/object_watcher.h"
14 14
15 namespace { 15 namespace {
16 16
17 // Deletion of the FilePathWatcher will call Cancel() to dispose of this
18 // object in the right thread. In that case, |this| is already cleaned up
19 // and |handle_| is INVALID_HANDLE_VALUE when the dtor enters.
20 //
21 // However, Cancel() might have to switch threads and its Task might never be
22 // called during shutdown. In that case, either |this| gets notified that the
23 // thread is shutting down and cleans up, or the Task (that wasn't executed)
24 // is deleted and releases a handle on |this|, triggering the dtor.
25 // In either case, we are on the right thread when cleaning up.
Mattias Nissler (ping if slow) 2011/03/21 16:07:45 same question as for the others
Joao da Silva 2011/03/22 10:10:08 Same reply :-)
17 class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate, 26 class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate,
18 public base::win::ObjectWatcher::Delegate { 27 public base::win::ObjectWatcher::Delegate,
28 public MessageLoop::DestructionObserver {
19 public: 29 public:
20 FilePathWatcherImpl() : delegate_(NULL), handle_(INVALID_HANDLE_VALUE) {} 30 FilePathWatcherImpl() : delegate_(NULL), handle_(INVALID_HANDLE_VALUE) {}
21 31
22 // FilePathWatcher::PlatformDelegate overrides. 32 // FilePathWatcher::PlatformDelegate overrides.
23 virtual bool Watch(const FilePath& path, 33 virtual bool Watch(const FilePath& path,
24 FilePathWatcher::Delegate* delegate, 34 FilePathWatcher::Delegate* delegate,
25 base::MessageLoopProxy* loop) OVERRIDE; 35 base::MessageLoopProxy* loop) OVERRIDE;
26 virtual void Cancel() OVERRIDE; 36 virtual void Cancel() OVERRIDE;
27 37
38 // MessageLoop::DestructionObserver overrides.
39 // This allows cleaning up when the |message_loop_| thread is shutting down.
40 virtual void WillDestroyCurrentMessageLoop();
41
28 // Callback from MessageLoopForIO. 42 // Callback from MessageLoopForIO.
29 virtual void OnObjectSignaled(HANDLE object); 43 virtual void OnObjectSignaled(HANDLE object);
30 44
31 private: 45 private:
32 virtual ~FilePathWatcherImpl(); 46 virtual ~FilePathWatcherImpl();
33 47
34 // Setup a watch handle for directory |dir|. Returns true if no fatal error 48 // Setup a watch handle for directory |dir|. Returns true if no fatal error
35 // occurs. |handle| will receive the handle value if |dir| is watchable, 49 // occurs. |handle| will receive the handle value if |dir| is watchable,
36 // otherwise INVALID_HANDLE_VALUE. 50 // otherwise INVALID_HANDLE_VALUE.
37 static bool SetupWatchHandle(const FilePath& dir, HANDLE* handle) 51 static bool SetupWatchHandle(const FilePath& dir, HANDLE* handle)
38 WARN_UNUSED_RESULT; 52 WARN_UNUSED_RESULT;
39 53
40 // (Re-)Initialize the watch handle. 54 // (Re-)Initialize the watch handle.
41 bool UpdateWatch() WARN_UNUSED_RESULT; 55 bool UpdateWatch() WARN_UNUSED_RESULT;
42 56
43 // Destroy the watch handle. 57 // Destroy the watch handle.
44 void DestroyWatch(); 58 void DestroyWatch();
45 59
60 // Cleans up and stops observing the |message_loop_| thread.
61 void CancelInternal();
62
46 // Delegate to notify upon changes. 63 // Delegate to notify upon changes.
47 scoped_refptr<FilePathWatcher::Delegate> delegate_; 64 scoped_refptr<FilePathWatcher::Delegate> delegate_;
48 65
49 // Path we're supposed to watch (passed to delegate). 66 // Path we're supposed to watch (passed to delegate).
50 FilePath target_; 67 FilePath target_;
51 68
52 // Handle for FindFirstChangeNotification. 69 // Handle for FindFirstChangeNotification.
53 HANDLE handle_; 70 HANDLE handle_;
54 71
55 // ObjectWatcher to watch handle_ for events. 72 // ObjectWatcher to watch handle_ for events.
(...skipping 11 matching lines...) Expand all
67 }; 84 };
68 85
69 bool FilePathWatcherImpl::Watch(const FilePath& path, 86 bool FilePathWatcherImpl::Watch(const FilePath& path,
70 FilePathWatcher::Delegate* delegate, 87 FilePathWatcher::Delegate* delegate,
71 base::MessageLoopProxy*) { 88 base::MessageLoopProxy*) {
72 DCHECK(target_.value().empty()); // Can only watch one path. 89 DCHECK(target_.value().empty()); // Can only watch one path.
73 90
74 set_message_loop(base::MessageLoopProxy::CreateForCurrentThread()); 91 set_message_loop(base::MessageLoopProxy::CreateForCurrentThread());
75 delegate_ = delegate; 92 delegate_ = delegate;
76 target_ = path; 93 target_ = path;
94 MessageLoop::current()->AddDestructionObserver(this);
77 95
78 if (!UpdateWatch()) 96 if (!UpdateWatch())
79 return false; 97 return false;
80 98
81 watcher_.StartWatching(handle_, this); 99 watcher_.StartWatching(handle_, this);
82 100
83 return true; 101 return true;
84 } 102 }
85 103
86 void FilePathWatcherImpl::Cancel() { 104 void FilePathWatcherImpl::Cancel() {
87 if (!message_loop().get()) { 105 if (!delegate_) {
88 // Watch was never called, so exit. 106 // Watch was never called, or the |message_loop_| has already quit.
89 return; 107 return;
90 } 108 }
91 109
92 // Switch to the file thread if necessary so we can stop |watcher_|. 110 // Switch to the file thread if necessary so we can stop |watcher_|.
93 if (!message_loop()->BelongsToCurrentThread()) { 111 if (!message_loop()->BelongsToCurrentThread()) {
94 message_loop()->PostTask(FROM_HERE, 112 message_loop()->PostTask(FROM_HERE,
95 NewRunnableMethod(this, &FilePathWatcherImpl::Cancel)); 113 NewRunnableMethod(this, &FilePathWatcherImpl::CancelInternal));
96 return; 114 } else {
115 CancelInternal();
97 } 116 }
117 }
98 118
119 void FilePathWatcherImpl::CancelInternal() {
99 if (handle_ != INVALID_HANDLE_VALUE) 120 if (handle_ != INVALID_HANDLE_VALUE)
100 DestroyWatch(); 121 DestroyWatch();
122
123 if (delegate_) {
124 MessageLoop::current()->RemoveDestructionObserver(this);
125 delegate_ = NULL;
126 }
127 }
128
129 void FilePathWatcherImpl::WillDestroyCurrentMessageLoop() {
130 CancelInternal();
131 }
132
133 FilePathWatcherImpl::~FilePathWatcherImpl() {
134 CancelInternal();
101 } 135 }
102 136
103 void FilePathWatcherImpl::OnObjectSignaled(HANDLE object) { 137 void FilePathWatcherImpl::OnObjectSignaled(HANDLE object) {
104 DCHECK(object == handle_); 138 DCHECK(object == handle_);
105 // Make sure we stay alive through the body of this function. 139 // Make sure we stay alive through the body of this function.
106 scoped_refptr<FilePathWatcherImpl> keep_alive(this); 140 scoped_refptr<FilePathWatcherImpl> keep_alive(this);
107 141
108 if (!UpdateWatch()) { 142 if (!UpdateWatch()) {
109 delegate_->OnError(); 143 delegate_->OnError();
110 return; 144 return;
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
142 } else if (!file_exists && !last_modified_.is_null()) { 176 } else if (!file_exists && !last_modified_.is_null()) {
143 last_modified_ = base::Time(); 177 last_modified_ = base::Time();
144 delegate_->OnFilePathChanged(target_); 178 delegate_->OnFilePathChanged(target_);
145 } 179 }
146 180
147 // The watch may have been cancelled by the callback. 181 // The watch may have been cancelled by the callback.
148 if (handle_ != INVALID_HANDLE_VALUE) 182 if (handle_ != INVALID_HANDLE_VALUE)
149 watcher_.StartWatching(handle_, this); 183 watcher_.StartWatching(handle_, this);
150 } 184 }
151 185
152 FilePathWatcherImpl::~FilePathWatcherImpl() {
153 if (handle_ != INVALID_HANDLE_VALUE)
154 DestroyWatch();
155 }
156
157 // static 186 // static
158 bool FilePathWatcherImpl::SetupWatchHandle(const FilePath& dir, 187 bool FilePathWatcherImpl::SetupWatchHandle(const FilePath& dir,
159 HANDLE* handle) { 188 HANDLE* handle) {
160 *handle = FindFirstChangeNotification( 189 *handle = FindFirstChangeNotification(
161 dir.value().c_str(), 190 dir.value().c_str(),
162 false, // Don't watch subtrees 191 false, // Don't watch subtrees
163 FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_SIZE | 192 FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_SIZE |
164 FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_DIR_NAME | 193 FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_DIR_NAME |
165 FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SECURITY); 194 FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SECURITY);
166 if (*handle != INVALID_HANDLE_VALUE) { 195 if (*handle != INVALID_HANDLE_VALUE) {
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after
247 watcher_.StopWatching(); 276 watcher_.StopWatching();
248 FindCloseChangeNotification(handle_); 277 FindCloseChangeNotification(handle_);
249 handle_ = INVALID_HANDLE_VALUE; 278 handle_ = INVALID_HANDLE_VALUE;
250 } 279 }
251 280
252 } // namespace 281 } // namespace
253 282
254 FilePathWatcher::FilePathWatcher() { 283 FilePathWatcher::FilePathWatcher() {
255 impl_ = new FilePathWatcherImpl(); 284 impl_ = new FilePathWatcherImpl();
256 } 285 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698