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

Side by Side Diff: chrome/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: 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) 2010 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 "chrome/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
dmac 2011/03/17 17:16:48 out of curiosity, had you already reached this sam
Mattias Nissler (ping if slow) 2011/03/17 18:14:27 Yes. Sorry I didn't update the bug. IIRC, there wa
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 scoped_refptr<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->message_loop()->PostTask(FROM_HERE,
97 NewRunnableMethod(watcher, &FilePathWatcherImpl::OnFilePathChanged)); 118 NewRunnableMethod(watcher, &FilePathWatcherImpl::OnFilePathChanged));
98 } 119 }
99 120
100 // FilePathWatcherImpl implementation: 121 // FilePathWatcherImpl implementation:
101 122
102 FilePathWatcherImpl::FilePathWatcherImpl() 123 FilePathWatcherImpl::FilePathWatcherImpl()
103 : fsevent_stream_(NULL), 124 : fsevent_stream_(NULL),
104 canceled_(false) { 125 canceled_(false) {
105 } 126 }
106 127
107 void FilePathWatcherImpl::OnFilePathChanged() { 128 void FilePathWatcherImpl::OnFilePathChanged() {
108 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 129 DCHECK(message_loop()->BelongsToCurrentThread());
109 DCHECK(!target_.empty()); 130 DCHECK(!target_.empty());
110 131
111 base::PlatformFileInfo file_info; 132 base::PlatformFileInfo file_info;
112 bool file_exists = file_util::GetFileInfo(target_, &file_info); 133 bool file_exists = file_util::GetFileInfo(target_, &file_info);
113 if (file_exists && (last_modified_.is_null() || 134 if (file_exists && (last_modified_.is_null() ||
114 last_modified_ != file_info.last_modified)) { 135 last_modified_ != file_info.last_modified)) {
115 last_modified_ = file_info.last_modified; 136 last_modified_ = file_info.last_modified;
116 first_notification_ = base::Time::Now(); 137 first_notification_ = base::Time::Now();
117 delegate_->OnFilePathChanged(target_); 138 delegate_->OnFilePathChanged(target_);
118 } else if (file_exists && !first_notification_.is_null()) { 139 } else if (file_exists && !first_notification_.is_null()) {
(...skipping 17 matching lines...) Expand all
136 first_notification_ = base::Time(); 157 first_notification_ = base::Time();
137 } 158 }
138 delegate_->OnFilePathChanged(target_); 159 delegate_->OnFilePathChanged(target_);
139 } else if (!file_exists && !last_modified_.is_null()) { 160 } else if (!file_exists && !last_modified_.is_null()) {
140 last_modified_ = base::Time(); 161 last_modified_ = base::Time();
141 delegate_->OnFilePathChanged(target_); 162 delegate_->OnFilePathChanged(target_);
142 } 163 }
143 } 164 }
144 165
145 bool FilePathWatcherImpl::Watch(const FilePath& path, 166 bool FilePathWatcherImpl::Watch(const FilePath& path,
146 FilePathWatcher::Delegate* delegate) { 167 FilePathWatcher::Delegate* delegate,
168 scoped_refptr<base::MessageLoopProxy> loop) {
147 DCHECK(target_.value().empty()); 169 DCHECK(target_.value().empty());
170 DCHECK(MessageLoopForIO::current());
Mattias Nissler (ping if slow) 2011/03/17 10:37:56 Same question as for the inotify implementation: W
dmac 2011/03/17 17:16:48 Same response ;-)
148 171
172 run_loop_message_loop_ = loop;
149 target_ = path; 173 target_ = path;
150 delegate_ = delegate; 174 delegate_ = delegate;
151 175
152 FSEventStreamEventId start_event = FSEventsGetCurrentEventId(); 176 FSEventStreamEventId start_event = FSEventsGetCurrentEventId();
153 177
154 base::PlatformFileInfo file_info; 178 base::PlatformFileInfo file_info;
155 if (file_util::GetFileInfo(target_, &file_info)) { 179 if (file_util::GetFileInfo(target_, &file_info)) {
156 last_modified_ = file_info.last_modified; 180 last_modified_ = file_info.last_modified;
157 first_notification_ = base::Time::Now(); 181 first_notification_ = base::Time::Now();
158 } 182 }
159 183
160 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 184 run_loop_message_loop()->PostTask(FROM_HERE,
161 NewRunnableMethod(this, &FilePathWatcherImpl::UpdateEventStream, 185 NewRunnableMethod(this, &FilePathWatcherImpl::UpdateEventStream,
162 start_event)); 186 start_event));
163 187
164 return true; 188 return true;
165 } 189 }
166 190
167 void FilePathWatcherImpl::Cancel() { 191 void FilePathWatcherImpl::Cancel() {
168 // Switch to the UI thread if necessary, so we can tear down the event stream. 192 // Switch to the UI thread if necessary, so we can tear down the event stream.
169 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { 193 if (run_loop_message_loop().get() &&
Mattias Nissler (ping if slow) 2011/03/17 10:37:56 I guess you need the NULL check since Cancel() can
dmac 2011/03/17 17:16:48 Done.
170 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 194 !run_loop_message_loop()->BelongsToCurrentThread()) {
195 run_loop_message_loop()->PostTask(FROM_HERE,
171 NewRunnableMethod(this, &FilePathWatcherImpl::Cancel)); 196 NewRunnableMethod(this, &FilePathWatcherImpl::Cancel));
172 return; 197 return;
173 } 198 }
174 199
175 canceled_ = true; 200 canceled_ = true;
176 if (fsevent_stream_) 201 if (fsevent_stream_)
177 DestroyEventStream(); 202 DestroyEventStream();
178 } 203 }
179 204
180 void FilePathWatcherImpl::UpdateEventStream(FSEventStreamEventId start_event) { 205 void FilePathWatcherImpl::UpdateEventStream(FSEventStreamEventId start_event) {
181 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 206 DCHECK(run_loop_message_loop()->BelongsToCurrentThread());
207 DCHECK(MessageLoopForUI::current());
Mattias Nissler (ping if slow) 2011/03/17 10:37:56 It seems that we can do the message loop type chec
dmac 2011/03/17 17:16:48 We can't actually (unless I'm missing something) b
182 208
183 // It can happen that the watcher gets canceled while tasks that call this 209 // 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. 210 // function are still in flight, so abort if this situation is detected.
185 if (canceled_) 211 if (canceled_)
186 return; 212 return;
187 213
188 if (fsevent_stream_) 214 if (fsevent_stream_)
189 DestroyEventStream(); 215 DestroyEventStream();
190 216
191 base::mac::ScopedCFTypeRef<CFStringRef> cf_path(CFStringCreateWithCString( 217 base::mac::ScopedCFTypeRef<CFStringRef> cf_path(CFStringCreateWithCString(
(...skipping 13 matching lines...) Expand all
205 context.copyDescription = NULL; 231 context.copyDescription = NULL;
206 232
207 fsevent_stream_ = FSEventStreamCreate(NULL, &FSEventsCallback, &context, 233 fsevent_stream_ = FSEventStreamCreate(NULL, &FSEventsCallback, &context,
208 watched_paths, 234 watched_paths,
209 start_event, 235 start_event,
210 kEventLatencySeconds, 236 kEventLatencySeconds,
211 kFSEventStreamCreateFlagWatchRoot); 237 kFSEventStreamCreateFlagWatchRoot);
212 FSEventStreamScheduleWithRunLoop(fsevent_stream_, CFRunLoopGetCurrent(), 238 FSEventStreamScheduleWithRunLoop(fsevent_stream_, CFRunLoopGetCurrent(),
213 kCFRunLoopDefaultMode); 239 kCFRunLoopDefaultMode);
214 if (!FSEventStreamStart(fsevent_stream_)) { 240 if (!FSEventStreamStart(fsevent_stream_)) {
215 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 241 message_loop()->PostTask(FROM_HERE,
216 NewRunnableMethod(delegate_.get(), 242 NewRunnableMethod(delegate_.get(),
217 &FilePathWatcher::Delegate::OnError)); 243 &FilePathWatcher::Delegate::OnError));
218 } 244 }
219 } 245 }
220 246
221 void FilePathWatcherImpl::DestroyEventStream() { 247 void FilePathWatcherImpl::DestroyEventStream() {
222 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 248 DCHECK(run_loop_message_loop()->BelongsToCurrentThread());
223 FSEventStreamStop(fsevent_stream_); 249 FSEventStreamStop(fsevent_stream_);
224 FSEventStreamUnscheduleFromRunLoop(fsevent_stream_, CFRunLoopGetCurrent(), 250 FSEventStreamUnscheduleFromRunLoop(fsevent_stream_, CFRunLoopGetCurrent(),
225 kCFRunLoopDefaultMode); 251 kCFRunLoopDefaultMode);
226 FSEventStreamRelease(fsevent_stream_); 252 FSEventStreamRelease(fsevent_stream_);
227 fsevent_stream_ = NULL; 253 fsevent_stream_ = NULL;
228 } 254 }
229 255
230 } // namespace 256 } // namespace
231 257
232 FilePathWatcher::FilePathWatcher() { 258 FilePathWatcher::FilePathWatcher() {
233 impl_ = new FilePathWatcherImpl(); 259 impl_ = new FilePathWatcherImpl();
234 } 260 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698