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

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

Issue 2123333003: Remove base::mac::LibDispatchTaskRunner and migrate its sole client to plain libdispatch. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Tweak comments Created 4 years, 5 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
« no previous file with comments | « base/files/file_path_watcher_fsevents.h ('k') | base/mac/libdispatch_task_runner.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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>
8
7 #include <list> 9 #include <list>
8 10
9 #include "base/bind.h" 11 #include "base/bind.h"
10 #include "base/files/file_util.h" 12 #include "base/files/file_util.h"
11 #include "base/lazy_instance.h" 13 #include "base/lazy_instance.h"
12 #include "base/logging.h" 14 #include "base/logging.h"
13 #include "base/mac/libdispatch_task_runner.h"
14 #include "base/mac/scoped_cftyperef.h" 15 #include "base/mac/scoped_cftyperef.h"
15 #include "base/macros.h" 16 #include "base/macros.h"
16 #include "base/message_loop/message_loop.h" 17 #include "base/message_loop/message_loop.h"
18 #include "base/strings/stringprintf.h"
17 #include "base/threading/thread_task_runner_handle.h" 19 #include "base/threading/thread_task_runner_handle.h"
18 20
19 namespace base { 21 namespace base {
20 22
21 namespace { 23 namespace {
22 24
23 // The latency parameter passed to FSEventsStreamCreate(). 25 // The latency parameter passed to FSEventsStreamCreate().
24 const CFAbsoluteTime kEventLatencySeconds = 0.3; 26 const CFAbsoluteTime kEventLatencySeconds = 0.3;
25 27
26 class FSEventsTaskRunner : public mac::LibDispatchTaskRunner {
27 public:
28 FSEventsTaskRunner()
29 : mac::LibDispatchTaskRunner("org.chromium.FilePathWatcherFSEvents") {
30 }
31
32 protected:
33 ~FSEventsTaskRunner() override {}
34 };
35
36 static LazyInstance<FSEventsTaskRunner>::Leaky g_task_runner =
37 LAZY_INSTANCE_INITIALIZER;
38
39 // Resolve any symlinks in the path. 28 // Resolve any symlinks in the path.
40 FilePath ResolvePath(const FilePath& path) { 29 FilePath ResolvePath(const FilePath& path) {
41 const unsigned kMaxLinksToResolve = 255; 30 const unsigned kMaxLinksToResolve = 255;
42 31
43 std::vector<FilePath::StringType> component_vector; 32 std::vector<FilePath::StringType> component_vector;
44 path.GetComponents(&component_vector); 33 path.GetComponents(&component_vector);
45 std::list<FilePath::StringType> 34 std::list<FilePath::StringType>
46 components(component_vector.begin(), component_vector.end()); 35 components(component_vector.begin(), component_vector.end());
47 36
48 FilePath result; 37 FilePath result;
(...skipping 23 matching lines...) Expand all
72 } 61 }
73 } 62 }
74 63
75 if (resolve_count >= kMaxLinksToResolve) 64 if (resolve_count >= kMaxLinksToResolve)
76 result.clear(); 65 result.clear();
77 return result; 66 return result;
78 } 67 }
79 68
80 } // namespace 69 } // namespace
81 70
82 FilePathWatcherFSEvents::FilePathWatcherFSEvents() : fsevent_stream_(NULL) { 71 FilePathWatcherFSEvents::FilePathWatcherFSEvents()
72 : queue_(dispatch_queue_create(
73 base::StringPrintf(
74 "org.chromium.base.FilePathWatcher.%p", this).c_str(),
75 DISPATCH_QUEUE_SERIAL)),
76 fsevent_stream_(nullptr) {
83 } 77 }
84 78
85 bool FilePathWatcherFSEvents::Watch(const FilePath& path, 79 bool FilePathWatcherFSEvents::Watch(const FilePath& path,
86 bool recursive, 80 bool recursive,
87 const FilePathWatcher::Callback& callback) { 81 const FilePathWatcher::Callback& callback) {
88 DCHECK(MessageLoopForIO::current()); 82 DCHECK(MessageLoopForIO::current());
89 DCHECK(!callback.is_null()); 83 DCHECK(!callback.is_null());
90 DCHECK(callback_.is_null()); 84 DCHECK(callback_.is_null());
91 85
92 // This class could support non-recursive watches, but that is currently 86 // This class could support non-recursive watches, but that is currently
93 // left to FilePathWatcherKQueue. 87 // left to FilePathWatcherKQueue.
94 if (!recursive) 88 if (!recursive)
95 return false; 89 return false;
96 90
97 set_task_runner(ThreadTaskRunnerHandle::Get()); 91 set_task_runner(ThreadTaskRunnerHandle::Get());
98 callback_ = callback; 92 callback_ = callback;
99 93
100 FSEventStreamEventId start_event = FSEventsGetCurrentEventId(); 94 FSEventStreamEventId start_event = FSEventsGetCurrentEventId();
101 g_task_runner.Get().PostTask( 95 // The block runtime would implicitly capture the reference, not the object
102 FROM_HERE, Bind(&FilePathWatcherFSEvents::StartEventStream, this, 96 // it's referencing. Copy the path into a local, so that the value is
103 start_event, path)); 97 // captured by the block's scope.
98 const FilePath path_copy(path);
99
100 dispatch_async(queue_, ^{
101 StartEventStream(start_event, path_copy);
102 });
104 return true; 103 return true;
105 } 104 }
106 105
107 void FilePathWatcherFSEvents::Cancel() { 106 void FilePathWatcherFSEvents::Cancel() {
108 set_cancelled(); 107 set_cancelled();
109 callback_.Reset(); 108 callback_.Reset();
110 109
111 // Switch to the dispatch queue thread to tear down the event stream. 110 // Switch to the dispatch queue to tear down the event stream. As the queue
112 g_task_runner.Get().PostTask( 111 // is owned by this object, and this method is called from the destructor,
113 FROM_HERE, 112 // execute the block synchronously.
114 Bind(&FilePathWatcherFSEvents::CancelOnMessageLoopThread, this)); 113 dispatch_sync(queue_, ^{
114 CancelOnMessageLoopThread();
115 });
115 } 116 }
116 117
117 // static 118 // static
118 void FilePathWatcherFSEvents::FSEventsCallback( 119 void FilePathWatcherFSEvents::FSEventsCallback(
119 ConstFSEventStreamRef stream, 120 ConstFSEventStreamRef stream,
120 void* event_watcher, 121 void* event_watcher,
121 size_t num_events, 122 size_t num_events,
122 void* event_paths, 123 void* event_paths,
123 const FSEventStreamEventFlags flags[], 124 const FSEventStreamEventFlags flags[],
124 const FSEventStreamEventId event_ids[]) { 125 const FSEventStreamEventId event_ids[]) {
125 FilePathWatcherFSEvents* watcher = 126 FilePathWatcherFSEvents* watcher =
126 reinterpret_cast<FilePathWatcherFSEvents*>(event_watcher); 127 reinterpret_cast<FilePathWatcherFSEvents*>(event_watcher);
127 DCHECK(g_task_runner.Get().RunsTasksOnCurrentThread());
128
129 bool root_changed = watcher->ResolveTargetPath(); 128 bool root_changed = watcher->ResolveTargetPath();
130 std::vector<FilePath> paths; 129 std::vector<FilePath> paths;
131 FSEventStreamEventId root_change_at = FSEventStreamGetLatestEventId(stream); 130 FSEventStreamEventId root_change_at = FSEventStreamGetLatestEventId(stream);
132 for (size_t i = 0; i < num_events; i++) { 131 for (size_t i = 0; i < num_events; i++) {
133 if (flags[i] & kFSEventStreamEventFlagRootChanged) 132 if (flags[i] & kFSEventStreamEventFlagRootChanged)
134 root_changed = true; 133 root_changed = true;
135 if (event_ids[i]) 134 if (event_ids[i])
136 root_change_at = std::min(root_change_at, event_ids[i]); 135 root_change_at = std::min(root_change_at, event_ids[i]);
137 paths.push_back(FilePath( 136 paths.push_back(FilePath(
138 reinterpret_cast<char**>(event_paths)[i]).StripTrailingSeparators()); 137 reinterpret_cast<char**>(event_paths)[i]).StripTrailingSeparators());
139 } 138 }
140 139
141 // Reinitialize the event stream if we find changes to the root. This is 140 // Reinitialize the event stream if we find changes to the root. This is
142 // necessary since FSEvents doesn't report any events for the subtree after 141 // necessary since FSEvents doesn't report any events for the subtree after
143 // the directory to be watched gets created. 142 // the directory to be watched gets created.
144 if (root_changed) { 143 if (root_changed) {
145 // Resetting the event stream from within the callback fails (FSEvents spews 144 // Resetting the event stream from within the callback fails (FSEvents spews
146 // bad file descriptor errors), so post a task to do the reset. 145 // bad file descriptor errors), so post a task to do the reset.
147 g_task_runner.Get().PostTask( 146 dispatch_async(watcher->queue_, ^{
148 FROM_HERE, 147 watcher->UpdateEventStream(root_change_at);
149 Bind(&FilePathWatcherFSEvents::UpdateEventStream, watcher, 148 });
150 root_change_at));
151 } 149 }
152 150
153 watcher->OnFilePathsChanged(paths); 151 watcher->OnFilePathsChanged(paths);
154 } 152 }
155 153
156 FilePathWatcherFSEvents::~FilePathWatcherFSEvents() { 154 FilePathWatcherFSEvents::~FilePathWatcherFSEvents() {
157 // This method may be called on either the libdispatch or task_runner() 155 // This method may be called on either the libdispatch or task_runner()
158 // thread. Checking callback_ on the libdispatch thread here is safe because 156 // thread. Checking callback_ on the libdispatch thread here is safe because
159 // it is executing in a task posted by Cancel() which first reset callback_. 157 // it is executing in a task posted by Cancel() which first reset callback_.
160 // PostTask forms a sufficient memory barrier to ensure that the value is 158 // PostTask forms a sufficient memory barrier to ensure that the value is
161 // consistent on the target thread. 159 // consistent on the target thread.
162 DCHECK(callback_.is_null()) 160 DCHECK(callback_.is_null())
163 << "Cancel() must be called before FilePathWatcher is destroyed."; 161 << "Cancel() must be called before FilePathWatcher is destroyed.";
164 } 162 }
165 163
166 void FilePathWatcherFSEvents::OnFilePathsChanged( 164 void FilePathWatcherFSEvents::OnFilePathsChanged(
167 const std::vector<FilePath>& paths) { 165 const std::vector<FilePath>& paths) {
168 DCHECK(g_task_runner.Get().RunsTasksOnCurrentThread());
Robert Sesek 2016/07/07 14:55:17 We lose these DCHECKs, but as noted in the linked
169 DCHECK(!resolved_target_.empty()); 166 DCHECK(!resolved_target_.empty());
170 task_runner()->PostTask( 167 task_runner()->PostTask(
171 FROM_HERE, Bind(&FilePathWatcherFSEvents::DispatchEvents, this, paths, 168 FROM_HERE, Bind(&FilePathWatcherFSEvents::DispatchEvents, this, paths,
172 target_, resolved_target_)); 169 target_, resolved_target_));
173 } 170 }
174 171
175 void FilePathWatcherFSEvents::DispatchEvents(const std::vector<FilePath>& paths, 172 void FilePathWatcherFSEvents::DispatchEvents(const std::vector<FilePath>& paths,
176 const FilePath& target, 173 const FilePath& target,
177 const FilePath& resolved_target) { 174 const FilePath& resolved_target) {
178 DCHECK(task_runner()->RunsTasksOnCurrentThread()); 175 DCHECK(task_runner()->RunsTasksOnCurrentThread());
179 176
180 // Don't issue callbacks after Cancel() has been called. 177 // Don't issue callbacks after Cancel() has been called.
181 if (is_cancelled() || callback_.is_null()) { 178 if (is_cancelled() || callback_.is_null()) {
182 return; 179 return;
183 } 180 }
184 181
185 for (const FilePath& path : paths) { 182 for (const FilePath& path : paths) {
186 if (resolved_target.IsParent(path) || resolved_target == path) { 183 if (resolved_target.IsParent(path) || resolved_target == path) {
187 callback_.Run(target, false); 184 callback_.Run(target, false);
188 return; 185 return;
189 } 186 }
190 } 187 }
191 } 188 }
192 189
193 void FilePathWatcherFSEvents::CancelOnMessageLoopThread() { 190 void FilePathWatcherFSEvents::CancelOnMessageLoopThread() {
194 // For all other implementations, the "message loop thread" is the IO thread, 191 // For all other implementations, the "message loop thread" is the IO thread,
195 // as returned by task_runner(). This implementation, however, needs to 192 // as returned by task_runner(). This implementation, however, needs to
196 // cancel pending work on the Dispatch Queue thread. 193 // cancel pending work on the Dispatch Queue thread.
197 DCHECK(g_task_runner.Get().RunsTasksOnCurrentThread());
198 194
199 if (fsevent_stream_) { 195 if (fsevent_stream_) {
200 DestroyEventStream(); 196 DestroyEventStream();
201 target_.clear(); 197 target_.clear();
202 resolved_target_.clear(); 198 resolved_target_.clear();
203 } 199 }
204 } 200 }
205 201
206 void FilePathWatcherFSEvents::UpdateEventStream( 202 void FilePathWatcherFSEvents::UpdateEventStream(
207 FSEventStreamEventId start_event) { 203 FSEventStreamEventId start_event) {
208 DCHECK(g_task_runner.Get().RunsTasksOnCurrentThread());
209
210 // It can happen that the watcher gets canceled while tasks that call this 204 // It can happen that the watcher gets canceled while tasks that call this
211 // function are still in flight, so abort if this situation is detected. 205 // function are still in flight, so abort if this situation is detected.
212 if (resolved_target_.empty()) 206 if (resolved_target_.empty())
213 return; 207 return;
214 208
215 if (fsevent_stream_) 209 if (fsevent_stream_)
216 DestroyEventStream(); 210 DestroyEventStream();
217 211
218 ScopedCFTypeRef<CFStringRef> cf_path(CFStringCreateWithCString( 212 ScopedCFTypeRef<CFStringRef> cf_path(CFStringCreateWithCString(
219 NULL, resolved_target_.value().c_str(), kCFStringEncodingMacHFS)); 213 NULL, resolved_target_.value().c_str(), kCFStringEncodingMacHFS));
(...skipping 10 matching lines...) Expand all
230 context.info = this; 224 context.info = this;
231 context.retain = NULL; 225 context.retain = NULL;
232 context.release = NULL; 226 context.release = NULL;
233 context.copyDescription = NULL; 227 context.copyDescription = NULL;
234 228
235 fsevent_stream_ = FSEventStreamCreate(NULL, &FSEventsCallback, &context, 229 fsevent_stream_ = FSEventStreamCreate(NULL, &FSEventsCallback, &context,
236 watched_paths, 230 watched_paths,
237 start_event, 231 start_event,
238 kEventLatencySeconds, 232 kEventLatencySeconds,
239 kFSEventStreamCreateFlagWatchRoot); 233 kFSEventStreamCreateFlagWatchRoot);
240 FSEventStreamSetDispatchQueue(fsevent_stream_, 234 FSEventStreamSetDispatchQueue(fsevent_stream_, queue_);
241 g_task_runner.Get().GetDispatchQueue());
242 235
243 if (!FSEventStreamStart(fsevent_stream_)) { 236 if (!FSEventStreamStart(fsevent_stream_)) {
244 task_runner()->PostTask( 237 task_runner()->PostTask(
245 FROM_HERE, Bind(&FilePathWatcherFSEvents::ReportError, this, target_)); 238 FROM_HERE, Bind(&FilePathWatcherFSEvents::ReportError, this, target_));
246 } 239 }
247 } 240 }
248 241
249 bool FilePathWatcherFSEvents::ResolveTargetPath() { 242 bool FilePathWatcherFSEvents::ResolveTargetPath() {
250 DCHECK(g_task_runner.Get().RunsTasksOnCurrentThread());
251 FilePath resolved = ResolvePath(target_).StripTrailingSeparators(); 243 FilePath resolved = ResolvePath(target_).StripTrailingSeparators();
252 bool changed = resolved != resolved_target_; 244 bool changed = resolved != resolved_target_;
253 resolved_target_ = resolved; 245 resolved_target_ = resolved;
254 if (resolved_target_.empty()) { 246 if (resolved_target_.empty()) {
255 task_runner()->PostTask( 247 task_runner()->PostTask(
256 FROM_HERE, Bind(&FilePathWatcherFSEvents::ReportError, this, target_)); 248 FROM_HERE, Bind(&FilePathWatcherFSEvents::ReportError, this, target_));
257 } 249 }
258 return changed; 250 return changed;
259 } 251 }
260 252
261 void FilePathWatcherFSEvents::ReportError(const FilePath& target) { 253 void FilePathWatcherFSEvents::ReportError(const FilePath& target) {
262 DCHECK(task_runner()->RunsTasksOnCurrentThread()); 254 DCHECK(task_runner()->RunsTasksOnCurrentThread());
263 if (!callback_.is_null()) { 255 if (!callback_.is_null()) {
264 callback_.Run(target, true); 256 callback_.Run(target, true);
265 } 257 }
266 } 258 }
267 259
268 void FilePathWatcherFSEvents::DestroyEventStream() { 260 void FilePathWatcherFSEvents::DestroyEventStream() {
269 FSEventStreamStop(fsevent_stream_); 261 FSEventStreamStop(fsevent_stream_);
270 FSEventStreamInvalidate(fsevent_stream_); 262 FSEventStreamInvalidate(fsevent_stream_);
271 FSEventStreamRelease(fsevent_stream_); 263 FSEventStreamRelease(fsevent_stream_);
272 fsevent_stream_ = NULL; 264 fsevent_stream_ = NULL;
273 } 265 }
274 266
275 void FilePathWatcherFSEvents::StartEventStream(FSEventStreamEventId start_event, 267 void FilePathWatcherFSEvents::StartEventStream(FSEventStreamEventId start_event,
276 const FilePath& path) { 268 const FilePath& path) {
277 DCHECK(g_task_runner.Get().RunsTasksOnCurrentThread());
278 DCHECK(resolved_target_.empty()); 269 DCHECK(resolved_target_.empty());
279 270
280 target_ = path; 271 target_ = path;
281 ResolveTargetPath(); 272 ResolveTargetPath();
282 UpdateEventStream(start_event); 273 UpdateEventStream(start_event);
283 } 274 }
284 275
285 } // namespace base 276 } // namespace base
OLDNEW
« no previous file with comments | « base/files/file_path_watcher_fsevents.h ('k') | base/mac/libdispatch_task_runner.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698