summaryrefslogtreecommitdiffstats
path: root/base
diff options
context:
space:
mode:
Diffstat (limited to 'base')
-rw-r--r--base/directory_watcher.h15
-rw-r--r--base/directory_watcher_inotify.cc321
-rw-r--r--base/directory_watcher_mac.cc3
-rw-r--r--base/directory_watcher_stub.cc2
-rw-r--r--base/directory_watcher_unittest.cc49
-rw-r--r--base/directory_watcher_win.cc5
-rw-r--r--base/file_util.h6
-rw-r--r--base/file_util_posix.cc10
8 files changed, 294 insertions, 117 deletions
diff --git a/base/directory_watcher.h b/base/directory_watcher.h
index ae01149..2d5b901 100644
--- a/base/directory_watcher.h
+++ b/base/directory_watcher.h
@@ -11,6 +11,7 @@
#include "base/ref_counted.h"
class FilePath;
+class MessageLoop;
// This class lets you register interest in changes on a directory.
// The delegate will get called whenever a file is added or changed in the
@@ -19,6 +20,7 @@ class DirectoryWatcher {
public:
class Delegate {
public:
+ virtual ~Delegate() {}
virtual void OnDirectoryChanged(const FilePath& path) = 0;
};
@@ -27,15 +29,18 @@ class DirectoryWatcher {
// Register interest in any changes in the directory |path|.
// OnDirectoryChanged will be called back for each change within the dir.
- // If |recursive| is true, the delegate will be notified for each change
- // within the directory tree starting at |path|. Returns false on error.
+ // Any background operations will be ran on |backend_loop|, or inside Watch
+ // if |backend_loop| is NULL. If |recursive| is true, the delegate will be
+ // notified for each change within the directory tree starting at |path|.
+ // Returns false on error.
//
// Note: on Windows you may got more notifications for non-recursive watch
// than you expect, especially on versions earlier than Vista. The behavior
// is consistent on any particular version of Windows, but not across
// different versions.
- bool Watch(const FilePath& path, Delegate* delegate, bool recursive) {
- return impl_->Watch(path, delegate, recursive);
+ bool Watch(const FilePath& path, Delegate* delegate,
+ MessageLoop* backend_loop, bool recursive) {
+ return impl_->Watch(path, delegate, backend_loop, recursive);
}
// Used internally to encapsulate different members on different platforms.
@@ -43,7 +48,7 @@ class DirectoryWatcher {
public:
virtual ~PlatformDelegate() {}
virtual bool Watch(const FilePath& path, Delegate* delegate,
- bool recursive) = 0;
+ MessageLoop* backend_loop, bool recursive) = 0;
};
private:
diff --git a/base/directory_watcher_inotify.cc b/base/directory_watcher_inotify.cc
index 86e7bd9..ee42127 100644
--- a/base/directory_watcher_inotify.cc
+++ b/base/directory_watcher_inotify.cc
@@ -6,8 +6,8 @@
#include <errno.h>
#include <string.h>
-#include <sys/ioctl.h>
#include <sys/inotify.h>
+#include <sys/ioctl.h>
#include <sys/select.h>
#include <unistd.h>
@@ -18,6 +18,7 @@
#include "base/eintr_wrapper.h"
#include "base/file_path.h"
+#include "base/file_util.h"
#include "base/hash_tables.h"
#include "base/lock.h"
#include "base/logging.h"
@@ -26,43 +27,38 @@
#include "base/singleton.h"
#include "base/task.h"
#include "base/thread.h"
+#include "base/waitable_event.h"
namespace {
+class DirectoryWatcherImpl;
+
// Singleton to manage all inotify watches.
class InotifyReader {
public:
typedef int Watch; // Watch descriptor used by AddWatch and RemoveWatch.
static const Watch kInvalidWatch = -1;
- // Watch |path| for changes. |delegate| will be notified on each change. Does
- // not check for duplicates. If you call it n times with same |path|
- // and |delegate|, it will receive n notifications for each change
- // in |path|. It makes implementation of DirectoryWatcher simple.
+ // Watch |path| for changes. |watcher| will be notified on each change.
// Returns kInvalidWatch on failure.
- Watch AddWatch(const FilePath& path, DirectoryWatcher::Delegate* delegate);
+ Watch AddWatch(const FilePath& path, DirectoryWatcherImpl* watcher);
- // Remove |watch| for |delegate|. If you had n watches for same |delegate|
- // and path, after calling this function you will have n - 1.
- // Returns true on success.
- bool RemoveWatch(Watch watch, DirectoryWatcher::Delegate* delegate);
+ // Remove |watch|. Returns true on success.
+ bool RemoveWatch(Watch watch, DirectoryWatcherImpl* watcher);
// Callback for InotifyReaderTask.
- void OnInotifyEvent(inotify_event* event);
+ void OnInotifyEvent(const inotify_event* event);
private:
friend struct DefaultSingletonTraits<InotifyReader>;
- typedef std::pair<DirectoryWatcher::Delegate*, MessageLoop*> DelegateInfo;
- typedef std::multiset<DelegateInfo> DelegateSet;
+ typedef std::set<DirectoryWatcherImpl*> WatcherSet;
InotifyReader();
~InotifyReader();
// We keep track of which delegates want to be notified on which watches.
- // Multiset is used because there may be many DirectoryWatchers for same path
- // and delegate.
- base::hash_map<Watch, DelegateSet> delegates_;
+ base::hash_map<Watch, WatcherSet> watchers_;
// For each watch we also want to know the path it's watching.
base::hash_map<Watch, FilePath> paths_;
@@ -85,6 +81,114 @@ class InotifyReader {
DISALLOW_COPY_AND_ASSIGN(InotifyReader);
};
+class DirectoryWatcherImpl : public DirectoryWatcher::PlatformDelegate {
+ public:
+ typedef std::set<FilePath> FilePathSet;
+
+ DirectoryWatcherImpl();
+ ~DirectoryWatcherImpl();
+
+ void EnsureSetupFinished();
+
+ // Called for each event coming from one of watches.
+ void OnInotifyEvent(const inotify_event* event);
+
+ // Callback for RegisterSubtreeWatchesTask.
+ bool OnEnumeratedSubtree(const FilePathSet& paths);
+
+ // Start watching |path| for changes and notify |delegate| on each change.
+ // If |recursive| is true, watch entire subtree.
+ // Returns true if watch for |path| has been added successfully. Watches
+ // required for |recursive| are added on a background thread and have no
+ // effect on the return value.
+ virtual bool Watch(const FilePath& path, DirectoryWatcher::Delegate* delegate,
+ MessageLoop* backend_loop, bool recursive);
+
+ private:
+ typedef std::set<InotifyReader::Watch> WatchSet;
+ typedef std::set<ino_t> InodeSet;
+
+ // Returns true if |inode| is watched by DirectoryWatcherImpl.
+ bool IsInodeWatched(ino_t inode) const;
+
+ // Delegate to notify upon changes.
+ DirectoryWatcher::Delegate* delegate_;
+
+ // Path we're watching (passed to delegate).
+ FilePath root_path_;
+
+ // Watch returned by InotifyReader.
+ InotifyReader::Watch watch_;
+
+ // Set of watched inodes.
+ InodeSet inodes_watched_;
+
+ // Keep track of registered watches.
+ WatchSet watches_;
+
+ // Lock to protect inodes_watched_ and watches_.
+ Lock lock_;
+
+ // Flag set to true when recursively watching subtree.
+ bool recursive_;
+
+ // Loop where we post directory change notifications to.
+ MessageLoop* loop_;
+
+ // Event signaled when the background task finished adding initial inotify
+ // watches for recursive watch.
+ base::WaitableEvent recursive_setup_finished_;
+
+ DISALLOW_COPY_AND_ASSIGN(DirectoryWatcherImpl);
+};
+
+class RegisterSubtreeWatchesTask : public Task {
+ public:
+ RegisterSubtreeWatchesTask(DirectoryWatcherImpl* watcher,
+ const FilePath& path)
+ : watcher_(watcher),
+ path_(path) {
+ }
+
+ virtual void Run() {
+ file_util::FileEnumerator dir_list(path_, true,
+ file_util::FileEnumerator::DIRECTORIES);
+
+ DirectoryWatcherImpl::FilePathSet subtree;
+ for (FilePath subdirectory = dir_list.Next();
+ !subdirectory.empty();
+ subdirectory = dir_list.Next()) {
+ subtree.insert(subdirectory);
+ }
+ watcher_->OnEnumeratedSubtree(subtree);
+ }
+
+ private:
+ DirectoryWatcherImpl* watcher_;
+ FilePath path_;
+
+ DISALLOW_COPY_AND_ASSIGN(RegisterSubtreeWatchesTask);
+};
+
+class DirectoryWatcherImplNotifyTask : public Task {
+ public:
+ DirectoryWatcherImplNotifyTask(DirectoryWatcher::Delegate* delegate,
+ const FilePath& path)
+ : delegate_(delegate),
+ path_(path) {
+ }
+
+ virtual void Run() {
+ delegate_->OnDirectoryChanged(path_);
+ }
+
+ private:
+ DirectoryWatcher::Delegate* delegate_;
+ FilePath path_;
+
+ DISALLOW_COPY_AND_ASSIGN(DirectoryWatcherImplNotifyTask);
+};
+
class InotifyReaderTask : public Task {
public:
InotifyReaderTask(InotifyReader* reader, int inotify_fd, int shutdown_fd)
@@ -151,25 +255,6 @@ class InotifyReaderTask : public Task {
DISALLOW_COPY_AND_ASSIGN(InotifyReaderTask);
};
-class InotifyReaderNotifyTask : public Task {
- public:
- InotifyReaderNotifyTask(DirectoryWatcher::Delegate* delegate,
- const FilePath& path)
- : delegate_(delegate),
- path_(path) {
- }
-
- virtual void Run() {
- delegate_->OnDirectoryChanged(path_);
- }
-
- private:
- DirectoryWatcher::Delegate* delegate_;
- FilePath path_;
-
- DISALLOW_COPY_AND_ASSIGN(InotifyReaderNotifyTask);
-};
-
InotifyReader::InotifyReader()
: thread_("inotify_reader"),
inotify_fd_(inotify_init()),
@@ -199,7 +284,8 @@ InotifyReader::~InotifyReader() {
}
InotifyReader::Watch InotifyReader::AddWatch(
- const FilePath& path, DirectoryWatcher::Delegate* delegate) {
+ const FilePath& path, DirectoryWatcherImpl* watcher) {
+
if (!valid_)
return kInvalidWatch;
@@ -208,19 +294,20 @@ InotifyReader::Watch InotifyReader::AddWatch(
Watch watch = inotify_add_watch(inotify_fd_, path.value().c_str(),
IN_CREATE | IN_DELETE |
IN_CLOSE_WRITE | IN_MOVE);
+
if (watch == kInvalidWatch)
return kInvalidWatch;
if (paths_[watch].empty())
paths_[watch] = path; // We don't yet watch this path.
- delegates_[watch].insert(std::make_pair(delegate, MessageLoop::current()));
+ watchers_[watch].insert(watcher);
return watch;
}
bool InotifyReader::RemoveWatch(Watch watch,
- DirectoryWatcher::Delegate* delegate) {
+ DirectoryWatcherImpl* watcher) {
if (!valid_)
return false;
@@ -229,89 +316,148 @@ bool InotifyReader::RemoveWatch(Watch watch,
if (paths_[watch].empty())
return false; // We don't recognize this watch.
- // Only erase one occurrence of delegate (there may be more).
- delegates_[watch].erase(
- delegates_[watch].find(std::make_pair(delegate, MessageLoop::current())));
+ watchers_[watch].erase(watcher);
- if (delegates_[watch].empty()) {
+ if (watchers_[watch].empty()) {
paths_.erase(watch);
- delegates_.erase(watch);
-
+ watchers_.erase(watch);
return (inotify_rm_watch(inotify_fd_, watch) == 0);
}
return true;
}
-void InotifyReader::OnInotifyEvent(inotify_event* event) {
+void InotifyReader::OnInotifyEvent(const inotify_event* event) {
if (event->mask & IN_IGNORED)
return;
- DelegateSet delegates_to_notify;
+ WatcherSet watchers_to_notify;
FilePath changed_path;
{
AutoLock auto_lock(lock_);
changed_path = paths_[event->wd];
- delegates_to_notify.insert(delegates_[event->wd].begin(),
- delegates_[event->wd].end());
+ watchers_to_notify.insert(watchers_[event->wd].begin(),
+ watchers_[event->wd].end());
}
- DelegateSet::iterator i;
- for (i = delegates_to_notify.begin(); i != delegates_to_notify.end(); ++i) {
- DirectoryWatcher::Delegate* delegate = i->first;
- MessageLoop* loop = i->second;
- loop->PostTask(FROM_HERE,
- new InotifyReaderNotifyTask(delegate, changed_path));
+ for (WatcherSet::iterator watcher = watchers_to_notify.begin();
+ watcher != watchers_to_notify.end();
+ ++watcher) {
+ (*watcher)->OnInotifyEvent(event);
}
}
-class DirectoryWatcherImpl : public DirectoryWatcher::PlatformDelegate {
- public:
- DirectoryWatcherImpl() : watch_(InotifyReader::kInvalidWatch) {}
- ~DirectoryWatcherImpl();
+DirectoryWatcherImpl::DirectoryWatcherImpl()
+ : watch_(InotifyReader::kInvalidWatch),
+ recursive_setup_finished_(false, false) {
+}
- virtual bool Watch(const FilePath& path, DirectoryWatcher::Delegate* delegate,
- bool recursive);
+DirectoryWatcherImpl::~DirectoryWatcherImpl() {
+ if (watch_ == InotifyReader::kInvalidWatch)
+ return;
- private:
- // Delegate to notify upon changes.
- DirectoryWatcher::Delegate* delegate_;
+ if (recursive_)
+ recursive_setup_finished_.Wait();
+ for (WatchSet::iterator watch = watches_.begin();
+ watch != watches_.end();
+ ++watch) {
+ Singleton<InotifyReader>::get()->RemoveWatch(*watch, this);
+ }
+ watches_.clear();
+ inodes_watched_.clear();
+}
- // Path we're watching (passed to delegate).
- FilePath path_;
+void DirectoryWatcherImpl::OnInotifyEvent(const inotify_event* event) {
+ loop_->PostTask(FROM_HERE,
+ new DirectoryWatcherImplNotifyTask(delegate_, root_path_));
- // Watch returned by InotifyReader.
- InotifyReader::Watch watch_;
+ if (!(event->mask & IN_ISDIR))
+ return;
- DISALLOW_COPY_AND_ASSIGN(DirectoryWatcherImpl);
-};
+ if (event->mask & IN_CREATE || event->mask & IN_MOVED_TO) {
+ // TODO(phajdan.jr): add watch for this new directory.
+ NOTIMPLEMENTED();
+ } else if (event->mask & IN_DELETE || event->mask & IN_MOVED_FROM) {
+ // TODO(phajdan.jr): remove our watch for this directory.
+ NOTIMPLEMENTED();
+ }
+}
-DirectoryWatcherImpl::~DirectoryWatcherImpl() {
- if (watch_ != InotifyReader::kInvalidWatch)
- Singleton<InotifyReader>::get()->RemoveWatch(watch_, delegate_);
+bool DirectoryWatcherImpl::IsInodeWatched(ino_t inode) const {
+ return inodes_watched_.find(inode) != inodes_watched_.end();
}
-bool DirectoryWatcherImpl::Watch(const FilePath& path,
- DirectoryWatcher::Delegate* delegate, bool recursive) {
- DCHECK(watch_ == InotifyReader::kInvalidWatch); // Can only watch one path.
-
- if (recursive) {
- // TODO(phajdan.jr): Support recursive watches.
- // Unfortunately inotify has no "native" support for them, but it can be
- // emulated by walking the directory tree and setting up watches for each
- // directory. Of course this is ineffective for large directory trees.
- // For the algorithm, see the link below:
- // http://osdir.com/ml/gnome.dashboard.devel/2004-10/msg00022.html
- NOTIMPLEMENTED();
+bool DirectoryWatcherImpl::OnEnumeratedSubtree(const FilePathSet& subtree) {
+ DCHECK(recursive_);
+
+ if (watch_ == InotifyReader::kInvalidWatch) {
+ recursive_setup_finished_.Signal();
return false;
}
+ bool success = true;
+ AutoLock auto_lock(lock_);
+ for (FilePathSet::iterator subdirectory = subtree.begin();
+ subdirectory != subtree.end();
+ ++subdirectory) {
+ ino_t inode;
+ if (!file_util::GetInode(*subdirectory, &inode)) {
+ success = false;
+ continue;
+ }
+ if (IsInodeWatched(inode))
+ continue;
+ InotifyReader::Watch watch =
+ Singleton<InotifyReader>::get()->AddWatch(*subdirectory, this);
+ if (watch != InotifyReader::kInvalidWatch) {
+ watches_.insert(watch);
+ inodes_watched_.insert(inode);
+ }
+ }
+ recursive_setup_finished_.Signal();
+ return success;
+}
+
+bool DirectoryWatcherImpl::Watch(const FilePath& path,
+ DirectoryWatcher::Delegate* delegate,
+ MessageLoop* backend_loop, bool recursive) {
+
+ // Can only watch one path.
+ DCHECK(watch_ == InotifyReader::kInvalidWatch);
+
+ ino_t inode;
+ if (!file_util::GetInode(path, &inode))
+ return false;
+
+ InotifyReader::Watch watch =
+ Singleton<InotifyReader>::get()->AddWatch(path, this);
+ if (watch == InotifyReader::kInvalidWatch)
+ return false;
+
delegate_ = delegate;
- path_ = path;
- watch_ = Singleton<InotifyReader>::get()->AddWatch(path, delegate_);
+ recursive_ = recursive;
+ root_path_ = path;
+ watch_ = watch;
+ loop_ = MessageLoop::current();
+
+ {
+ AutoLock auto_lock(lock_);
+ inodes_watched_.insert(inode);
+ watches_.insert(watch_);
+ }
+
+ if (recursive_) {
+ Task* subtree_task = new RegisterSubtreeWatchesTask(this, root_path_);
+ if (backend_loop) {
+ backend_loop->PostTask(FROM_HERE, subtree_task);
+ } else {
+ subtree_task->Run();
+ delete subtree_task;
+ }
+ }
- return watch_ != InotifyReader::kInvalidWatch;
+ return true;
}
} // namespace
@@ -319,3 +465,4 @@ bool DirectoryWatcherImpl::Watch(const FilePath& path,
DirectoryWatcher::DirectoryWatcher() {
impl_ = new DirectoryWatcherImpl();
}
+
diff --git a/base/directory_watcher_mac.cc b/base/directory_watcher_mac.cc
index d4b3082..dc3eba6 100644
--- a/base/directory_watcher_mac.cc
+++ b/base/directory_watcher_mac.cc
@@ -28,7 +28,7 @@ class DirectoryWatcherImpl : public DirectoryWatcher::PlatformDelegate {
}
virtual bool Watch(const FilePath& path, DirectoryWatcher::Delegate* delegate,
- bool recursive);
+ MessageLoop* backend_loop, bool recursive);
void OnFSEventsCallback(const FilePath& event_path) {
DCHECK(!path_.value().empty());
@@ -72,6 +72,7 @@ void FSEventsCallback(ConstFSEventStreamRef stream,
bool DirectoryWatcherImpl::Watch(const FilePath& path,
DirectoryWatcher::Delegate* delegate,
+ MessageLoop* backend_loop,
bool recursive) {
DCHECK(path_.value().empty()); // Can only watch one path.
diff --git a/base/directory_watcher_stub.cc b/base/directory_watcher_stub.cc
index a92a84c..92cc5ff 100644
--- a/base/directory_watcher_stub.cc
+++ b/base/directory_watcher_stub.cc
@@ -10,7 +10,7 @@
class DirectoryWatcherImpl : public DirectoryWatcher::PlatformDelegate {
public:
virtual bool Watch(const FilePath& path, DirectoryWatcher::Delegate* delegate,
- bool recursive) {
+ MessageLoop* backend_loop, bool recursive) {
return false;
}
};
diff --git a/base/directory_watcher_unittest.cc b/base/directory_watcher_unittest.cc
index 6a2abc9..1b15de8 100644
--- a/base/directory_watcher_unittest.cc
+++ b/base/directory_watcher_unittest.cc
@@ -13,6 +13,7 @@
#include "base/path_service.h"
#include "base/platform_thread.h"
#include "base/string_util.h"
+#include "base/thread.h"
#if defined(OS_WIN)
#include "base/win_util.h"
#endif // defined(OS_WIN)
@@ -151,7 +152,7 @@ class TestDelegate : public DirectoryWatcher::Delegate {
TEST_F(DirectoryWatcherTest, NewFile) {
DirectoryWatcher watcher;
TestDelegate delegate(this);
- ASSERT_TRUE(watcher.Watch(test_dir_, &delegate, false));
+ ASSERT_TRUE(watcher.Watch(test_dir_, &delegate, NULL, false));
SetExpectedNumberOfNotifiedDelegates(1);
ASSERT_TRUE(WriteTestFile(test_dir_.AppendASCII("test_file"), "content"));
@@ -166,7 +167,7 @@ TEST_F(DirectoryWatcherTest, ModifiedFile) {
DirectoryWatcher watcher;
TestDelegate delegate(this);
- ASSERT_TRUE(watcher.Watch(test_dir_, &delegate, false));
+ ASSERT_TRUE(watcher.Watch(test_dir_, &delegate, NULL, false));
// Now make sure we get notified if the file is modified.
SetExpectedNumberOfNotifiedDelegates(1);
@@ -181,7 +182,7 @@ TEST_F(DirectoryWatcherTest, DeletedFile) {
DirectoryWatcher watcher;
TestDelegate delegate(this);
- ASSERT_TRUE(watcher.Watch(test_dir_, &delegate, false));
+ ASSERT_TRUE(watcher.Watch(test_dir_, &delegate, NULL, false));
// Now make sure we get notified if the file is deleted.
SetExpectedNumberOfNotifiedDelegates(1);
@@ -195,7 +196,7 @@ TEST_F(DirectoryWatcherTest, Unregister) {
{
DirectoryWatcher watcher;
- ASSERT_TRUE(watcher.Watch(test_dir_, &delegate, false));
+ ASSERT_TRUE(watcher.Watch(test_dir_, &delegate, NULL, false));
// And then let it fall out of scope, clearing its watch.
}
@@ -209,15 +210,10 @@ TEST_F(DirectoryWatcherTest, Unregister) {
TEST_F(DirectoryWatcherTest, SubDirRecursive) {
FilePath subdir(CreateTestDirDirectoryASCII("SubDir", true));
-#if defined(OS_LINUX)
- // TODO(port): Recursive watches are not implemented on Linux.
- return;
-#endif // !defined(OS_WIN)
-
// Verify that modifications to a subdirectory are noticed by recursive watch.
TestDelegate delegate(this);
DirectoryWatcher watcher;
- ASSERT_TRUE(watcher.Watch(test_dir_, &delegate, true));
+ ASSERT_TRUE(watcher.Watch(test_dir_, &delegate, NULL, true));
// Write a file to the subdir.
SetExpectedNumberOfNotifiedDelegates(1);
ASSERT_TRUE(WriteTestFile(subdir.AppendASCII("test_file"), "some content"));
@@ -244,7 +240,7 @@ TEST_F(DirectoryWatcherTest, SubDirNonRecursive) {
// by a not-recursive watch.
DirectoryWatcher watcher;
TestDelegate delegate(this);
- ASSERT_TRUE(watcher.Watch(test_dir_, &delegate, false));
+ ASSERT_TRUE(watcher.Watch(test_dir_, &delegate, NULL, false));
// Modify the test file. There should be no notifications.
SetExpectedNumberOfNotifiedDelegates(0);
@@ -276,7 +272,7 @@ class Deleter : public DirectoryWatcher::Delegate {
TEST_F(DirectoryWatcherTest, DeleteDuringNotify) {
DirectoryWatcher* watcher = new DirectoryWatcher;
Deleter deleter(watcher, &loop_); // Takes ownership of watcher.
- ASSERT_TRUE(watcher->Watch(test_dir_, &deleter, false));
+ ASSERT_TRUE(watcher->Watch(test_dir_, &deleter, NULL, false));
ASSERT_TRUE(WriteTestFile(test_dir_.AppendASCII("test_file"), "content"));
loop_.Run();
@@ -286,11 +282,21 @@ TEST_F(DirectoryWatcherTest, DeleteDuringNotify) {
ASSERT_TRUE(deleter.watcher_.get() == NULL);
}
+TEST_F(DirectoryWatcherTest, BackendLoop) {
+ base::Thread thread("test");
+ ASSERT_TRUE(thread.Start());
+
+ DirectoryWatcher watcher;
+ TestDelegate delegate(this);
+ ASSERT_TRUE(watcher.Watch(test_dir_, &delegate, thread.message_loop(),
+ true));
+}
+
TEST_F(DirectoryWatcherTest, MultipleWatchersSingleFile) {
DirectoryWatcher watcher1, watcher2;
TestDelegate delegate1(this), delegate2(this);
- ASSERT_TRUE(watcher1.Watch(test_dir_, &delegate1, false));
- ASSERT_TRUE(watcher2.Watch(test_dir_, &delegate2, false));
+ ASSERT_TRUE(watcher1.Watch(test_dir_, &delegate1, NULL, false));
+ ASSERT_TRUE(watcher2.Watch(test_dir_, &delegate2, NULL, false));
SetExpectedNumberOfNotifiedDelegates(2);
ASSERT_TRUE(WriteTestFile(test_dir_.AppendASCII("test_file"), "content"));
@@ -304,7 +310,8 @@ TEST_F(DirectoryWatcherTest, MultipleWatchersDifferentFiles) {
FilePath subdirs[kNumberOfWatchers];
for (int i = 0; i < kNumberOfWatchers; i++) {
subdirs[i] = CreateTestDirDirectoryASCII("Dir" + IntToString(i), false);
- ASSERT_TRUE(watchers[i].Watch(subdirs[i], &delegates[i], false));
+ ASSERT_TRUE(watchers[i].Watch(subdirs[i], &delegates[i],
+ NULL, ((i % 2) == 0)));
}
for (int i = 0; i < kNumberOfWatchers; i++) {
// Verify that we only get modifications from one watcher (each watcher has
@@ -328,7 +335,7 @@ TEST_F(DirectoryWatcherTest, MultipleWatchersDifferentFiles) {
TEST_F(DirectoryWatcherTest, WatchCreatedDirectory) {
TestDelegate delegate(this);
DirectoryWatcher watcher;
- ASSERT_TRUE(watcher.Watch(test_dir_, &delegate, true));
+ ASSERT_TRUE(watcher.Watch(test_dir_, &delegate, NULL, true));
SetExpectedNumberOfNotifiedDelegates(1);
FilePath subdir(CreateTestDirDirectoryASCII("SubDir", true));
@@ -347,7 +354,7 @@ TEST_F(DirectoryWatcherTest, RecursiveWatchDeletedSubdirectory) {
TestDelegate delegate(this);
DirectoryWatcher watcher;
- ASSERT_TRUE(watcher.Watch(test_dir_, &delegate, true));
+ ASSERT_TRUE(watcher.Watch(test_dir_, &delegate, NULL, true));
// Write a file to the subdir.
SetExpectedNumberOfNotifiedDelegates(1);
@@ -367,8 +374,8 @@ TEST_F(DirectoryWatcherTest, MoveFileAcrossWatches) {
TestDelegate delegate1(this), delegate2(this);
DirectoryWatcher watcher1, watcher2;
- ASSERT_TRUE(watcher1.Watch(subdir1, &delegate1, true));
- ASSERT_TRUE(watcher2.Watch(subdir2, &delegate2, true));
+ ASSERT_TRUE(watcher1.Watch(subdir1, &delegate1, NULL, true));
+ ASSERT_TRUE(watcher2.Watch(subdir2, &delegate2, NULL, true));
SetExpectedNumberOfNotifiedDelegates(1);
ASSERT_TRUE(WriteTestFile(subdir1.AppendASCII("file"), "some content"));
@@ -397,8 +404,8 @@ TEST_F(DirectoryWatcherTest, MoveFileAcrossWatches) {
// Basic test: add a file and verify we notice it.
TEST_F(DirectoryWatcherTest, NonExistentDirectory) {
DirectoryWatcher watcher;
- ASSERT_FALSE(watcher.Watch(test_dir_.AppendASCII("does-not-exist"), NULL,
- false));
+ ASSERT_FALSE(watcher.Watch(test_dir_.AppendASCII("does-not-exist"),
+ NULL, NULL, false));
}
} // namespace
diff --git a/base/directory_watcher_win.cc b/base/directory_watcher_win.cc
index e75fe57..b6eb9ab 100644
--- a/base/directory_watcher_win.cc
+++ b/base/directory_watcher_win.cc
@@ -18,7 +18,7 @@ class DirectoryWatcherImpl : public DirectoryWatcher::PlatformDelegate,
virtual ~DirectoryWatcherImpl();
virtual bool Watch(const FilePath& path, DirectoryWatcher::Delegate* delegate,
- bool recursive);
+ MessageLoop* backend_loop, bool recursive);
// Callback from MessageLoopForIO.
virtual void OnObjectSignaled(HANDLE object);
@@ -44,7 +44,8 @@ DirectoryWatcherImpl::~DirectoryWatcherImpl() {
}
bool DirectoryWatcherImpl::Watch(const FilePath& path,
- DirectoryWatcher::Delegate* delegate, bool recursive) {
+ DirectoryWatcher::Delegate* delegate,
+ MessageLoop* backend_loop, bool recursive) {
DCHECK(path_.value().empty()); // Can only watch one path.
handle_ = FindFirstChangeNotification(
diff --git a/base/file_util.h b/base/file_util.h
index 3d07229..a3c9a6e 100644
--- a/base/file_util.h
+++ b/base/file_util.h
@@ -15,6 +15,7 @@
#elif defined(OS_POSIX)
#include <fts.h>
#include <sys/stat.h>
+#include <sys/types.h>
#endif
#include <stdio.h>
@@ -342,6 +343,11 @@ bool GetFileInfo(const FilePath& file_path, FileInfo* info);
// Deprecated temporary compatibility function.
bool GetFileInfo(const std::wstring& file_path, FileInfo* info);
+#if defined(OS_POSIX)
+// Store inode number of |path| in |inode|. Return true on success.
+bool GetInode(const FilePath& path, ino_t* inode);
+#endif
+
// Wrapper for fopen-like calls. Returns non-NULL FILE* on success.
FILE* OpenFile(const FilePath& filename, const char* mode);
// Deprecated temporary compatibility functions.
diff --git a/base/file_util_posix.cc b/base/file_util_posix.cc
index ae296ca..96c1c17 100644
--- a/base/file_util_posix.cc
+++ b/base/file_util_posix.cc
@@ -440,6 +440,16 @@ bool GetFileInfo(const FilePath& file_path, FileInfo* results) {
return true;
}
+bool GetInode(const FilePath& path, ino_t* inode) {
+ struct stat buffer;
+ int result = stat(path.value().c_str(), &buffer);
+ if (result < 0)
+ return false;
+
+ *inode = buffer.st_ino;
+ return true;
+}
+
FILE* OpenFile(const std::string& filename, const char* mode) {
return OpenFile(FilePath(filename), mode);
}