Index: base/file_path_component_watcher_mac.cc |
diff --git a/base/file_path_component_watcher_mac.cc b/base/file_path_component_watcher_mac.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..b126c8310f910871d390036927d67a7df585df16 |
--- /dev/null |
+++ b/base/file_path_component_watcher_mac.cc |
@@ -0,0 +1,179 @@ |
+// Copyright (c) 2010 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "base/file_path_component_watcher.h" |
+ |
+#include <CoreFoundation/CoreFoundation.h> |
+#include <fcntl.h> |
+#include <sys/event.h> |
+#include <sys/param.h> |
+#include <vector> |
Mark Mentovai
2011/03/11 20:13:52
Blank line before. C++ system headers are in a sep
|
+ |
+#include "base/file_util.h" |
+#include "base/mac/scoped_cftyperef.h" |
+ |
+namespace base { |
+ |
+namespace { |
+ |
+// Mac-specific file watcher implementation based on kqueue. |
Mark Mentovai
2011/03/11 20:13:52
This sounds like it’s going to eat up a lot of fil
|
+class FilePathComponentWatcherImpl |
+ : public FilePathComponentWatcher::PlatformDelegate { |
+ public: |
+ FilePathComponentWatcherImpl(FilePathComponentWatcher* watcher) |
Mark Mentovai
2011/03/11 20:13:52
explicit
|
+ : watcher_(watcher) {} |
+ virtual ~FilePathComponentWatcherImpl() {} |
Mark Mentovai
2011/03/11 20:13:52
Not needed?
Scott Byer
2011/03/11 20:25:02
What about closing the file descriptors on exit, i
|
+ |
+ void OnFilePathChanged(CFFileDescriptorRef fd); |
+ |
+ // FilePathComponentWatcher::PlatformDelegate overrides. |
+ virtual bool Watch(const FilePath& path, |
+ FilePathComponentWatcher::Delegate* delegate); |
+ virtual void Cancel(); |
+ |
+ private: |
+ FilePathComponentWatcher* watcher_; |
+ base::mac::ScopedCFTypeRef<CFFileDescriptorRef> kqueue_fd_; |
+ std::vector<int> file_descriptors_; |
+ scoped_refptr<FilePathComponentWatcher::Delegate> delegate_; |
+ FilePath path_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(FilePathComponentWatcherImpl); |
+}; |
+ |
+void CloseFile(int fd) { |
+ if(close(fd) != 0) { |
Mark Mentovai
2011/03/11 20:13:52
if<space>(close…
|
+ PLOG(ERROR) << "close"; |
+ } |
+} |
+ |
+void CloseFileDescriptors(std::vector<int>& descriptors) { |
Mark Mentovai
2011/03/11 20:13:52
It’s uncommon (and therefore unexpected) to pass n
|
+ std::for_each(descriptors.begin(), descriptors.end(), CloseFile); |
+ descriptors.clear(); |
+} |
+ |
+bool OpenFileDescriptorsForPath(FilePath path, std::vector<int>* descriptors) { |
Mark Mentovai
2011/03/11 20:13:52
Start this function with CloseFileDescriptors(*des
|
+ FilePath normalized_path; |
+ if (!file_util::NormalizeFilePath(path, &normalized_path)) { |
+ return false; |
+ } |
+ |
+ std::vector<FilePath::StringType> components; |
+ normalized_path.GetComponents(&components); |
+ |
+ if (components.size() < 1) { |
+ return false; |
+ } |
+ |
+ FilePath built_path(components[0]); |
+ std::vector<int> local_descriptors; |
+ for(std::vector<FilePath::StringType>::iterator i = components.begin() + 1; |
+ i != components.end(); ++i) { |
+ built_path = built_path.Append(*i); |
+ int fd = open(built_path.value().c_str(), O_RDONLY); |
+ if (fd == -1) { |
+ PLOG(ERROR) << "open " << built_path.value(); |
+ CloseFileDescriptors(*descriptors); |
+ descriptors->clear(); |
+ return false; |
+ } else { |
Mark Mentovai
2011/03/11 20:13:52
No else needed after a return.
|
+ descriptors->push_back(fd); |
+ } |
+ } |
+ return true; |
+} |
+ |
+void PathComponentWatcherTrampoline(CFFileDescriptorRef fd, |
+ CFOptionFlags callBackTypes, |
Mark Mentovai
2011/03/11 20:13:52
Match our style: callback_types.
|
+ void *info) { |
+ FilePathComponentWatcherImpl *impl = |
+ reinterpret_cast<FilePathComponentWatcherImpl*>(info); |
+ impl->OnFilePathChanged(fd); |
+} |
+ |
+// FilePathComponentWatcherImpl implementation: |
+ |
+void FilePathComponentWatcherImpl::OnFilePathChanged(CFFileDescriptorRef fd) { |
Mark Mentovai
2011/03/11 20:13:52
I would find this class implementation easier to r
|
+ int native_fd = CFFileDescriptorGetNativeDescriptor(fd); |
+ struct kevent kev; |
+ struct timespec timeout = {0, 0}; |
+ int status = kevent(native_fd, NULL, 0, &kev, 1, &timeout); |
+ FilePath new_path; |
Mark Mentovai
2011/03/11 20:13:52
Don’t declare this ’til you use it, which looks li
|
+ if (status < 0) { |
+ PLOG(ERROR) << "kevent"; |
+ if (delegate_) { |
+ delegate_->OnError(watcher_); |
+ } |
+ } else if (delegate_) { |
Mark Mentovai
2011/03/11 20:13:52
When will there not be a delegate?
Scott Byer
2011/03/11 20:25:02
If delegate_ is NULL, I would think we'd want to D
|
+ char path[MAXPATHLEN]; |
Mark Mentovai
2011/03/11 20:13:52
Anything involving MAXPATHLEN is a hack. I think w
|
+ int path_fd = file_descriptors_[file_descriptors_.size() - 1]; |
Mark Mentovai
2011/03/11 20:13:52
You can just use file_descriptors_.back().
|
+ if (fcntl(path_fd, F_GETPATH, path) != -1) { |
+ new_path = FilePath(path); |
+ } |
+ delegate_->OnFilePathComponentsChanged(watcher_, path_, new_path); |
Mark Mentovai
2011/03/11 20:13:52
new_path may be empty here, which I’m assuming is
|
+ } |
+} |
+ |
+bool FilePathComponentWatcherImpl::Watch( |
+ const FilePath& path, FilePathComponentWatcher::Delegate* delegate) { |
+ delegate_ = delegate; |
+ path_ = path; |
+ int kq = kqueue(); |
+ if (kq == -1) { |
+ PLOG(ERROR) << "kqueue"; |
+ return false; |
+ } |
+ if (!OpenFileDescriptorsForPath(path, &file_descriptors_)) { |
+ LOG(ERROR) << "FileDescriptorsForPath: " << path.value(); |
Mark Mentovai
2011/03/11 20:13:52
OpenFileDescriptorsForPath?
|
+ return false; |
+ } |
+ |
+ size_t count = file_descriptors_.size(); |
+ |
+ scoped_ptr<struct kevent> changes(reinterpret_cast<struct kevent*>( |
Mark Mentovai
2011/03/11 20:13:52
scoped_ptr is intended to be used with objects all
|
+ calloc(count, sizeof(struct kevent)))); |
+ |
+ for(size_t i = 0; i != count; ++i) { |
Mark Mentovai
2011/03/11 20:13:52
for<space>(size_t…
|
+ EV_SET(&(changes.get())[i], file_descriptors_[i], EVFILT_VNODE, |
+ (EV_ADD | EV_CLEAR | EV_ENABLE), |
+ (NOTE_RENAME | NOTE_DELETE | NOTE_ATTRIB | NOTE_REVOKE), 0, NULL); |
+ } |
+ |
+ if (HANDLE_EINTR(kevent(kq, changes.get(), count, NULL, 0, NULL)) == -1) { |
Mark Mentovai
2011/03/11 20:13:52
This confused me for a second, because I saw the N
|
+ PLOG(ERROR) << "kevent"; |
+ return false; |
+ } |
+ |
+ CFFileDescriptorContext context; |
+ memset(&context, 0, sizeof(context)); |
Mark Mentovai
2011/03/11 20:13:52
Or just CFFileDescriptorContext context = {0};
|
+ context.info = this; |
+ |
+ kqueue_fd_.reset( |
+ CFFileDescriptorCreate(kCFAllocatorDefault, kq, true, |
+ PathComponentWatcherTrampoline, &context)); |
+ CFFileDescriptorEnableCallBacks(kqueue_fd_, kCFFileDescriptorReadCallBack); |
+ base::mac::ScopedCFTypeRef<CFRunLoopSourceRef> source( |
+ CFFileDescriptorCreateRunLoopSource(kCFAllocatorDefault, kqueue_fd_, 0)); |
+ CFRunLoopAddSource(CFRunLoopGetMain(), |
Mark Mentovai
2011/03/11 20:13:52
The run loop source leaks because nobody ever remo
Mark Mentovai
2011/03/11 20:13:52
I usually use CFRunLoopGetCurrent. Do you have a r
|
+ source, |
Mark Mentovai
2011/03/11 20:13:52
This didn’t all fit on one line?
|
+ kCFRunLoopDefaultMode); |
+ |
+ return true; |
+} |
+ |
+void FilePathComponentWatcherImpl::Cancel() { |
+ if (kqueue_fd_.get()) { |
+ CFFileDescriptorInvalidate(kqueue_fd_); |
+ kqueue_fd_.reset(); |
+ } |
+ CloseFileDescriptors(file_descriptors_); |
+} |
+ |
+} // namespace |
+ |
+FilePathComponentWatcher::FilePathComponentWatcher() { |
+ impl_ = new FilePathComponentWatcherImpl(this); |
+} |
+ |
+} // namespace base |