summaryrefslogtreecommitdiffstats
path: root/base
diff options
context:
space:
mode:
authortony@chromium.org <tony@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-15 01:57:07 +0000
committertony@chromium.org <tony@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-15 01:57:07 +0000
commitd6f9c9e2adb5c36fe4091f70ecda64e69dc031b4 (patch)
tree9f4e6d8d010a33244e2acfc2f20de26ded8a375e /base
parente0fc2f1f81f13440af3bb4cf5e56a031f91fc163 (diff)
downloadchromium_src-d6f9c9e2adb5c36fe4091f70ecda64e69dc031b4.zip
chromium_src-d6f9c9e2adb5c36fe4091f70ecda64e69dc031b4.tar.gz
chromium_src-d6f9c9e2adb5c36fe4091f70ecda64e69dc031b4.tar.bz2
Move FileWatcher from src/base/ to src/chrome/browser/ and switch
it from using MessageLoop to post tasks to using ChromeThread::PostTask, which is safer. Review URL: http://codereview.chromium.org/864001 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@41560 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base')
-rw-r--r--base/base.gyp1
-rw-r--r--base/base.gypi22
-rw-r--r--base/file_watcher.h65
-rw-r--r--base/file_watcher_inotify.cc323
-rw-r--r--base/file_watcher_mac.cc134
-rw-r--r--base/file_watcher_stub.cc20
-rw-r--r--base/file_watcher_unittest.cc261
-rw-r--r--base/file_watcher_win.cc113
8 files changed, 0 insertions, 939 deletions
diff --git a/base/base.gyp b/base/base.gyp
index 1a40a29..aae38b7 100644
--- a/base/base.gyp
+++ b/base/base.gyp
@@ -70,7 +70,6 @@
'file_path_unittest.cc',
'file_util_unittest.cc',
'file_version_info_unittest.cc',
- 'file_watcher_unittest.cc',
'gmock_unittest.cc',
'histogram_unittest.cc',
'hmac_unittest.cc',
diff --git a/base/base.gypi b/base/base.gypi
index a251641..72ce24d 100644
--- a/base/base.gypi
+++ b/base/base.gypi
@@ -293,7 +293,6 @@
'sources!': [
'atomicops_internals_x86_gcc.cc',
'base_paths_posix.cc',
- 'file_watcher_inotify.cc',
'linux_util.cc',
'message_pump_glib.cc',
],
@@ -316,17 +315,6 @@
'sources/': [ ['exclude', '_openbsd\\.cc$'] ],
},
],
- [ 'GENERATOR == "quentin"', {
- # Quentin builds don't have a recent enough glibc to include the
- # inotify headers
- 'sources!': [
- 'file_watcher_inotify.cc',
- ],
- 'sources': [
- 'file_watcher_stub.cc',
- ],
- },
- ],
[ 'OS == "mac"', {
'sources!': [
# TODO(wtc): Remove nss_util.{cc,h} when http://crbug.com/30689
@@ -421,12 +409,6 @@
],
},],
[ 'OS == "freebsd" or OS == "openbsd"', {
- 'sources!': [
- 'file_watcher_inotify.cc',
- ],
- 'sources': [
- 'file_watcher_stub.cc',
- ],
'link_settings': {
'libraries': [
'-L/usr/local/lib -lexecinfo',
@@ -506,10 +488,6 @@
'base_drop_target.cc',
'base_drop_target.h',
'data_pack.cc',
- 'file_watcher.h',
- 'file_watcher_inotify.cc',
- 'file_watcher_mac.cc',
- 'file_watcher_win.cc',
'dynamic_annotations.h',
'dynamic_annotations.cc',
'event_recorder.cc',
diff --git a/base/file_watcher.h b/base/file_watcher.h
deleted file mode 100644
index 1147d42..0000000
--- a/base/file_watcher.h
+++ /dev/null
@@ -1,65 +0,0 @@
-// 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.
-
-// This module provides a way to monitor a file for changes.
-
-#ifndef BASE_FILE_WATCHER_H_
-#define BASE_FILE_WATCHER_H_
-
-#include "base/basictypes.h"
-#include "base/ref_counted.h"
-
-class FilePath;
-class MessageLoop;
-
-// This class lets you register interest in changes on a file. The delegate
-// will get called whenever the file is changed, including created or deleted.
-// WARNING: To be able to get create/delete notifications and to work cross
-// platform, we actually listen for changes to the directory containing
-// the file.
-// WARNING: On OSX and Windows, the OS API doesn't tell us which file in the
-// directory changed. We work around this by watching the file time, but this
-// can result in some extra notifications if we get other notifications within
-// 2s of the file having changed.
-class FileWatcher {
- public:
- class Delegate {
- public:
- virtual ~Delegate() {}
- virtual void OnFileChanged(const FilePath& path) = 0;
- };
-
- FileWatcher();
- ~FileWatcher() {}
-
- // Register interest in any changes on the file |path|.
- // OnFileChanged will be called back for each change to the file.
- // Any background operations will be ran on |backend_loop|, or inside Watch
- // if |backend_loop| is NULL. Note: The directory containing |path| must
- // exist before you try to watch the file.
- // Returns false on error.
- bool Watch(const FilePath& path, Delegate* delegate,
- MessageLoop* backend_loop) {
- return impl_->Watch(path, delegate, backend_loop);
- }
-
- // Used internally to encapsulate different members on different platforms.
- class PlatformDelegate : public base::RefCounted<PlatformDelegate> {
- public:
- virtual bool Watch(const FilePath& path, Delegate* delegate,
- MessageLoop* backend_loop) = 0;
-
- protected:
- friend class base::RefCounted<PlatformDelegate>;
-
- virtual ~PlatformDelegate() {}
- };
-
- private:
- scoped_refptr<PlatformDelegate> impl_;
-
- DISALLOW_COPY_AND_ASSIGN(FileWatcher);
-};
-
-#endif // BASE_FILE_WATCHER_H_
diff --git a/base/file_watcher_inotify.cc b/base/file_watcher_inotify.cc
deleted file mode 100644
index 9bc4626..0000000
--- a/base/file_watcher_inotify.cc
+++ /dev/null
@@ -1,323 +0,0 @@
-// Copyright (c) 2009 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_watcher.h"
-
-#include <errno.h>
-#include <string.h>
-#include <sys/inotify.h>
-#include <sys/ioctl.h>
-#include <sys/select.h>
-#include <unistd.h>
-
-#include <algorithm>
-#include <set>
-#include <utility>
-#include <vector>
-
-#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"
-#include "base/message_loop.h"
-#include "base/scoped_ptr.h"
-#include "base/singleton.h"
-#include "base/task.h"
-#include "base/thread.h"
-#include "base/waitable_event.h"
-
-namespace {
-
-class FileWatcherImpl;
-
-// 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. |watcher| will be notified on each change.
- // Returns kInvalidWatch on failure.
- Watch AddWatch(const FilePath& path, FileWatcherImpl* watcher);
-
- // Remove |watch|. Returns true on success.
- bool RemoveWatch(Watch watch, FileWatcherImpl* watcher);
-
- // Callback for InotifyReaderTask.
- void OnInotifyEvent(const inotify_event* event);
-
- private:
- friend struct DefaultSingletonTraits<InotifyReader>;
-
- typedef std::set<FileWatcherImpl*> WatcherSet;
-
- InotifyReader();
- ~InotifyReader();
-
- // We keep track of which delegates want to be notified on which watches.
- base::hash_map<Watch, WatcherSet> watchers_;
-
- // Lock to protect watchers_.
- Lock lock_;
-
- // Separate thread on which we run blocking read for inotify events.
- base::Thread thread_;
-
- // File descriptor returned by inotify_init.
- const int inotify_fd_;
-
- // Use self-pipe trick to unblock select during shutdown.
- int shutdown_pipe_[2];
-
- // Flag set to true when startup was successful.
- bool valid_;
-
- DISALLOW_COPY_AND_ASSIGN(InotifyReader);
-};
-
-class FileWatcherImpl : public FileWatcher::PlatformDelegate {
- public:
- FileWatcherImpl();
- ~FileWatcherImpl();
-
- // Called for each event coming from the watch.
- void OnInotifyEvent(const inotify_event* event);
-
- // Start watching |path| for changes and notify |delegate| on each change.
- // Returns true if watch for |path| has been added successfully.
- virtual bool Watch(const FilePath& path, FileWatcher::Delegate* delegate,
- MessageLoop* backend_loop);
-
- private:
- // Delegate to notify upon changes.
- FileWatcher::Delegate* delegate_;
-
- // Watch returned by InotifyReader.
- InotifyReader::Watch watch_;
-
- // The file we're watching.
- FilePath path_;
-
- // Loop where we post file change notifications to.
- MessageLoop* loop_;
-
- DISALLOW_COPY_AND_ASSIGN(FileWatcherImpl);
-};
-
-class FileWatcherImplNotifyTask : public Task {
- public:
- FileWatcherImplNotifyTask(FileWatcher::Delegate* delegate,
- const FilePath& path)
- : delegate_(delegate), path_(path) {
- }
-
- virtual void Run() {
- delegate_->OnFileChanged(path_);
- }
-
- private:
- FileWatcher::Delegate* delegate_;
- FilePath path_;
-
- DISALLOW_COPY_AND_ASSIGN(FileWatcherImplNotifyTask);
-};
-
-class InotifyReaderTask : public Task {
- public:
- InotifyReaderTask(InotifyReader* reader, int inotify_fd, int shutdown_fd)
- : reader_(reader),
- inotify_fd_(inotify_fd),
- shutdown_fd_(shutdown_fd) {
- }
-
- virtual void Run() {
- while (true) {
- fd_set rfds;
- FD_ZERO(&rfds);
- FD_SET(inotify_fd_, &rfds);
- FD_SET(shutdown_fd_, &rfds);
-
- // Wait until some inotify events are available.
- int select_result =
- HANDLE_EINTR(select(std::max(inotify_fd_, shutdown_fd_) + 1,
- &rfds, NULL, NULL, NULL));
- if (select_result < 0) {
- DPLOG(WARNING) << "select failed";
- return;
- }
-
- if (FD_ISSET(shutdown_fd_, &rfds))
- return;
-
- // Adjust buffer size to current event queue size.
- int buffer_size;
- int ioctl_result = HANDLE_EINTR(ioctl(inotify_fd_, FIONREAD,
- &buffer_size));
-
- if (ioctl_result != 0) {
- DPLOG(WARNING) << "ioctl failed";
- return;
- }
-
- std::vector<char> buffer(buffer_size);
-
- ssize_t bytes_read = HANDLE_EINTR(read(inotify_fd_, &buffer[0],
- buffer_size));
-
- if (bytes_read < 0) {
- DPLOG(WARNING) << "read from inotify fd failed";
- return;
- }
-
- ssize_t i = 0;
- while (i < bytes_read) {
- inotify_event* event = reinterpret_cast<inotify_event*>(&buffer[i]);
- size_t event_size = sizeof(inotify_event) + event->len;
- DCHECK(i + event_size <= static_cast<size_t>(bytes_read));
- reader_->OnInotifyEvent(event);
- i += event_size;
- }
- }
- }
-
- private:
- InotifyReader* reader_;
- int inotify_fd_;
- int shutdown_fd_;
-
- DISALLOW_COPY_AND_ASSIGN(InotifyReaderTask);
-};
-
-InotifyReader::InotifyReader()
- : thread_("inotify_reader"),
- inotify_fd_(inotify_init()),
- valid_(false) {
- shutdown_pipe_[0] = -1;
- shutdown_pipe_[1] = -1;
- if (inotify_fd_ >= 0 && pipe(shutdown_pipe_) == 0 && thread_.Start()) {
- thread_.message_loop()->PostTask(
- FROM_HERE, new InotifyReaderTask(this, inotify_fd_, shutdown_pipe_[0]));
- valid_ = true;
- }
-}
-
-InotifyReader::~InotifyReader() {
- if (valid_) {
- // Write to the self-pipe so that the select call in InotifyReaderTask
- // returns.
- ssize_t ret = HANDLE_EINTR(write(shutdown_pipe_[1], "", 1));
- DPCHECK(ret > 0);
- DCHECK_EQ(ret, 1);
- thread_.Stop();
- }
- if (inotify_fd_ >= 0)
- close(inotify_fd_);
- if (shutdown_pipe_[0] >= 0)
- close(shutdown_pipe_[0]);
- if (shutdown_pipe_[1] >= 0)
- close(shutdown_pipe_[1]);
-}
-
-InotifyReader::Watch InotifyReader::AddWatch(
- const FilePath& path, FileWatcherImpl* watcher) {
- if (!valid_)
- return kInvalidWatch;
-
- AutoLock auto_lock(lock_);
-
- 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;
-
- watchers_[watch].insert(watcher);
-
- return watch;
-}
-
-bool InotifyReader::RemoveWatch(Watch watch,
- FileWatcherImpl* watcher) {
- if (!valid_)
- return false;
-
- AutoLock auto_lock(lock_);
-
- watchers_[watch].erase(watcher);
-
- if (watchers_[watch].empty()) {
- watchers_.erase(watch);
- return (inotify_rm_watch(inotify_fd_, watch) == 0);
- }
-
- return true;
-}
-
-void InotifyReader::OnInotifyEvent(const inotify_event* event) {
- if (event->mask & IN_IGNORED)
- return;
-
- // In case you want to limit the scope of this lock, it's not sufficient
- // to just copy things under the lock, and then run the notifications
- // without holding the lock. FileWatcherImpl's dtor removes its watches,
- // and to do that obtains the lock. After it finishes removing watches,
- // it's destroyed. So, if you copy under the lock and notify without the lock,
- // it's possible you'll copy the FileWatcherImpl which is being
- // destroyed, then it will destroy itself, and then you'll try to notify it.
- AutoLock auto_lock(lock_);
-
- for (WatcherSet::iterator watcher = watchers_[event->wd].begin();
- watcher != watchers_[event->wd].end();
- ++watcher) {
- (*watcher)->OnInotifyEvent(event);
- }
-}
-
-FileWatcherImpl::FileWatcherImpl()
- : watch_(InotifyReader::kInvalidWatch) {
-}
-
-FileWatcherImpl::~FileWatcherImpl() {
- if (watch_ == InotifyReader::kInvalidWatch)
- return;
-
- Singleton<InotifyReader>::get()->RemoveWatch(watch_, this);
-}
-
-void FileWatcherImpl::OnInotifyEvent(const inotify_event* event) {
- // Since we're watching the directory, filter out inotify events
- // if it's not related to the file we're watching.
- if (path_ != path_.DirName().Append(event->name))
- return;
-
- loop_->PostTask(FROM_HERE,
- new FileWatcherImplNotifyTask(delegate_, path_));
-}
-
-bool FileWatcherImpl::Watch(const FilePath& path,
- FileWatcher::Delegate* delegate,
- MessageLoop* backend_loop) {
- // Each FileWatcherImpl can only watch one file.
- DCHECK(watch_ == InotifyReader::kInvalidWatch);
-
- // It's not possible to watch a file that doesn't exist, so instead,
- // watch the parent directory.
- if (!file_util::PathExists(path.DirName()))
- return false;
-
- delegate_ = delegate;
- path_ = path;
- loop_ = MessageLoop::current();
- watch_ = Singleton<InotifyReader>::get()->AddWatch(path.DirName(), this);
- return watch_ != InotifyReader::kInvalidWatch;
-}
-
-} // namespace
-
-FileWatcher::FileWatcher() {
- impl_ = new FileWatcherImpl();
-}
diff --git a/base/file_watcher_mac.cc b/base/file_watcher_mac.cc
deleted file mode 100644
index 2d85133..0000000
--- a/base/file_watcher_mac.cc
+++ /dev/null
@@ -1,134 +0,0 @@
-// Copyright (c) 2009 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_watcher.h"
-
-#include <CoreServices/CoreServices.h>
-
-#include "base/file_path.h"
-#include "base/file_util.h"
-#include "base/logging.h"
-#include "base/message_loop.h"
-#include "base/scoped_cftyperef.h"
-#include "base/time.h"
-
-namespace {
-
-const CFAbsoluteTime kEventLatencySeconds = 0.3;
-
-class FileWatcherImpl : public FileWatcher::PlatformDelegate {
- public:
- FileWatcherImpl() {}
- ~FileWatcherImpl() {
- if (!path_.value().empty()) {
- FSEventStreamStop(fsevent_stream_);
- FSEventStreamInvalidate(fsevent_stream_);
- FSEventStreamRelease(fsevent_stream_);
- }
- }
-
- virtual bool Watch(const FilePath& path, FileWatcher::Delegate* delegate,
- MessageLoop* backend_loop);
-
- void OnFSEventsCallback(const FilePath& event_path) {
- DCHECK(!path_.value().empty());
- FilePath absolute_event_path = event_path;
- if (!file_util::AbsolutePath(&absolute_event_path))
- return;
-
- file_util::FileInfo file_info;
- bool file_exists = file_util::GetFileInfo(path_, &file_info);
- if (file_exists && (last_modified_.is_null() ||
- last_modified_ != file_info.last_modified)) {
- last_modified_ = file_info.last_modified;
- delegate_->OnFileChanged(path_);
- } else if (file_exists && (base::Time::Now() - last_modified_ <
- base::TimeDelta::FromSeconds(2))) {
- // Since we only have a resolution of 1s, if we get a callback within
- // 2s of the file having changed, go ahead and notify our observer. This
- // might be from a different file change, but it's better to notify too
- // much rather than miss a notification.
- delegate_->OnFileChanged(path_);
- } else if (!file_exists && !last_modified_.is_null()) {
- last_modified_ = base::Time();
- delegate_->OnFileChanged(path_);
- }
- }
-
- private:
- // Delegate to notify upon changes.
- FileWatcher::Delegate* delegate_;
-
- // Path we're watching (passed to delegate).
- FilePath path_;
-
- // Backend stream we receive event callbacks from (strong reference).
- FSEventStreamRef fsevent_stream_;
-
- // Keep track of the last modified time of the file. We use nulltime
- // to represent the file not existing.
- base::Time last_modified_;
-
- DISALLOW_COPY_AND_ASSIGN(FileWatcherImpl);
-};
-
-void FSEventsCallback(ConstFSEventStreamRef stream,
- void* event_watcher, size_t num_events,
- void* event_paths, const FSEventStreamEventFlags flags[],
- const FSEventStreamEventId event_ids[]) {
- char** paths = reinterpret_cast<char**>(event_paths);
- FileWatcherImpl* watcher =
- reinterpret_cast<FileWatcherImpl*>(event_watcher);
- for (size_t i = 0; i < num_events; i++)
- watcher->OnFSEventsCallback(FilePath(paths[i]));
-}
-
-bool FileWatcherImpl::Watch(const FilePath& path,
- FileWatcher::Delegate* delegate,
- MessageLoop* backend_loop) {
- DCHECK(path_.value().empty()); // Can only watch one path.
- DCHECK(MessageLoop::current()->type() == MessageLoop::TYPE_UI);
-
- FilePath parent_dir = path.DirName();
- if (!file_util::AbsolutePath(&parent_dir))
- return false;
-
- file_util::FileInfo file_info;
- if (file_util::GetFileInfo(path, &file_info))
- last_modified_ = file_info.last_modified;
-
- path_ = path;
- delegate_ = delegate;
-
- scoped_cftyperef<CFStringRef> cf_path(CFStringCreateWithCString(
- NULL, path.DirName().value().c_str(), kCFStringEncodingMacHFS));
- CFStringRef path_for_array = cf_path.get();
- scoped_cftyperef<CFArrayRef> watched_paths(CFArrayCreate(
- NULL, reinterpret_cast<const void**>(&path_for_array), 1,
- &kCFTypeArrayCallBacks));
-
- FSEventStreamContext context;
- context.version = 0;
- context.info = this;
- context.retain = NULL;
- context.release = NULL;
- context.copyDescription = NULL;
-
- fsevent_stream_ = FSEventStreamCreate(NULL, &FSEventsCallback, &context,
- watched_paths,
- kFSEventStreamEventIdSinceNow,
- kEventLatencySeconds,
- kFSEventStreamCreateFlagNone);
- FSEventStreamScheduleWithRunLoop(fsevent_stream_, CFRunLoopGetCurrent(),
- kCFRunLoopDefaultMode);
- FSEventStreamStart(fsevent_stream_);
-
- return true;
-}
-
-} // namespace
-
-FileWatcher::FileWatcher() {
- impl_ = new FileWatcherImpl();
-}
diff --git a/base/file_watcher_stub.cc b/base/file_watcher_stub.cc
deleted file mode 100644
index 99484fc..0000000
--- a/base/file_watcher_stub.cc
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright (c) 2009 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.
-
-// This file exists for Linux systems which don't have the inotify headers, and
-// thus cannot build file_watcher_inotify.cc
-
-#include "base/file_watcher.h"
-
-class FileWatcherImpl : public FileWatcher::PlatformDelegate {
- public:
- virtual bool Watch(const FilePath& path, FileWatcher::Delegate* delegate,
- MessageLoop* backend_loop) {
- return false;
- }
-};
-
-FileWatcher::FileWatcher() {
- impl_ = new FileWatcherImpl();
-}
diff --git a/base/file_watcher_unittest.cc b/base/file_watcher_unittest.cc
deleted file mode 100644
index 014488e..0000000
--- a/base/file_watcher_unittest.cc
+++ /dev/null
@@ -1,261 +0,0 @@
-// Copyright (c) 2008 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_watcher.h"
-
-#include <limits>
-
-#include "base/basictypes.h"
-#include "base/file_path.h"
-#include "base/file_util.h"
-#include "base/message_loop.h"
-#include "base/path_service.h"
-#include "base/platform_thread.h"
-#include "base/scoped_temp_dir.h"
-#include "base/string_util.h"
-#include "base/thread.h"
-#if defined(OS_WIN)
-#include "base/win_util.h"
-#endif // defined(OS_WIN)
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace {
-
-// For tests where we wait a bit to verify nothing happened
-const int kWaitForEventTime = 500;
-
-class FileWatcherTest : public testing::Test {
- public:
- // Implementation of FileWatcher on Mac requires UI loop.
- FileWatcherTest()
- : loop_(MessageLoop::TYPE_UI),
- notified_delegates_(0),
- expected_notified_delegates_(0) {
- }
-
- void OnTestDelegateFirstNotification() {
- notified_delegates_++;
- if (notified_delegates_ >= expected_notified_delegates_)
- MessageLoop::current()->Quit();
- }
-
- protected:
- virtual void SetUp() {
- temp_dir_.reset(new ScopedTempDir);
- ASSERT_TRUE(temp_dir_->CreateUniqueTempDir());
- }
-
- FilePath test_file() {
- return temp_dir_->path().AppendASCII("FileWatcherTest");
- }
-
- virtual void TearDown() {
- // Make sure there are no tasks in the loop.
- loop_.RunAllPending();
- }
-
- // Write |content| to the test file. Returns true on success.
- bool WriteTestFile(const std::string& content) {
- int write_size = file_util::WriteFile(test_file(), content.c_str(),
- content.length());
- return write_size == static_cast<int>(content.length());
- }
-
- void SetExpectedNumberOfNotifiedDelegates(int n) {
- notified_delegates_ = 0;
- expected_notified_delegates_ = n;
- }
-
- void VerifyExpectedNumberOfNotifiedDelegates() {
- // Check that we get at least the expected number of notified delegates.
- if (expected_notified_delegates_ - notified_delegates_ > 0)
- loop_.Run();
- EXPECT_EQ(expected_notified_delegates_, notified_delegates_);
- }
-
- void VerifyNoExtraNotifications() {
- // Check that we get no more than the expected number of notified delegates.
- loop_.PostDelayedTask(FROM_HERE, new MessageLoop::QuitTask,
- kWaitForEventTime);
- loop_.Run();
- EXPECT_EQ(expected_notified_delegates_, notified_delegates_);
- }
-
- // We need this function for reliable tests on Mac OS X. FSEvents API
- // has a latency interval and can merge multiple events into one,
- // and we need a clear distinction between events triggered by test setup code
- // and test code.
- void SyncIfPOSIX() {
-#if defined(OS_POSIX)
- sync();
-#endif // defined(OS_POSIX)
- }
-
- MessageLoop loop_;
- scoped_ptr<ScopedTempDir> temp_dir_;
-
- // The number of test delegates which received their notification.
- int notified_delegates_;
-
- // The number of notified test delegates after which we quit the message loop.
- int expected_notified_delegates_;
-};
-
-class TestDelegate : public FileWatcher::Delegate {
- public:
- explicit TestDelegate(FileWatcherTest* test)
- : test_(test),
- got_notification_(false),
- original_thread_id_(PlatformThread::CurrentId()) {
- }
-
- bool got_notification() const {
- return got_notification_;
- }
-
- void reset() {
- got_notification_ = false;
- }
-
- virtual void OnFileChanged(const FilePath& path) {
- EXPECT_EQ(original_thread_id_, PlatformThread::CurrentId());
- if (!got_notification_)
- test_->OnTestDelegateFirstNotification();
- got_notification_ = true;
- }
-
- private:
- // Hold a pointer to current test fixture to inform it on first notification.
- FileWatcherTest* test_;
-
- // Set to true after first notification.
- bool got_notification_;
-
- // Keep track of original thread id to verify that callbacks are called
- // on the same thread.
- PlatformThreadId original_thread_id_;
-};
-
-// Basic test: Create the file and verify that we notice.
-TEST_F(FileWatcherTest, NewFile) {
- FileWatcher watcher;
- TestDelegate delegate(this);
- ASSERT_TRUE(watcher.Watch(test_file(), &delegate, NULL));
-
- SetExpectedNumberOfNotifiedDelegates(1);
- ASSERT_TRUE(WriteTestFile("content"));
- VerifyExpectedNumberOfNotifiedDelegates();
-}
-
-// Verify that modifying the file is caught.
-TEST_F(FileWatcherTest, ModifiedFile) {
- ASSERT_TRUE(WriteTestFile("content"));
- SyncIfPOSIX();
-
- FileWatcher watcher;
- TestDelegate delegate(this);
- ASSERT_TRUE(watcher.Watch(test_file(), &delegate, NULL));
-
- // Now make sure we get notified if the file is modified.
- SetExpectedNumberOfNotifiedDelegates(1);
- ASSERT_TRUE(WriteTestFile("new content"));
- VerifyExpectedNumberOfNotifiedDelegates();
-}
-
-TEST_F(FileWatcherTest, DeletedFile) {
- ASSERT_TRUE(WriteTestFile("content"));
- SyncIfPOSIX();
-
- FileWatcher watcher;
- TestDelegate delegate(this);
- ASSERT_TRUE(watcher.Watch(test_file(), &delegate, NULL));
-
- // Now make sure we get notified if the file is deleted.
- SetExpectedNumberOfNotifiedDelegates(1);
- file_util::Delete(test_file(), false);
- VerifyExpectedNumberOfNotifiedDelegates();
-}
-
-// Verify that letting the watcher go out of scope stops notifications.
-TEST_F(FileWatcherTest, Unregister) {
- TestDelegate delegate(this);
-
- {
- FileWatcher watcher;
- ASSERT_TRUE(watcher.Watch(test_file(), &delegate, NULL));
-
- // And then let it fall out of scope, clearing its watch.
- }
-
- // Write a file to the test dir.
- SetExpectedNumberOfNotifiedDelegates(0);
- ASSERT_TRUE(WriteTestFile("content"));
- VerifyExpectedNumberOfNotifiedDelegates();
- VerifyNoExtraNotifications();
-}
-
-
-namespace {
-// Used by the DeleteDuringNotify test below.
-// Deletes the FileWatcher when it's notified.
-class Deleter : public FileWatcher::Delegate {
- public:
- Deleter(FileWatcher* watcher, MessageLoop* loop)
- : watcher_(watcher),
- loop_(loop) {
- }
-
- virtual void OnFileChanged(const FilePath& path) {
- watcher_.reset(NULL);
- loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask());
- }
-
- scoped_ptr<FileWatcher> watcher_;
- MessageLoop* loop_;
-};
-} // anonymous namespace
-
-// Verify that deleting a watcher during the callback doesn't crash.
-TEST_F(FileWatcherTest, DeleteDuringNotify) {
- FileWatcher* watcher = new FileWatcher;
- Deleter deleter(watcher, &loop_); // Takes ownership of watcher.
- ASSERT_TRUE(watcher->Watch(test_file(), &deleter, NULL));
-
- ASSERT_TRUE(WriteTestFile("content"));
- loop_.Run();
-
- // We win if we haven't crashed yet.
- // Might as well double-check it got deleted, too.
- ASSERT_TRUE(deleter.watcher_.get() == NULL);
-}
-
-TEST_F(FileWatcherTest, BackendLoop) {
- base::Thread thread("test");
- ASSERT_TRUE(thread.Start());
-
- FileWatcher watcher;
- TestDelegate delegate(this);
- ASSERT_TRUE(watcher.Watch(test_file(), &delegate, thread.message_loop()));
-}
-
-TEST_F(FileWatcherTest, MultipleWatchersSingleFile) {
- FileWatcher watcher1, watcher2;
- TestDelegate delegate1(this), delegate2(this);
- ASSERT_TRUE(watcher1.Watch(test_file(), &delegate1, NULL));
- ASSERT_TRUE(watcher2.Watch(test_file(), &delegate2, NULL));
-
- SetExpectedNumberOfNotifiedDelegates(2);
- ASSERT_TRUE(WriteTestFile("content"));
- VerifyExpectedNumberOfNotifiedDelegates();
-}
-
-// Verify that watching a file who's parent directory doesn't exist
-// fails, but doesn't asssert.
-TEST_F(FileWatcherTest, NonExistentDirectory) {
- FileWatcher watcher;
- ASSERT_FALSE(watcher.Watch(test_file().AppendASCII("FileToWatch"),
- NULL, NULL));
-}
-
-} // namespace
diff --git a/base/file_watcher_win.cc b/base/file_watcher_win.cc
deleted file mode 100644
index ac04757..0000000
--- a/base/file_watcher_win.cc
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright (c) 2009 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_watcher.h"
-
-#include "base/file_path.h"
-#include "base/file_util.h"
-#include "base/logging.h"
-#include "base/object_watcher.h"
-#include "base/ref_counted.h"
-#include "base/time.h"
-
-namespace {
-
-class FileWatcherImpl : public FileWatcher::PlatformDelegate,
- public base::ObjectWatcher::Delegate {
- public:
- FileWatcherImpl() : delegate_(NULL), handle_(INVALID_HANDLE_VALUE) {}
-
- virtual bool Watch(const FilePath& path, FileWatcher::Delegate* delegate,
- MessageLoop* backend_loop);
-
- // Callback from MessageLoopForIO.
- virtual void OnObjectSignaled(HANDLE object);
-
- private:
- virtual ~FileWatcherImpl();
-
- // Delegate to notify upon changes.
- FileWatcher::Delegate* delegate_;
-
- // Path we're watching (passed to delegate).
- FilePath path_;
-
- // Handle for FindFirstChangeNotification.
- HANDLE handle_;
-
- // ObjectWatcher to watch handle_ for events.
- base::ObjectWatcher watcher_;
-
- // Keep track of the last modified time of the file. We use nulltime
- // to represent the file not existing.
- base::Time last_modified_;
-
- DISALLOW_COPY_AND_ASSIGN(FileWatcherImpl);
-};
-
-FileWatcherImpl::~FileWatcherImpl() {
- if (handle_ != INVALID_HANDLE_VALUE) {
- watcher_.StopWatching();
- FindCloseChangeNotification(handle_);
- }
-}
-
-bool FileWatcherImpl::Watch(const FilePath& path,
- FileWatcher::Delegate* delegate,
- MessageLoop* backend_loop) {
- DCHECK(path_.value().empty()); // Can only watch one path.
- file_util::FileInfo file_info;
- if (file_util::GetFileInfo(path, &file_info))
- last_modified_ = file_info.last_modified;
-
- // FindFirstChangeNotification watches directories, so use the parent path.
- handle_ = FindFirstChangeNotification(
- path.DirName().value().c_str(),
- false, // Don't watch subtrees
- FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_SIZE |
- FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_DIR_NAME);
- if (handle_ == INVALID_HANDLE_VALUE)
- return false;
-
- delegate_ = delegate;
- path_ = path;
- watcher_.StartWatching(handle_, this);
-
- return true;
-}
-
-void FileWatcherImpl::OnObjectSignaled(HANDLE object) {
- DCHECK(object == handle_);
- // Make sure we stay alive through the body of this function.
- scoped_refptr<FileWatcherImpl> keep_alive(this);
-
- file_util::FileInfo file_info;
- bool file_exists = file_util::GetFileInfo(path_, &file_info);
- if (file_exists && (last_modified_.is_null() ||
- last_modified_ != file_info.last_modified)) {
- last_modified_ = file_info.last_modified;
- delegate_->OnFileChanged(path_);
- } else if (file_exists && (base::Time::Now() - last_modified_ <
- base::TimeDelta::FromSeconds(2))) {
- // Since we only have a resolution of 1s, if we get a callback within
- // 2s of the file having changed, go ahead and notify our observer. This
- // might be from a different file change, but it's better to notify too
- // much rather than miss a notification.
- delegate_->OnFileChanged(path_);
- } else if (!file_exists && !last_modified_.is_null()) {
- last_modified_ = base::Time();
- delegate_->OnFileChanged(path_);
- }
-
- // Register for more notifications on file change.
- BOOL ok = FindNextChangeNotification(object);
- DCHECK(ok);
- watcher_.StartWatching(object, this);
-}
-
-} // namespace
-
-FileWatcher::FileWatcher() {
- impl_ = new FileWatcherImpl();
-}