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

Side by Side Diff: chrome/browser/file_path_watcher_win.cc

Issue 5606002: Move:... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 10 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
« no previous file with comments | « chrome/browser/file_path_watcher_stub.cc ('k') | chrome/browser/file_select_helper.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/file_path_watcher.h"
6
7 #include "base/file_path.h"
8 #include "base/file_util.h"
9 #include "base/logging.h"
10 #include "base/object_watcher.h"
11 #include "base/ref_counted.h"
12 #include "base/time.h"
13
14 namespace {
15
16 class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate,
17 public base::ObjectWatcher::Delegate {
18 public:
19 FilePathWatcherImpl() : delegate_(NULL), handle_(INVALID_HANDLE_VALUE) {}
20
21 virtual bool Watch(const FilePath& path, FilePathWatcher::Delegate* delegate);
22 virtual void Cancel();
23
24 // Callback from MessageLoopForIO.
25 virtual void OnObjectSignaled(HANDLE object);
26
27 private:
28 virtual ~FilePathWatcherImpl();
29
30 // Setup a watch handle for directory |dir|. Returns true if no fatal error
31 // occurs. |handle| will receive the handle value if |dir| is watchable,
32 // otherwise INVALID_HANDLE_VALUE.
33 static bool SetupWatchHandle(const FilePath& dir, HANDLE* handle)
34 WARN_UNUSED_RESULT;
35
36 // (Re-)Initialize the watch handle.
37 bool UpdateWatch() WARN_UNUSED_RESULT;
38
39 // Destroy the watch handle.
40 void DestroyWatch();
41
42 // Delegate to notify upon changes.
43 scoped_refptr<FilePathWatcher::Delegate> delegate_;
44
45 // Path we're supposed to watch (passed to delegate).
46 FilePath target_;
47
48 // Handle for FindFirstChangeNotification.
49 HANDLE handle_;
50
51 // ObjectWatcher to watch handle_ for events.
52 base::ObjectWatcher watcher_;
53
54 // Keep track of the last modified time of the file. We use nulltime
55 // to represent the file not existing.
56 base::Time last_modified_;
57
58 // The time at which we processed the first notification with the
59 // |last_modified_| time stamp.
60 base::Time first_notification_;
61
62 DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl);
63 };
64
65 bool FilePathWatcherImpl::Watch(const FilePath& path,
66 FilePathWatcher::Delegate* delegate) {
67 DCHECK(target_.value().empty()); // Can only watch one path.
68 delegate_ = delegate;
69 target_ = path;
70
71 if (!UpdateWatch())
72 return false;
73
74 watcher_.StartWatching(handle_, this);
75
76 return true;
77 }
78
79 void FilePathWatcherImpl::Cancel() {
80 // Switch to the file thread if necessary so we can stop |watcher_|.
81 if (!BrowserThread::CurrentlyOn(BrowserThread::FILE)) {
82 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
83 NewRunnableMethod(this, &FilePathWatcherImpl::Cancel));
84 return;
85 }
86
87 if (handle_ != INVALID_HANDLE_VALUE)
88 DestroyWatch();
89 }
90
91 void FilePathWatcherImpl::OnObjectSignaled(HANDLE object) {
92 DCHECK(object == handle_);
93 // Make sure we stay alive through the body of this function.
94 scoped_refptr<FilePathWatcherImpl> keep_alive(this);
95
96 if (!UpdateWatch()) {
97 delegate_->OnError();
98 return;
99 }
100
101 // Check whether the event applies to |target_| and notify the delegate.
102 base::PlatformFileInfo file_info;
103 bool file_exists = file_util::GetFileInfo(target_, &file_info);
104 if (file_exists && (last_modified_.is_null() ||
105 last_modified_ != file_info.last_modified)) {
106 last_modified_ = file_info.last_modified;
107 first_notification_ = base::Time::Now();
108 delegate_->OnFilePathChanged(target_);
109 } else if (file_exists && !first_notification_.is_null()) {
110 // The target's last modification time is equal to what's on record. This
111 // means that either an unrelated event occurred, or the target changed
112 // again (file modification times only have a resolution of 1s). Comparing
113 // file modification times against the wall clock is not reliable to find
114 // out whether the change is recent, since this code might just run too
115 // late. Moreover, there's no guarantee that file modification time and wall
116 // clock times come from the same source.
117 //
118 // Instead, the time at which the first notification carrying the current
119 // |last_notified_| time stamp is recorded. Later notifications that find
120 // the same file modification time only need to be forwarded until wall
121 // clock has advanced one second from the initial notification. After that
122 // interval, client code is guaranteed to having seen the current revision
123 // of the file.
124 if (base::Time::Now() - first_notification_ >
125 base::TimeDelta::FromSeconds(1)) {
126 // Stop further notifications for this |last_modification_| time stamp.
127 first_notification_ = base::Time();
128 }
129 delegate_->OnFilePathChanged(target_);
130 } else if (!file_exists && !last_modified_.is_null()) {
131 last_modified_ = base::Time();
132 delegate_->OnFilePathChanged(target_);
133 }
134
135 // The watch may have been cancelled by the callback.
136 if (handle_ != INVALID_HANDLE_VALUE)
137 watcher_.StartWatching(handle_, this);
138 }
139
140 FilePathWatcherImpl::~FilePathWatcherImpl() {
141 if (handle_ != INVALID_HANDLE_VALUE)
142 DestroyWatch();
143 }
144
145 // static
146 bool FilePathWatcherImpl::SetupWatchHandle(const FilePath& dir,
147 HANDLE* handle) {
148 *handle = FindFirstChangeNotification(
149 dir.value().c_str(),
150 false, // Don't watch subtrees
151 FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_SIZE |
152 FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_DIR_NAME |
153 FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SECURITY);
154 if (*handle != INVALID_HANDLE_VALUE) {
155 // Make sure the handle we got points to an existing directory. It seems
156 // that windows sometimes hands out watches to direectories that are
157 // about to go away, but doesn't sent notifications if that happens.
158 if (!file_util::DirectoryExists(dir)) {
159 FindCloseChangeNotification(*handle);
160 *handle = INVALID_HANDLE_VALUE;
161 }
162 return true;
163 }
164
165 // If FindFirstChangeNotification failed because the target directory
166 // doesn't exist, access is denied (happens if the file is already gone but
167 // there are still handles open), or the target is not a directory, try the
168 // immediate parent directory instead.
169 DWORD error_code = GetLastError();
170 if (error_code != ERROR_FILE_NOT_FOUND &&
171 error_code != ERROR_PATH_NOT_FOUND &&
172 error_code != ERROR_ACCESS_DENIED &&
173 error_code != ERROR_SHARING_VIOLATION &&
174 error_code != ERROR_DIRECTORY) {
175 PLOG(ERROR) << "FindFirstChangeNotification failed for "
176 << dir.value();
177 return false;
178 }
179
180 return true;
181 }
182
183 bool FilePathWatcherImpl::UpdateWatch() {
184 if (handle_ != INVALID_HANDLE_VALUE)
185 DestroyWatch();
186
187 base::PlatformFileInfo file_info;
188 if (file_util::GetFileInfo(target_, &file_info)) {
189 last_modified_ = file_info.last_modified;
190 first_notification_ = base::Time::Now();
191 }
192
193 // Start at the target and walk up the directory chain until we succesfully
194 // create a watch handle in |handle_|. |child_dirs| keeps a stack of child
195 // directories stripped from target, in reverse order.
196 std::vector<FilePath> child_dirs;
197 FilePath watched_path(target_);
198 while (true) {
199 if (!SetupWatchHandle(watched_path, &handle_))
200 return false;
201
202 // Break if a valid handle is returned. Try the parent directory otherwise.
203 if (handle_ != INVALID_HANDLE_VALUE)
204 break;
205
206 // Abort if we hit the root directory.
207 child_dirs.push_back(watched_path.BaseName());
208 FilePath parent(watched_path.DirName());
209 if (parent == watched_path) {
210 LOG(ERROR) << "Reached the root directory";
211 return false;
212 }
213 watched_path = parent;
214 }
215
216 // At this point, handle_ is valid. However, the bottom-up search that the
217 // above code performs races against directory creation. So try to walk back
218 // down and see whether any children appeared in the mean time.
219 while (!child_dirs.empty()) {
220 watched_path = watched_path.Append(child_dirs.back());
221 child_dirs.pop_back();
222 HANDLE temp_handle = INVALID_HANDLE_VALUE;
223 if (!SetupWatchHandle(watched_path, &temp_handle))
224 return false;
225 if (temp_handle == INVALID_HANDLE_VALUE)
226 break;
227 FindCloseChangeNotification(handle_);
228 handle_ = temp_handle;
229 }
230
231 return true;
232 }
233
234 void FilePathWatcherImpl::DestroyWatch() {
235 watcher_.StopWatching();
236 FindCloseChangeNotification(handle_);
237 handle_ = INVALID_HANDLE_VALUE;
238 }
239
240 } // namespace
241
242 FilePathWatcher::FilePathWatcher() {
243 impl_ = new FilePathWatcherImpl();
244 }
OLDNEW
« no previous file with comments | « chrome/browser/file_path_watcher_stub.cc ('k') | chrome/browser/file_select_helper.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698