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

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

Issue 2596273003: Remove ref-counting from FilePathWatcher. (Closed)
Patch Set: self-review Created 3 years, 12 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
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 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_fsevents.h" 5 #include "base/files/file_path_watcher_fsevents.h"
6 6
7 #include <dispatch/dispatch.h> 7 #include <dispatch/dispatch.h>
8 8
9 #include <list> 9 #include <list>
10 10
11 #include "base/bind.h" 11 #include "base/bind.h"
12 #include "base/files/file_util.h" 12 #include "base/files/file_util.h"
13 #include "base/lazy_instance.h" 13 #include "base/lazy_instance.h"
14 #include "base/logging.h" 14 #include "base/logging.h"
15 #include "base/mac/scoped_cftyperef.h" 15 #include "base/mac/scoped_cftyperef.h"
16 #include "base/macros.h"
17 #include "base/strings/stringprintf.h" 16 #include "base/strings/stringprintf.h"
18 #include "base/threading/sequenced_task_runner_handle.h" 17 #include "base/threading/sequenced_task_runner_handle.h"
19 18
20 namespace base { 19 namespace base {
21 20
22 namespace { 21 namespace {
23 22
24 // The latency parameter passed to FSEventsStreamCreate(). 23 // The latency parameter passed to FSEventsStreamCreate().
25 const CFAbsoluteTime kEventLatencySeconds = 0.3; 24 const CFAbsoluteTime kEventLatencySeconds = 0.3;
26 25
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
62 61
63 if (resolve_count >= kMaxLinksToResolve) 62 if (resolve_count >= kMaxLinksToResolve)
64 result.clear(); 63 result.clear();
65 return result; 64 return result;
66 } 65 }
67 66
68 } // namespace 67 } // namespace
69 68
70 FilePathWatcherFSEvents::FilePathWatcherFSEvents() 69 FilePathWatcherFSEvents::FilePathWatcherFSEvents()
71 : queue_(dispatch_queue_create( 70 : queue_(dispatch_queue_create(
72 base::StringPrintf( 71 base::StringPrintf("org.chromium.base.FilePathWatcher.%p", this)
73 "org.chromium.base.FilePathWatcher.%p", this).c_str(), 72 .c_str(),
74 DISPATCH_QUEUE_SERIAL)), 73 DISPATCH_QUEUE_SERIAL)),
75 fsevent_stream_(nullptr) { 74 fsevent_stream_(nullptr),
75 weak_factory_(this) {}
76
77 FilePathWatcherFSEvents::~FilePathWatcherFSEvents() {
78 DCHECK(!task_runner() || task_runner()->RunsTasksOnCurrentThread());
gab 2017/01/05 21:22:53 Is this a new requirement? Where is it documented?
fdoray 2017/01/05 23:04:11 The DCHECK below is not new. The DCHECK above doc
79 DCHECK(callback_.is_null())
80 << "Cancel() must be called before FilePathWatcher is destroyed.";
76 } 81 }
77 82
78 bool FilePathWatcherFSEvents::Watch(const FilePath& path, 83 bool FilePathWatcherFSEvents::Watch(const FilePath& path,
79 bool recursive, 84 bool recursive,
80 const FilePathWatcher::Callback& callback) { 85 const FilePathWatcher::Callback& callback) {
81 DCHECK(!callback.is_null()); 86 DCHECK(!callback.is_null());
82 DCHECK(callback_.is_null()); 87 DCHECK(callback_.is_null());
83 88
84 // This class could support non-recursive watches, but that is currently 89 // This class could support non-recursive watches, but that is currently
85 // left to FilePathWatcherKQueue. 90 // left to FilePathWatcherKQueue.
(...skipping 12 matching lines...) Expand all
98 dispatch_async(queue_, ^{ 103 dispatch_async(queue_, ^{
99 StartEventStream(start_event, path_copy); 104 StartEventStream(start_event, path_copy);
100 }); 105 });
101 return true; 106 return true;
102 } 107 }
103 108
104 void FilePathWatcherFSEvents::Cancel() { 109 void FilePathWatcherFSEvents::Cancel() {
105 set_cancelled(); 110 set_cancelled();
106 callback_.Reset(); 111 callback_.Reset();
107 112
108 // Switch to the dispatch queue to tear down the event stream. As the queue 113 // Switch to the dispatch queue to tear down the event stream. As the queue is
109 // is owned by this object, and this method is called from the destructor, 114 // owned by |this|, and this method is called from the destructor, execute the
110 // execute the block synchronously. 115 // block synchronously.
111 dispatch_sync(queue_, ^{ 116 dispatch_sync(queue_, ^{
112 CancelOnMessageLoopThread(); 117 if (fsevent_stream_) {
118 DestroyEventStream();
119 target_.clear();
120 resolved_target_.clear();
121 }
113 }); 122 });
114 } 123 }
115 124
116 // static 125 // static
117 void FilePathWatcherFSEvents::FSEventsCallback( 126 void FilePathWatcherFSEvents::FSEventsCallback(
118 ConstFSEventStreamRef stream, 127 ConstFSEventStreamRef stream,
119 void* event_watcher, 128 void* event_watcher,
120 size_t num_events, 129 size_t num_events,
121 void* event_paths, 130 void* event_paths,
122 const FSEventStreamEventFlags flags[], 131 const FSEventStreamEventFlags flags[],
(...skipping 10 matching lines...) Expand all
133 root_change_at = std::min(root_change_at, event_ids[i]); 142 root_change_at = std::min(root_change_at, event_ids[i]);
134 paths.push_back(FilePath( 143 paths.push_back(FilePath(
135 reinterpret_cast<char**>(event_paths)[i]).StripTrailingSeparators()); 144 reinterpret_cast<char**>(event_paths)[i]).StripTrailingSeparators());
136 } 145 }
137 146
138 // Reinitialize the event stream if we find changes to the root. This is 147 // Reinitialize the event stream if we find changes to the root. This is
139 // necessary since FSEvents doesn't report any events for the subtree after 148 // necessary since FSEvents doesn't report any events for the subtree after
140 // the directory to be watched gets created. 149 // the directory to be watched gets created.
141 if (root_changed) { 150 if (root_changed) {
142 // Resetting the event stream from within the callback fails (FSEvents spews 151 // Resetting the event stream from within the callback fails (FSEvents spews
143 // bad file descriptor errors), so post a task to do the reset. 152 // bad file descriptor errors). Therefore, post a task to task_runner() that
144 dispatch_async(watcher->queue_, ^{ 153 // will itself schedule a call to UpdateEventStream() on the dispatch queue.
145 watcher->UpdateEventStream(root_change_at); 154 // Bouncing on task_runner() and using a weak ptr ensures that
146 }); 155 // UpdateEventStream() won't be called after |watcher| is destroyed (i.e.
156 // after the watch is cancelled).
157 watcher->task_runner()->PostTask(
158 FROM_HERE, Bind(
159 [](WeakPtr<FilePathWatcherFSEvents> weak_watcher,
160 FSEventStreamEventId root_change_at) {
161 if (!weak_watcher)
162 return;
163 FilePathWatcherFSEvents* watcher = weak_watcher.get();
164 dispatch_async(watcher->queue_, ^{
gab 2017/01/05 21:22:53 Is the async dispatch required here? i.e. it was r
fdoray 2017/01/05 23:04:11 Yes it's required. UpdateEventStream() must run on
165 watcher->UpdateEventStream(root_change_at);
166 });
167 },
168 watcher->weak_factory_.GetWeakPtr(), root_change_at));
147 } 169 }
148 170
149 watcher->OnFilePathsChanged(paths); 171 watcher->OnFilePathsChanged(paths);
150 } 172 }
151 173
152 FilePathWatcherFSEvents::~FilePathWatcherFSEvents() {
153 // This method may be called on either the libdispatch or task_runner()
154 // thread. Checking callback_ on the libdispatch thread here is safe because
155 // it is executing in a task posted by Cancel() which first reset callback_.
156 // PostTask forms a sufficient memory barrier to ensure that the value is
157 // consistent on the target thread.
158 DCHECK(callback_.is_null())
159 << "Cancel() must be called before FilePathWatcher is destroyed.";
160 }
161
162 void FilePathWatcherFSEvents::OnFilePathsChanged( 174 void FilePathWatcherFSEvents::OnFilePathsChanged(
163 const std::vector<FilePath>& paths) { 175 const std::vector<FilePath>& paths) {
164 DCHECK(!resolved_target_.empty()); 176 DCHECK(!resolved_target_.empty());
165 task_runner()->PostTask( 177 task_runner()->PostTask(
166 FROM_HERE, Bind(&FilePathWatcherFSEvents::DispatchEvents, this, paths, 178 FROM_HERE,
167 target_, resolved_target_)); 179 Bind(&FilePathWatcherFSEvents::DispatchEvents, weak_factory_.GetWeakPtr(),
180 paths, target_, resolved_target_));
168 } 181 }
169 182
170 void FilePathWatcherFSEvents::DispatchEvents(const std::vector<FilePath>& paths, 183 void FilePathWatcherFSEvents::DispatchEvents(const std::vector<FilePath>& paths,
171 const FilePath& target, 184 const FilePath& target,
172 const FilePath& resolved_target) { 185 const FilePath& resolved_target) {
173 DCHECK(task_runner()->RunsTasksOnCurrentThread()); 186 DCHECK(task_runner()->RunsTasksOnCurrentThread());
174 187
175 // Don't issue callbacks after Cancel() has been called. 188 // Don't issue callbacks after Cancel() has been called.
176 if (is_cancelled() || callback_.is_null()) { 189 if (is_cancelled() || callback_.is_null()) {
177 return; 190 return;
178 } 191 }
179 192
180 for (const FilePath& path : paths) { 193 for (const FilePath& path : paths) {
181 if (resolved_target.IsParent(path) || resolved_target == path) { 194 if (resolved_target.IsParent(path) || resolved_target == path) {
182 callback_.Run(target, false); 195 callback_.Run(target, false);
183 return; 196 return;
184 } 197 }
185 } 198 }
186 } 199 }
187 200
188 void FilePathWatcherFSEvents::CancelOnMessageLoopThread() {
189 // For all other implementations, the "message loop thread" is the IO thread,
190 // as returned by task_runner(). This implementation, however, needs to
191 // cancel pending work on the Dispatch Queue thread.
192
193 if (fsevent_stream_) {
194 DestroyEventStream();
195 target_.clear();
196 resolved_target_.clear();
197 }
198 }
199
200 void FilePathWatcherFSEvents::UpdateEventStream( 201 void FilePathWatcherFSEvents::UpdateEventStream(
201 FSEventStreamEventId start_event) { 202 FSEventStreamEventId start_event) {
202 // It can happen that the watcher gets canceled while tasks that call this 203 // It can happen that the watcher gets canceled while tasks that call this
203 // function are still in flight, so abort if this situation is detected. 204 // function are still in flight, so abort if this situation is detected.
204 if (resolved_target_.empty()) 205 if (resolved_target_.empty())
205 return; 206 return;
206 207
207 if (fsevent_stream_) 208 if (fsevent_stream_)
208 DestroyEventStream(); 209 DestroyEventStream();
209 210
(...skipping 15 matching lines...) Expand all
225 context.copyDescription = NULL; 226 context.copyDescription = NULL;
226 227
227 fsevent_stream_ = FSEventStreamCreate(NULL, &FSEventsCallback, &context, 228 fsevent_stream_ = FSEventStreamCreate(NULL, &FSEventsCallback, &context,
228 watched_paths, 229 watched_paths,
229 start_event, 230 start_event,
230 kEventLatencySeconds, 231 kEventLatencySeconds,
231 kFSEventStreamCreateFlagWatchRoot); 232 kFSEventStreamCreateFlagWatchRoot);
232 FSEventStreamSetDispatchQueue(fsevent_stream_, queue_); 233 FSEventStreamSetDispatchQueue(fsevent_stream_, queue_);
233 234
234 if (!FSEventStreamStart(fsevent_stream_)) { 235 if (!FSEventStreamStart(fsevent_stream_)) {
235 task_runner()->PostTask( 236 task_runner()->PostTask(FROM_HERE,
236 FROM_HERE, Bind(&FilePathWatcherFSEvents::ReportError, this, target_)); 237 Bind(&FilePathWatcherFSEvents::ReportError,
238 weak_factory_.GetWeakPtr(), target_));
237 } 239 }
238 } 240 }
239 241
240 bool FilePathWatcherFSEvents::ResolveTargetPath() { 242 bool FilePathWatcherFSEvents::ResolveTargetPath() {
241 FilePath resolved = ResolvePath(target_).StripTrailingSeparators(); 243 FilePath resolved = ResolvePath(target_).StripTrailingSeparators();
242 bool changed = resolved != resolved_target_; 244 bool changed = resolved != resolved_target_;
243 resolved_target_ = resolved; 245 resolved_target_ = resolved;
244 if (resolved_target_.empty()) { 246 if (resolved_target_.empty()) {
245 task_runner()->PostTask( 247 task_runner()->PostTask(FROM_HERE,
246 FROM_HERE, Bind(&FilePathWatcherFSEvents::ReportError, this, target_)); 248 Bind(&FilePathWatcherFSEvents::ReportError,
249 weak_factory_.GetWeakPtr(), target_));
247 } 250 }
248 return changed; 251 return changed;
249 } 252 }
250 253
251 void FilePathWatcherFSEvents::ReportError(const FilePath& target) { 254 void FilePathWatcherFSEvents::ReportError(const FilePath& target) {
252 DCHECK(task_runner()->RunsTasksOnCurrentThread()); 255 DCHECK(task_runner()->RunsTasksOnCurrentThread());
253 if (!callback_.is_null()) { 256 if (!callback_.is_null()) {
254 callback_.Run(target, true); 257 callback_.Run(target, true);
255 } 258 }
256 } 259 }
257 260
258 void FilePathWatcherFSEvents::DestroyEventStream() { 261 void FilePathWatcherFSEvents::DestroyEventStream() {
259 FSEventStreamStop(fsevent_stream_); 262 FSEventStreamStop(fsevent_stream_);
260 FSEventStreamInvalidate(fsevent_stream_); 263 FSEventStreamInvalidate(fsevent_stream_);
261 FSEventStreamRelease(fsevent_stream_); 264 FSEventStreamRelease(fsevent_stream_);
262 fsevent_stream_ = NULL; 265 fsevent_stream_ = NULL;
263 } 266 }
264 267
265 void FilePathWatcherFSEvents::StartEventStream(FSEventStreamEventId start_event, 268 void FilePathWatcherFSEvents::StartEventStream(FSEventStreamEventId start_event,
266 const FilePath& path) { 269 const FilePath& path) {
267 DCHECK(resolved_target_.empty()); 270 DCHECK(resolved_target_.empty());
268 271
269 target_ = path; 272 target_ = path;
270 ResolveTargetPath(); 273 ResolveTargetPath();
271 UpdateEventStream(start_event); 274 UpdateEventStream(start_event);
272 } 275 }
273 276
274 } // namespace base 277 } // namespace base
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698