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

Side by Side Diff: content/common/file_path_watcher/file_path_watcher_mac.cc

Issue 6670081: Move FilePathWatcher class from browser/... to common/... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: fix up linux clang issue, and clean up bad commented block 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) 2010 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 "chrome/browser/file_path_watcher/file_path_watcher.h" 5 #include "content/common/file_path_watcher/file_path_watcher.h"
6 6
7 #include <CoreServices/CoreServices.h> 7 #include <CoreServices/CoreServices.h>
8 #include <set> 8 #include <set>
9 9
10 #include "base/file_path.h" 10 #include "base/file_path.h"
11 #include "base/file_util.h" 11 #include "base/file_util.h"
12 #include "base/logging.h" 12 #include "base/logging.h"
13 #include "base/mac/scoped_cftyperef.h" 13 #include "base/mac/scoped_cftyperef.h"
14 #include "base/message_loop.h"
14 #include "base/singleton.h" 15 #include "base/singleton.h"
15 #include "base/time.h" 16 #include "base/time.h"
16 17
18 // Note to future well meaning engineers. Unless kqueue semantics have changed
19 // considerably, do NOT try to reimplement this class using kqueue. The main
20 // problem is that this class requires the ability to watch a directory
21 // and notice changes to any files within it. A kqueue on a directory can watch
22 // for creation and deletion of files, but not for modifications to files within
23 // the directory. To do this with the current kqueue semantics would require
24 // kqueueing every file in the directory, and file descriptors are a limited
25 // resource. If you have a good idea on how to get around this, the source for a
26 // reasonable implementation of this class using kqueues is attached here:
27 // http://code.google.com/p/chromium/issues/detail?id=54822#c13
28
17 namespace { 29 namespace {
18 30
19 // The latency parameter passed to FSEventsStreamCreate(). 31 // The latency parameter passed to FSEventsStreamCreate().
20 const CFAbsoluteTime kEventLatencySeconds = 0.3; 32 const CFAbsoluteTime kEventLatencySeconds = 0.3;
21 33
22 // Mac-specific file watcher implementation based on the FSEvents API. 34 // Mac-specific file watcher implementation based on the FSEvents API.
23 class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate { 35 class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate {
24 public: 36 public:
25 FilePathWatcherImpl(); 37 FilePathWatcherImpl();
26 38
27 // Called from the FSEvents callback whenever there is a change to the paths 39 // Called from the FSEvents callback whenever there is a change to the paths
28 void OnFilePathChanged(); 40 void OnFilePathChanged();
29 41
30 // (Re-)Initialize the event stream to start reporting events from 42 // (Re-)Initialize the event stream to start reporting events from
31 // |start_event|. 43 // |start_event|.
32 void UpdateEventStream(FSEventStreamEventId start_event); 44 void UpdateEventStream(FSEventStreamEventId start_event);
33 45
34 // FilePathWatcher::PlatformDelegate overrides. 46 // FilePathWatcher::PlatformDelegate overrides.
35 virtual bool Watch(const FilePath& path, FilePathWatcher::Delegate* delegate); 47 virtual bool Watch(const FilePath& path,
36 virtual void Cancel(); 48 FilePathWatcher::Delegate* delegate,
49 base::MessageLoopProxy* loop) OVERRIDE;
50 virtual void Cancel() OVERRIDE;
51
52 scoped_refptr<base::MessageLoopProxy> run_loop_message_loop() {
53 return run_loop_message_loop_;
54 }
37 55
38 private: 56 private:
39 virtual ~FilePathWatcherImpl() {} 57 virtual ~FilePathWatcherImpl() {}
40 58
41 // Destroy the event stream. 59 // Destroy the event stream.
42 void DestroyEventStream(); 60 void DestroyEventStream();
43 61
44 // Delegate to notify upon changes. 62 // Delegate to notify upon changes.
45 scoped_refptr<FilePathWatcher::Delegate> delegate_; 63 scoped_refptr<FilePathWatcher::Delegate> delegate_;
46 64
47 // Target path to watch (passed to delegate). 65 // Target path to watch (passed to delegate).
48 FilePath target_; 66 FilePath target_;
49 67
50 // Keep track of the last modified time of the file. We use nulltime 68 // Keep track of the last modified time of the file. We use nulltime
51 // to represent the file not existing. 69 // to represent the file not existing.
52 base::Time last_modified_; 70 base::Time last_modified_;
53 71
54 // The time at which we processed the first notification with the 72 // The time at which we processed the first notification with the
55 // |last_modified_| time stamp. 73 // |last_modified_| time stamp.
56 base::Time first_notification_; 74 base::Time first_notification_;
57 75
58 // Backend stream we receive event callbacks from (strong reference). 76 // Backend stream we receive event callbacks from (strong reference).
59 FSEventStreamRef fsevent_stream_; 77 FSEventStreamRef fsevent_stream_;
60 78
79 // Run loop for FSEventStream to run on.
80 scoped_refptr<base::MessageLoopProxy> run_loop_message_loop_;
81
61 // Used to detect early cancellation. 82 // Used to detect early cancellation.
62 bool canceled_; 83 bool canceled_;
63 84
64 DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl); 85 DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl);
65 }; 86 };
66 87
67 // The callback passed to FSEventStreamCreate(). 88 // The callback passed to FSEventStreamCreate().
68 void FSEventsCallback(ConstFSEventStreamRef stream, 89 void FSEventsCallback(ConstFSEventStreamRef stream,
69 void* event_watcher, size_t num_events, 90 void* event_watcher, size_t num_events,
70 void* event_paths, const FSEventStreamEventFlags flags[], 91 void* event_paths, const FSEventStreamEventFlags flags[],
71 const FSEventStreamEventId event_ids[]) { 92 const FSEventStreamEventId event_ids[]) {
72 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
73
74 FilePathWatcherImpl* watcher = 93 FilePathWatcherImpl* watcher =
75 reinterpret_cast<FilePathWatcherImpl*>(event_watcher); 94 reinterpret_cast<FilePathWatcherImpl*>(event_watcher);
95 DCHECK(watcher->run_loop_message_loop()->BelongsToCurrentThread());
96
76 bool root_changed = false; 97 bool root_changed = false;
77 FSEventStreamEventId root_change_at = FSEventStreamGetLatestEventId(stream); 98 FSEventStreamEventId root_change_at = FSEventStreamGetLatestEventId(stream);
78 for (size_t i = 0; i < num_events; i++) { 99 for (size_t i = 0; i < num_events; i++) {
79 if (flags[i] & kFSEventStreamEventFlagRootChanged) 100 if (flags[i] & kFSEventStreamEventFlagRootChanged)
80 root_changed = true; 101 root_changed = true;
81 if (event_ids[i]) 102 if (event_ids[i])
82 root_change_at = std::min(root_change_at, event_ids[i]); 103 root_change_at = std::min(root_change_at, event_ids[i]);
83 } 104 }
84 105
85 // Reinitialize the event stream if we find changes to the root. This is 106 // Reinitialize the event stream if we find changes to the root. This is
86 // necessary since FSEvents doesn't report any events for the subtree after 107 // necessary since FSEvents doesn't report any events for the subtree after
87 // the directory to be watched gets created. 108 // the directory to be watched gets created.
88 if (root_changed) { 109 if (root_changed) {
89 // Resetting the event stream from within the callback fails (FSEvents spews 110 // Resetting the event stream from within the callback fails (FSEvents spews
90 // bad file descriptor errors), so post a task to do the reset. 111 // bad file descriptor errors), so post a task to do the reset.
91 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 112 watcher->run_loop_message_loop()->PostTask(FROM_HERE,
92 NewRunnableMethod(watcher, &FilePathWatcherImpl::UpdateEventStream, 113 NewRunnableMethod(watcher, &FilePathWatcherImpl::UpdateEventStream,
93 root_change_at)); 114 root_change_at));
94 } 115 }
95 116
96 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 117 watcher->OnFilePathChanged();
97 NewRunnableMethod(watcher, &FilePathWatcherImpl::OnFilePathChanged));
98 } 118 }
99 119
100 // FilePathWatcherImpl implementation: 120 // FilePathWatcherImpl implementation:
101 121
102 FilePathWatcherImpl::FilePathWatcherImpl() 122 FilePathWatcherImpl::FilePathWatcherImpl()
103 : fsevent_stream_(NULL), 123 : fsevent_stream_(NULL),
104 canceled_(false) { 124 canceled_(false) {
105 } 125 }
106 126
107 void FilePathWatcherImpl::OnFilePathChanged() { 127 void FilePathWatcherImpl::OnFilePathChanged() {
108 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 128 // Switch to the CFRunLoop based thread if necessary, so we can tear down
129 // the event stream.
130 if (!message_loop()->BelongsToCurrentThread()) {
131 message_loop()->PostTask(
132 FROM_HERE,
133 NewRunnableMethod(this, &FilePathWatcherImpl::OnFilePathChanged));
134 return;
135 }
136
137 DCHECK(message_loop()->BelongsToCurrentThread());
109 DCHECK(!target_.empty()); 138 DCHECK(!target_.empty());
110 139
111 base::PlatformFileInfo file_info; 140 base::PlatformFileInfo file_info;
112 bool file_exists = file_util::GetFileInfo(target_, &file_info); 141 bool file_exists = file_util::GetFileInfo(target_, &file_info);
113 if (file_exists && (last_modified_.is_null() || 142 if (file_exists && (last_modified_.is_null() ||
114 last_modified_ != file_info.last_modified)) { 143 last_modified_ != file_info.last_modified)) {
115 last_modified_ = file_info.last_modified; 144 last_modified_ = file_info.last_modified;
116 first_notification_ = base::Time::Now(); 145 first_notification_ = base::Time::Now();
117 delegate_->OnFilePathChanged(target_); 146 delegate_->OnFilePathChanged(target_);
118 } else if (file_exists && !first_notification_.is_null()) { 147 } else if (file_exists && !first_notification_.is_null()) {
(...skipping 17 matching lines...) Expand all
136 first_notification_ = base::Time(); 165 first_notification_ = base::Time();
137 } 166 }
138 delegate_->OnFilePathChanged(target_); 167 delegate_->OnFilePathChanged(target_);
139 } else if (!file_exists && !last_modified_.is_null()) { 168 } else if (!file_exists && !last_modified_.is_null()) {
140 last_modified_ = base::Time(); 169 last_modified_ = base::Time();
141 delegate_->OnFilePathChanged(target_); 170 delegate_->OnFilePathChanged(target_);
142 } 171 }
143 } 172 }
144 173
145 bool FilePathWatcherImpl::Watch(const FilePath& path, 174 bool FilePathWatcherImpl::Watch(const FilePath& path,
146 FilePathWatcher::Delegate* delegate) { 175 FilePathWatcher::Delegate* delegate,
176 base::MessageLoopProxy* loop) {
147 DCHECK(target_.value().empty()); 177 DCHECK(target_.value().empty());
178 DCHECK(MessageLoopForIO::current());
148 179
180 set_message_loop(base::MessageLoopProxy::CreateForCurrentThread());
181 run_loop_message_loop_ = loop;
149 target_ = path; 182 target_ = path;
150 delegate_ = delegate; 183 delegate_ = delegate;
151 184
152 FSEventStreamEventId start_event = FSEventsGetCurrentEventId(); 185 FSEventStreamEventId start_event = FSEventsGetCurrentEventId();
153 186
154 base::PlatformFileInfo file_info; 187 base::PlatformFileInfo file_info;
155 if (file_util::GetFileInfo(target_, &file_info)) { 188 if (file_util::GetFileInfo(target_, &file_info)) {
156 last_modified_ = file_info.last_modified; 189 last_modified_ = file_info.last_modified;
157 first_notification_ = base::Time::Now(); 190 first_notification_ = base::Time::Now();
158 } 191 }
159 192
160 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 193 run_loop_message_loop()->PostTask(FROM_HERE,
161 NewRunnableMethod(this, &FilePathWatcherImpl::UpdateEventStream, 194 NewRunnableMethod(this, &FilePathWatcherImpl::UpdateEventStream,
162 start_event)); 195 start_event));
163 196
164 return true; 197 return true;
165 } 198 }
166 199
167 void FilePathWatcherImpl::Cancel() { 200 void FilePathWatcherImpl::Cancel() {
168 // Switch to the UI thread if necessary, so we can tear down the event stream. 201 if (!run_loop_message_loop().get()) {
169 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { 202 // Watch was never called, so exit.
170 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 203 return;
204 }
205
206 // Switch to the CFRunLoop based thread if necessary, so we can tear down
207 // the event stream.
208 if (!run_loop_message_loop()->BelongsToCurrentThread()) {
209 run_loop_message_loop()->PostTask(FROM_HERE,
171 NewRunnableMethod(this, &FilePathWatcherImpl::Cancel)); 210 NewRunnableMethod(this, &FilePathWatcherImpl::Cancel));
172 return; 211 return;
173 } 212 }
174 213
175 canceled_ = true; 214 canceled_ = true;
176 if (fsevent_stream_) 215 if (fsevent_stream_)
177 DestroyEventStream(); 216 DestroyEventStream();
178 } 217 }
179 218
180 void FilePathWatcherImpl::UpdateEventStream(FSEventStreamEventId start_event) { 219 void FilePathWatcherImpl::UpdateEventStream(FSEventStreamEventId start_event) {
181 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 220 DCHECK(run_loop_message_loop()->BelongsToCurrentThread());
221 DCHECK(MessageLoopForUI::current());
182 222
183 // It can happen that the watcher gets canceled while tasks that call this 223 // It can happen that the watcher gets canceled while tasks that call this
184 // function are still in flight, so abort if this situation is detected. 224 // function are still in flight, so abort if this situation is detected.
185 if (canceled_) 225 if (canceled_)
186 return; 226 return;
187 227
188 if (fsevent_stream_) 228 if (fsevent_stream_)
189 DestroyEventStream(); 229 DestroyEventStream();
190 230
191 base::mac::ScopedCFTypeRef<CFStringRef> cf_path(CFStringCreateWithCString( 231 base::mac::ScopedCFTypeRef<CFStringRef> cf_path(CFStringCreateWithCString(
(...skipping 13 matching lines...) Expand all
205 context.copyDescription = NULL; 245 context.copyDescription = NULL;
206 246
207 fsevent_stream_ = FSEventStreamCreate(NULL, &FSEventsCallback, &context, 247 fsevent_stream_ = FSEventStreamCreate(NULL, &FSEventsCallback, &context,
208 watched_paths, 248 watched_paths,
209 start_event, 249 start_event,
210 kEventLatencySeconds, 250 kEventLatencySeconds,
211 kFSEventStreamCreateFlagWatchRoot); 251 kFSEventStreamCreateFlagWatchRoot);
212 FSEventStreamScheduleWithRunLoop(fsevent_stream_, CFRunLoopGetCurrent(), 252 FSEventStreamScheduleWithRunLoop(fsevent_stream_, CFRunLoopGetCurrent(),
213 kCFRunLoopDefaultMode); 253 kCFRunLoopDefaultMode);
214 if (!FSEventStreamStart(fsevent_stream_)) { 254 if (!FSEventStreamStart(fsevent_stream_)) {
215 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 255 message_loop()->PostTask(FROM_HERE,
216 NewRunnableMethod(delegate_.get(), 256 NewRunnableMethod(delegate_.get(),
217 &FilePathWatcher::Delegate::OnError)); 257 &FilePathWatcher::Delegate::OnError));
218 } 258 }
219 } 259 }
220 260
221 void FilePathWatcherImpl::DestroyEventStream() { 261 void FilePathWatcherImpl::DestroyEventStream() {
222 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 262 DCHECK(run_loop_message_loop()->BelongsToCurrentThread());
223 FSEventStreamStop(fsevent_stream_); 263 FSEventStreamStop(fsevent_stream_);
224 FSEventStreamUnscheduleFromRunLoop(fsevent_stream_, CFRunLoopGetCurrent(), 264 FSEventStreamUnscheduleFromRunLoop(fsevent_stream_, CFRunLoopGetCurrent(),
225 kCFRunLoopDefaultMode); 265 kCFRunLoopDefaultMode);
226 FSEventStreamRelease(fsevent_stream_); 266 FSEventStreamRelease(fsevent_stream_);
227 fsevent_stream_ = NULL; 267 fsevent_stream_ = NULL;
228 } 268 }
229 269
230 } // namespace 270 } // namespace
231 271
232 FilePathWatcher::FilePathWatcher() { 272 FilePathWatcher::FilePathWatcher() {
233 impl_ = new FilePathWatcherImpl(); 273 impl_ = new FilePathWatcherImpl();
234 } 274 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698