summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorphajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-14 17:49:23 +0000
committerphajdan.jr@chromium.org <phajdan.jr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-05-14 17:49:23 +0000
commit825003f15cf15eb6c516dadba39ca45bc90024a0 (patch)
treea25370356ef39c2567bf8c30cd58cb9b80e20932
parente55d77c4230d7a2ddbf8bcd8e2d7f5def54c44d3 (diff)
downloadchromium_src-825003f15cf15eb6c516dadba39ca45bc90024a0.zip
chromium_src-825003f15cf15eb6c516dadba39ca45bc90024a0.tar.gz
chromium_src-825003f15cf15eb6c516dadba39ca45bc90024a0.tar.bz2
Add support for almost-recursive watches in Linux DirectoryWatcher
After this patch DirectoryWatcher when asked for recursive watch will scan the subtree and add inotify watches for each subfolder, but further changes to the tree structure won't trigger adding/removing watches. Support for really recursive watches is planned. This is just to divide the work, because the task is not easy. Based on patch by Janwar Dinata <j.dinata@gmail.com> reviewed at http://codereview.chromium.org/92151 http://crbug.com/8968 Review URL: http://codereview.chromium.org/115229 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@16070 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--AUTHORS1
-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
-rw-r--r--chrome/browser/extensions/user_script_master.cc6
10 files changed, 300 insertions, 118 deletions
diff --git a/AUTHORS b/AUTHORS
index 66028ddc..35bd8b9 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -36,3 +36,4 @@ Edward Crossman <tedoc2000@gmail.com>
Nikita Ofitserov <himikof@gmail.com>
Sean Bryant <sean@cyberwang.net>
Robert Sesek <rsesek@bluestatic.org>
+Janwar Dinata <j.dinata@gmail.com>
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);
}
diff --git a/chrome/browser/extensions/user_script_master.cc b/chrome/browser/extensions/user_script_master.cc
index bcacf671f..8b5e4d9 100644
--- a/chrome/browser/extensions/user_script_master.cc
+++ b/chrome/browser/extensions/user_script_master.cc
@@ -12,6 +12,8 @@
#include "base/pickle.h"
#include "base/stl_util-inl.h"
#include "base/string_util.h"
+#include "base/thread.h"
+#include "chrome/browser/browser_process.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/url_constants.h"
#include "net/base/net_util.h"
@@ -271,7 +273,9 @@ void UserScriptMaster::AddWatchedPath(const FilePath& path) {
// TODO(aa): Enable this when DirectoryWatcher is implemented for linux.
#if defined(OS_WIN) || defined(OS_MACOSX)
DirectoryWatcher* watcher = new DirectoryWatcher();
- watcher->Watch(path, this, true);
+ base::Thread* file_thread = g_browser_process->file_thread();
+ watcher->Watch(path, this, file_thread ? file_thread->message_loop() : NULL,
+ true);
dir_watchers_.push_back(watcher);
#endif
}