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

Side by Side Diff: base/files/file_path_watcher_win.cc

Issue 11415066: FilePathWatcher::Watch() - Listen for sub directory changes. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: '' Created 8 years 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 "base/files/file_path_watcher.h" 5 #include "base/files/file_path_watcher.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/file_path.h" 8 #include "base/file_path.h"
9 #include "base/file_util.h" 9 #include "base/file_util.h"
10 #include "base/logging.h" 10 #include "base/logging.h"
11 #include "base/memory/ref_counted.h" 11 #include "base/memory/ref_counted.h"
12 #include "base/message_loop_proxy.h" 12 #include "base/message_loop_proxy.h"
13 #include "base/time.h" 13 #include "base/time.h"
14 #include "base/win/object_watcher.h" 14 #include "base/win/object_watcher.h"
15 15
16 namespace base { 16 namespace base {
17 namespace files { 17 namespace files {
18 18
19 namespace { 19 namespace {
20 20
21 class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate, 21 class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate,
22 public base::win::ObjectWatcher::Delegate, 22 public base::win::ObjectWatcher::Delegate,
23 public MessageLoop::DestructionObserver { 23 public MessageLoop::DestructionObserver {
24 public: 24 public:
25 FilePathWatcherImpl() : delegate_(NULL), handle_(INVALID_HANDLE_VALUE) {} 25 FilePathWatcherImpl()
26 : delegate_(NULL),
27 handle_(INVALID_HANDLE_VALUE),
28 recursive_watch_(false) {}
26 29
27 // FilePathWatcher::PlatformDelegate overrides. 30 // FilePathWatcher::PlatformDelegate overrides.
28 virtual bool Watch(const FilePath& path, 31 virtual bool Watch(const FilePath& path,
32 bool recursive,
29 FilePathWatcher::Delegate* delegate) OVERRIDE; 33 FilePathWatcher::Delegate* delegate) OVERRIDE;
30 virtual void Cancel() OVERRIDE; 34 virtual void Cancel() OVERRIDE;
31 35
32 // Deletion of the FilePathWatcher will call Cancel() to dispose of this 36 // Deletion of the FilePathWatcher will call Cancel() to dispose of this
33 // object in the right thread. This also observes destruction of the required 37 // object in the right thread. This also observes destruction of the required
34 // cleanup thread, in case it quits before Cancel() is called. 38 // cleanup thread, in case it quits before Cancel() is called.
35 virtual void WillDestroyCurrentMessageLoop() OVERRIDE; 39 virtual void WillDestroyCurrentMessageLoop() OVERRIDE;
36 40
37 // Callback from MessageLoopForIO. 41 // Callback from MessageLoopForIO.
38 virtual void OnObjectSignaled(HANDLE object); 42 virtual void OnObjectSignaled(HANDLE object);
39 43
40 private: 44 private:
41 virtual ~FilePathWatcherImpl() {} 45 virtual ~FilePathWatcherImpl() {}
42 46
43 // Setup a watch handle for directory |dir|. Returns true if no fatal error 47 // Setup a watch handle for directory |dir|. Set |recursive| to true to watch
44 // occurs. |handle| will receive the handle value if |dir| is watchable, 48 // the directory sub trees. Returns true if no fatal error occurs. |handle|
45 // otherwise INVALID_HANDLE_VALUE. 49 // will receive the handle value if |dir| is watchable, otherwise
46 static bool SetupWatchHandle(const FilePath& dir, HANDLE* handle) 50 // INVALID_HANDLE_VALUE.
47 WARN_UNUSED_RESULT; 51 static bool SetupWatchHandle(const FilePath& dir,
52 bool recursive,
53 HANDLE* handle) WARN_UNUSED_RESULT;
48 54
49 // (Re-)Initialize the watch handle. 55 // (Re-)Initialize the watch handle.
50 bool UpdateWatch() WARN_UNUSED_RESULT; 56 bool UpdateWatch() WARN_UNUSED_RESULT;
51 57
52 // Destroy the watch handle. 58 // Destroy the watch handle.
53 void DestroyWatch(); 59 void DestroyWatch();
54 60
55 // Cleans up and stops observing the |message_loop_| thread. 61 // Cleans up and stops observing the |message_loop_| thread.
56 void CancelOnMessageLoopThread() OVERRIDE; 62 void CancelOnMessageLoopThread() OVERRIDE;
57 63
58 // Delegate to notify upon changes. 64 // Delegate to notify upon changes.
59 scoped_refptr<FilePathWatcher::Delegate> delegate_; 65 scoped_refptr<FilePathWatcher::Delegate> delegate_;
60 66
61 // Path we're supposed to watch (passed to delegate). 67 // Path we're supposed to watch (passed to delegate).
62 FilePath target_; 68 FilePath target_;
63 69
64 // Handle for FindFirstChangeNotification. 70 // Handle for FindFirstChangeNotification.
65 HANDLE handle_; 71 HANDLE handle_;
66 72
67 // ObjectWatcher to watch handle_ for events. 73 // ObjectWatcher to watch handle_ for events.
68 base::win::ObjectWatcher watcher_; 74 base::win::ObjectWatcher watcher_;
69 75
76 // Set to true to watch the sub trees of the specified directory file path.
77 bool recursive_watch_;
Lei Zhang 2012/12/03 21:44:39 Can you just pass this to UpdateWatch() and SetupW
kmadhusu 2012/12/03 22:54:57 UpdateWatch() is also called from FilePathWatcherI
78
70 // Keep track of the last modified time of the file. We use nulltime 79 // Keep track of the last modified time of the file. We use nulltime
71 // to represent the file not existing. 80 // to represent the file not existing.
72 base::Time last_modified_; 81 base::Time last_modified_;
73 82
74 // The time at which we processed the first notification with the 83 // The time at which we processed the first notification with the
75 // |last_modified_| time stamp. 84 // |last_modified_| time stamp.
76 base::Time first_notification_; 85 base::Time first_notification_;
77 86
78 DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl); 87 DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl);
79 }; 88 };
80 89
81 bool FilePathWatcherImpl::Watch(const FilePath& path, 90 bool FilePathWatcherImpl::Watch(const FilePath& path,
91 bool recursive,
82 FilePathWatcher::Delegate* delegate) { 92 FilePathWatcher::Delegate* delegate) {
83 DCHECK(target_.value().empty()); // Can only watch one path. 93 DCHECK(target_.value().empty()); // Can only watch one path.
84 94
85 set_message_loop(base::MessageLoopProxy::current()); 95 set_message_loop(base::MessageLoopProxy::current());
86 delegate_ = delegate; 96 delegate_ = delegate;
87 target_ = path; 97 target_ = path;
98 recursive_watch_ = recursive;
88 MessageLoop::current()->AddDestructionObserver(this); 99 MessageLoop::current()->AddDestructionObserver(this);
89 100
90 if (!UpdateWatch()) 101 if (!UpdateWatch())
91 return false; 102 return false;
92 103
93 watcher_.StartWatching(handle_, this); 104 watcher_.StartWatching(handle_, this);
94 105
95 return true; 106 return true;
96 } 107 }
97 108
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after
172 delegate_->OnFilePathChanged(target_); 183 delegate_->OnFilePathChanged(target_);
173 } 184 }
174 185
175 // The watch may have been cancelled by the callback. 186 // The watch may have been cancelled by the callback.
176 if (handle_ != INVALID_HANDLE_VALUE) 187 if (handle_ != INVALID_HANDLE_VALUE)
177 watcher_.StartWatching(handle_, this); 188 watcher_.StartWatching(handle_, this);
178 } 189 }
179 190
180 // static 191 // static
181 bool FilePathWatcherImpl::SetupWatchHandle(const FilePath& dir, 192 bool FilePathWatcherImpl::SetupWatchHandle(const FilePath& dir,
193 bool recursive,
182 HANDLE* handle) { 194 HANDLE* handle) {
183 *handle = FindFirstChangeNotification( 195 *handle = FindFirstChangeNotification(
184 dir.value().c_str(), 196 dir.value().c_str(),
185 false, // Don't watch subtrees 197 recursive,
186 FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_SIZE | 198 FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_SIZE |
187 FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_DIR_NAME | 199 FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_DIR_NAME |
188 FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SECURITY); 200 FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SECURITY);
189 if (*handle != INVALID_HANDLE_VALUE) { 201 if (*handle != INVALID_HANDLE_VALUE) {
190 // Make sure the handle we got points to an existing directory. It seems 202 // Make sure the handle we got points to an existing directory. It seems
191 // that windows sometimes hands out watches to direectories that are 203 // that windows sometimes hands out watches to directories that are
192 // about to go away, but doesn't sent notifications if that happens. 204 // about to go away, but doesn't sent notifications if that happens.
193 if (!file_util::DirectoryExists(dir)) { 205 if (!file_util::DirectoryExists(dir)) {
194 FindCloseChangeNotification(*handle); 206 FindCloseChangeNotification(*handle);
195 *handle = INVALID_HANDLE_VALUE; 207 *handle = INVALID_HANDLE_VALUE;
196 } 208 }
197 return true; 209 return true;
198 } 210 }
199 211
200 // If FindFirstChangeNotification failed because the target directory 212 // If FindFirstChangeNotification failed because the target directory
201 // doesn't exist, access is denied (happens if the file is already gone but 213 // doesn't exist, access is denied (happens if the file is already gone but
(...skipping 23 matching lines...) Expand all
225 last_modified_ = file_info.last_modified; 237 last_modified_ = file_info.last_modified;
226 first_notification_ = base::Time::Now(); 238 first_notification_ = base::Time::Now();
227 } 239 }
228 240
229 // Start at the target and walk up the directory chain until we succesfully 241 // Start at the target and walk up the directory chain until we succesfully
230 // create a watch handle in |handle_|. |child_dirs| keeps a stack of child 242 // create a watch handle in |handle_|. |child_dirs| keeps a stack of child
231 // directories stripped from target, in reverse order. 243 // directories stripped from target, in reverse order.
232 std::vector<FilePath> child_dirs; 244 std::vector<FilePath> child_dirs;
233 FilePath watched_path(target_); 245 FilePath watched_path(target_);
234 while (true) { 246 while (true) {
235 if (!SetupWatchHandle(watched_path, &handle_)) 247 if (!SetupWatchHandle(watched_path, recursive_watch_, &handle_))
236 return false; 248 return false;
237 249
238 // Break if a valid handle is returned. Try the parent directory otherwise. 250 // Break if a valid handle is returned. Try the parent directory otherwise.
239 if (handle_ != INVALID_HANDLE_VALUE) 251 if (handle_ != INVALID_HANDLE_VALUE)
240 break; 252 break;
241 253
242 // Abort if we hit the root directory. 254 // Abort if we hit the root directory.
243 child_dirs.push_back(watched_path.BaseName()); 255 child_dirs.push_back(watched_path.BaseName());
244 FilePath parent(watched_path.DirName()); 256 FilePath parent(watched_path.DirName());
245 if (parent == watched_path) { 257 if (parent == watched_path) {
246 DLOG(ERROR) << "Reached the root directory"; 258 DLOG(ERROR) << "Reached the root directory";
247 return false; 259 return false;
248 } 260 }
249 watched_path = parent; 261 watched_path = parent;
250 } 262 }
251 263
252 // At this point, handle_ is valid. However, the bottom-up search that the 264 // At this point, handle_ is valid. However, the bottom-up search that the
253 // above code performs races against directory creation. So try to walk back 265 // above code performs races against directory creation. So try to walk back
254 // down and see whether any children appeared in the mean time. 266 // down and see whether any children appeared in the mean time.
255 while (!child_dirs.empty()) { 267 while (!child_dirs.empty()) {
256 watched_path = watched_path.Append(child_dirs.back()); 268 watched_path = watched_path.Append(child_dirs.back());
257 child_dirs.pop_back(); 269 child_dirs.pop_back();
258 HANDLE temp_handle = INVALID_HANDLE_VALUE; 270 HANDLE temp_handle = INVALID_HANDLE_VALUE;
259 if (!SetupWatchHandle(watched_path, &temp_handle)) 271 if (!SetupWatchHandle(watched_path, recursive_watch_, &temp_handle))
260 return false; 272 return false;
261 if (temp_handle == INVALID_HANDLE_VALUE) 273 if (temp_handle == INVALID_HANDLE_VALUE)
262 break; 274 break;
263 FindCloseChangeNotification(handle_); 275 FindCloseChangeNotification(handle_);
264 handle_ = temp_handle; 276 handle_ = temp_handle;
265 } 277 }
266 278
267 return true; 279 return true;
268 } 280 }
269 281
270 void FilePathWatcherImpl::DestroyWatch() { 282 void FilePathWatcherImpl::DestroyWatch() {
271 watcher_.StopWatching(); 283 watcher_.StopWatching();
272 FindCloseChangeNotification(handle_); 284 FindCloseChangeNotification(handle_);
273 handle_ = INVALID_HANDLE_VALUE; 285 handle_ = INVALID_HANDLE_VALUE;
274 } 286 }
275 287
276 } // namespace 288 } // namespace
277 289
278 FilePathWatcher::FilePathWatcher() { 290 FilePathWatcher::FilePathWatcher() {
279 impl_ = new FilePathWatcherImpl(); 291 impl_ = new FilePathWatcherImpl();
280 } 292 }
281 293
282 } // namespace files 294 } // namespace files
283 } // namespace base 295 } // namespace base
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698