Chromium Code Reviews| 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 |