summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--base/base.gyp11
-rw-r--r--base/mac/scoped_cffiledescriptorref.h75
-rw-r--r--base/message_loop.cc22
-rw-r--r--base/message_loop.h28
-rw-r--r--base/message_pump_io_ios.cc209
-rw-r--r--base/message_pump_io_ios.h141
-rw-r--r--base/message_pump_io_ios_unittest.cc188
-rw-r--r--base/message_pump_libevent.cc10
-rw-r--r--base/message_pump_libevent.h6
-rw-r--r--base/message_pump_libevent_unittest.cc8
10 files changed, 680 insertions, 18 deletions
diff --git a/base/base.gyp b/base/base.gyp
index 4102004..1ba9e43 100644
--- a/base/base.gyp
+++ b/base/base.gyp
@@ -194,7 +194,7 @@
],
},
}],
- ['OS != "win"', {
+ ['OS != "win" and OS != "ios"', {
'dependencies': ['../third_party/libevent/libevent.gyp:libevent'],
},],
['component=="shared_library"', {
@@ -227,6 +227,8 @@
'message_pump_glib.h',
'message_pump_gtk.cc',
'message_pump_gtk.h',
+ 'message_pump_io_ios.cc',
+ 'message_pump_io_ios.h',
'message_pump_observer.h',
'message_pump_aurax11.cc',
'message_pump_aurax11.h',
@@ -481,6 +483,7 @@
'message_loop_proxy_unittest.cc',
'message_loop_unittest.cc',
'message_pump_glib_unittest.cc',
+ 'message_pump_io_ios_unittest.cc',
'message_pump_libevent_unittest.cc',
'metrics/sample_map_unittest.cc',
'metrics/sample_vector_unittest.cc',
@@ -613,9 +616,11 @@
['include', '^process_util_unittest_ios\\.cc$'],
# Requires spawning processes.
['exclude', '^metrics/stats_table_unittest\\.cc$'],
+ # iOS does not use message_pump_libevent.
+ ['exclude', '^message_pump_libevent_unittest\\.cc$'],
# TODO(ios): Remove these as base/ is unforked.
- # For now, exclude everything that doesn't build as-is, just to
- # get a minimal target building.
+ # For now, exclude everything that doesn't build as-is, just to get
+ # a minimal target building.
# Unittests that don't pass.
['exclude', '^message_loop_unittest\\.cc$'],
],
diff --git a/base/mac/scoped_cffiledescriptorref.h b/base/mac/scoped_cffiledescriptorref.h
new file mode 100644
index 0000000..07196aa
--- /dev/null
+++ b/base/mac/scoped_cffiledescriptorref.h
@@ -0,0 +1,75 @@
+// Copyright 2012 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.
+
+#ifndef BASE_MAC_SCOPED_CFFILEDESCRIPTORREF_H_
+#define BASE_MAC_SCOPED_CFFILEDESCRIPTORREF_H_
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+
+namespace base {
+namespace mac {
+
+// ScopedCFFileDescriptorRef is designed after ScopedCFTypeRef<>. On
+// destruction, it will invalidate the file descriptor.
+// ScopedCFFileDescriptorRef (unlike ScopedCFTypeRef<>) does not support RETAIN
+// semantics, copying, or assignment, as doing so would increase the chances
+// that a file descriptor is invalidated while still in use.
+class ScopedCFFileDescriptorRef {
+ public:
+ explicit ScopedCFFileDescriptorRef(CFFileDescriptorRef fdref = NULL)
+ : fdref_(fdref) {
+ }
+
+ ~ScopedCFFileDescriptorRef() {
+ if (fdref_) {
+ CFFileDescriptorInvalidate(fdref_);
+ CFRelease(fdref_);
+ }
+ }
+
+ void reset(CFFileDescriptorRef fdref = NULL) {
+ if (fdref_ == fdref)
+ return;
+ if (fdref_) {
+ CFFileDescriptorInvalidate(fdref_);
+ CFRelease(fdref_);
+ }
+ fdref_ = fdref;
+ }
+
+ bool operator==(CFFileDescriptorRef that) const {
+ return fdref_ == that;
+ }
+
+ bool operator!=(CFFileDescriptorRef that) const {
+ return fdref_ != that;
+ }
+
+ operator CFFileDescriptorRef() const {
+ return fdref_;
+ }
+
+ CFFileDescriptorRef get() const {
+ return fdref_;
+ }
+
+ CFFileDescriptorRef release() WARN_UNUSED_RESULT {
+ CFFileDescriptorRef temp = fdref_;
+ fdref_ = NULL;
+ return temp;
+ }
+
+ private:
+ CFFileDescriptorRef fdref_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedCFFileDescriptorRef);
+};
+
+} // namespace mac
+} // namespace base
+
+#endif // BASE_MAC_SCOPED_CFFILEDESCRIPTORREF_H_
diff --git a/base/message_loop.cc b/base/message_loop.cc
index f427993..5428026 100644
--- a/base/message_loop.cc
+++ b/base/message_loop.cc
@@ -27,7 +27,7 @@
#if defined(OS_MACOSX)
#include "base/message_pump_mac.h"
#endif
-#if defined(OS_POSIX)
+#if defined(OS_POSIX) && !defined(OS_IOS)
#include "base/message_pump_libevent.h"
#endif
#if defined(OS_ANDROID)
@@ -157,6 +157,9 @@ MessageLoop::MessageLoop(Type type)
#if defined(OS_WIN)
#define MESSAGE_PUMP_UI new base::MessagePumpForUI()
#define MESSAGE_PUMP_IO new base::MessagePumpForIO()
+#elif defined(OS_IOS)
+#define MESSAGE_PUMP_UI base::MessagePumpMac::Create()
+#define MESSAGE_PUMP_IO new base::MessagePumpIOSForIO()
#elif defined(OS_MACOSX)
#define MESSAGE_PUMP_UI base::MessagePumpMac::Create()
#define MESSAGE_PUMP_IO new base::MessagePumpLibevent()
@@ -776,6 +779,21 @@ bool MessageLoopForIO::WaitForIOCompletion(DWORD timeout, IOHandler* filter) {
return pump_io()->WaitForIOCompletion(timeout, filter);
}
+#elif defined(OS_IOS)
+
+bool MessageLoopForIO::WatchFileDescriptor(int fd,
+ bool persistent,
+ Mode mode,
+ FileDescriptorWatcher *controller,
+ Watcher *delegate) {
+ return pump_io()->WatchFileDescriptor(
+ fd,
+ persistent,
+ mode,
+ controller,
+ delegate);
+}
+
#elif defined(OS_POSIX) && !defined(OS_NACL)
bool MessageLoopForIO::WatchFileDescriptor(int fd,
@@ -786,7 +804,7 @@ bool MessageLoopForIO::WatchFileDescriptor(int fd,
return pump_libevent()->WatchFileDescriptor(
fd,
persistent,
- static_cast<base::MessagePumpLibevent::Mode>(mode),
+ mode,
controller,
delegate);
}
diff --git a/base/message_loop.h b/base/message_loop.h
index 74dfe65..5ec57bc 100644
--- a/base/message_loop.h
+++ b/base/message_loop.h
@@ -26,6 +26,8 @@
// We need this to declare base::MessagePumpWin::Dispatcher, which we should
// really just eliminate.
#include "base/message_pump_win.h"
+#elif defined(OS_IOS)
+#include "base/message_pump_io_ios.h"
#elif defined(OS_POSIX)
#include "base/message_pump_libevent.h"
#if !defined(OS_MACOSX) && !defined(OS_ANDROID)
@@ -388,7 +390,7 @@ class BASE_EXPORT MessageLoop : public base::MessagePump::Delegate {
base::MessagePumpWin* pump_win() {
return static_cast<base::MessagePumpWin*>(pump_.get());
}
-#elif defined(OS_POSIX)
+#elif defined(OS_POSIX) && !defined(OS_IOS)
base::MessagePumpLibevent* pump_libevent() {
return static_cast<base::MessagePumpLibevent*>(pump_.get());
}
@@ -613,6 +615,17 @@ class BASE_EXPORT MessageLoopForIO : public MessageLoop {
typedef base::MessagePumpForIO::IOHandler IOHandler;
typedef base::MessagePumpForIO::IOContext IOContext;
typedef base::MessagePumpForIO::IOObserver IOObserver;
+#elif defined(OS_IOS)
+ typedef base::MessagePumpIOSForIO::Watcher Watcher;
+ typedef base::MessagePumpIOSForIO::FileDescriptorWatcher
+ FileDescriptorWatcher;
+ typedef base::MessagePumpIOSForIO::IOObserver IOObserver;
+
+ enum Mode {
+ WATCH_READ = base::MessagePumpIOSForIO::WATCH_READ,
+ WATCH_WRITE = base::MessagePumpIOSForIO::WATCH_WRITE,
+ WATCH_READ_WRITE = base::MessagePumpIOSForIO::WATCH_READ_WRITE
+ };
#elif defined(OS_POSIX)
typedef base::MessagePumpLibevent::Watcher Watcher;
typedef base::MessagePumpLibevent::FileDescriptorWatcher
@@ -657,6 +670,19 @@ class BASE_EXPORT MessageLoopForIO : public MessageLoop {
return static_cast<base::MessagePumpForIO*>(pump_.get());
}
+#elif defined(OS_IOS)
+ // Please see MessagePumpIOSForIO for definition.
+ bool WatchFileDescriptor(int fd,
+ bool persistent,
+ Mode mode,
+ FileDescriptorWatcher *controller,
+ Watcher *delegate);
+
+ private:
+ base::MessagePumpIOSForIO* pump_io() {
+ return static_cast<base::MessagePumpIOSForIO*>(pump_.get());
+ }
+
#elif defined(OS_POSIX)
// Please see MessagePumpLibevent for definition.
bool WatchFileDescriptor(int fd,
diff --git a/base/message_pump_io_ios.cc b/base/message_pump_io_ios.cc
new file mode 100644
index 0000000..ca800ff
--- /dev/null
+++ b/base/message_pump_io_ios.cc
@@ -0,0 +1,209 @@
+// Copyright 2012 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/message_pump_io_ios.h"
+
+namespace base {
+
+MessagePumpIOSForIO::FileDescriptorWatcher::FileDescriptorWatcher()
+ : is_persistent_(false),
+ fdref_(NULL),
+ callback_types_(0),
+ fd_source_(NULL),
+ pump_(NULL),
+ watcher_(NULL) {
+}
+
+MessagePumpIOSForIO::FileDescriptorWatcher::~FileDescriptorWatcher() {
+ StopWatchingFileDescriptor();
+}
+
+bool MessagePumpIOSForIO::FileDescriptorWatcher::StopWatchingFileDescriptor() {
+ if (fdref_ == NULL)
+ return true;
+
+ CFFileDescriptorDisableCallBacks(fdref_, callback_types_);
+ pump_->RemoveRunLoopSource(fd_source_);
+ fd_source_.reset();
+ fdref_.reset();
+ callback_types_ = 0;
+ pump_ = NULL;
+ watcher_ = NULL;
+ return true;
+}
+
+void MessagePumpIOSForIO::FileDescriptorWatcher::Init(
+ CFFileDescriptorRef fdref,
+ CFOptionFlags callback_types,
+ CFRunLoopSourceRef fd_source,
+ bool is_persistent) {
+ DCHECK(fdref);
+ DCHECK(!fdref_);
+
+ is_persistent_ = is_persistent;
+ fdref_.reset(fdref);
+ callback_types_ = callback_types;
+ fd_source_.reset(fd_source);
+}
+
+void MessagePumpIOSForIO::FileDescriptorWatcher::OnFileCanReadWithoutBlocking(
+ int fd,
+ MessagePumpIOSForIO* pump) {
+ DCHECK(callback_types_ & kCFFileDescriptorReadCallBack);
+ pump->WillProcessIOEvent();
+ watcher_->OnFileCanReadWithoutBlocking(fd);
+ pump->DidProcessIOEvent();
+}
+
+void MessagePumpIOSForIO::FileDescriptorWatcher::OnFileCanWriteWithoutBlocking(
+ int fd,
+ MessagePumpIOSForIO* pump) {
+ DCHECK(callback_types_ & kCFFileDescriptorWriteCallBack);
+ pump->WillProcessIOEvent();
+ watcher_->OnFileCanWriteWithoutBlocking(fd);
+ pump->DidProcessIOEvent();
+}
+
+MessagePumpIOSForIO::MessagePumpIOSForIO() {
+}
+
+MessagePumpIOSForIO::~MessagePumpIOSForIO() {
+}
+
+bool MessagePumpIOSForIO::WatchFileDescriptor(
+ int fd,
+ bool persistent,
+ int mode,
+ FileDescriptorWatcher *controller,
+ Watcher *delegate) {
+ DCHECK_GE(fd, 0);
+ DCHECK(controller);
+ DCHECK(delegate);
+ DCHECK(mode == WATCH_READ || mode == WATCH_WRITE || mode == WATCH_READ_WRITE);
+
+ // WatchFileDescriptor should be called on the pump thread. It is not
+ // threadsafe, and your watcher may never be registered.
+ DCHECK(watch_file_descriptor_caller_checker_.CalledOnValidThread());
+
+ CFFileDescriptorContext source_context = {0};
+ source_context.info = controller;
+
+ CFOptionFlags callback_types = 0;
+ if (mode & WATCH_READ) {
+ callback_types |= kCFFileDescriptorReadCallBack;
+ }
+ if (mode & WATCH_WRITE) {
+ callback_types |= kCFFileDescriptorWriteCallBack;
+ }
+
+ CFFileDescriptorRef fdref = controller->fdref_;
+ if (fdref == NULL) {
+ base::mac::ScopedCFTypeRef<CFFileDescriptorRef> scoped_fdref(
+ CFFileDescriptorCreate(kCFAllocatorDefault, fd, false, HandleFdIOEvent,
+ &source_context));
+ if (scoped_fdref == NULL) {
+ NOTREACHED() << "CFFileDescriptorCreate failed";
+ return false;
+ }
+
+ CFFileDescriptorEnableCallBacks(scoped_fdref, callback_types);
+
+ // TODO(wtc): what should the 'order' argument be?
+ base::mac::ScopedCFTypeRef<CFRunLoopSourceRef> scoped_fd_source(
+ CFFileDescriptorCreateRunLoopSource(kCFAllocatorDefault,
+ scoped_fdref,
+ 0));
+ if (scoped_fd_source == NULL) {
+ NOTREACHED() << "CFFileDescriptorCreateRunLoopSource failed";
+ return false;
+ }
+ CFRunLoopAddSource(run_loop(), scoped_fd_source, kCFRunLoopCommonModes);
+
+ // Transfer ownership of scoped_fdref and fd_source to controller.
+ controller->Init(scoped_fdref.release(), callback_types,
+ scoped_fd_source.release(), persistent);
+ } else {
+ // It's illegal to use this function to listen on 2 separate fds with the
+ // same |controller|.
+ if (CFFileDescriptorGetNativeDescriptor(fdref) != fd) {
+ NOTREACHED() << "FDs don't match: "
+ << CFFileDescriptorGetNativeDescriptor(fdref)
+ << " != " << fd;
+ return false;
+ }
+ if (persistent != controller->is_persistent_) {
+ NOTREACHED() << "persistent doesn't match";
+ return false;
+ }
+
+ // Combine old/new event masks.
+ CFFileDescriptorDisableCallBacks(fdref, controller->callback_types_);
+ controller->callback_types_ |= callback_types;
+ CFFileDescriptorEnableCallBacks(fdref, controller->callback_types_);
+ }
+
+ controller->set_watcher(delegate);
+ controller->set_pump(this);
+
+ return true;
+}
+
+void MessagePumpIOSForIO::RemoveRunLoopSource(CFRunLoopSourceRef source) {
+ CFRunLoopRemoveSource(run_loop(), source, kCFRunLoopCommonModes);
+}
+
+void MessagePumpIOSForIO::AddIOObserver(IOObserver *obs) {
+ io_observers_.AddObserver(obs);
+}
+
+void MessagePumpIOSForIO::RemoveIOObserver(IOObserver *obs) {
+ io_observers_.RemoveObserver(obs);
+}
+
+void MessagePumpIOSForIO::WillProcessIOEvent() {
+ FOR_EACH_OBSERVER(IOObserver, io_observers_, WillProcessIOEvent());
+}
+
+void MessagePumpIOSForIO::DidProcessIOEvent() {
+ FOR_EACH_OBSERVER(IOObserver, io_observers_, DidProcessIOEvent());
+}
+
+// static
+void MessagePumpIOSForIO::HandleFdIOEvent(CFFileDescriptorRef fdref,
+ CFOptionFlags callback_types,
+ void* context) {
+ FileDescriptorWatcher* controller =
+ static_cast<FileDescriptorWatcher*>(context);
+ DCHECK_EQ(fdref, controller->fdref_);
+
+ // Ensure that |fdref| will remain live for the duration of this function
+ // call even if |controller| is deleted or |StopWatchingFileDescriptor()| is
+ // called, either of which will cause |fdref| to be released.
+ mac::ScopedCFTypeRef<CFFileDescriptorRef> scoped_fdref(
+ fdref, base::scoped_policy::RETAIN);
+
+ int fd = CFFileDescriptorGetNativeDescriptor(fdref);
+ MessagePumpIOSForIO* pump = controller->pump();
+ if (callback_types & kCFFileDescriptorWriteCallBack)
+ controller->OnFileCanWriteWithoutBlocking(fd, pump);
+
+ // Perform the read callback only if the file descriptor has not been
+ // invalidated in the write callback. As |FileDescriptorWatcher| invalidates
+ // its file descriptor on destruction, the file descriptor being valid also
+ // guarantees that |controller| has not been deleted.
+ if (callback_types & kCFFileDescriptorReadCallBack &&
+ CFFileDescriptorIsValid(fdref)) {
+ DCHECK_EQ(fdref, controller->fdref_);
+ controller->OnFileCanReadWithoutBlocking(fd, pump);
+ }
+
+ // Re-enable callbacks after the read/write if the file descriptor is still
+ // valid and the controller is persistent.
+ if (CFFileDescriptorIsValid(fdref) && controller->is_persistent_) {
+ DCHECK_EQ(fdref, controller->fdref_);
+ CFFileDescriptorEnableCallBacks(fdref, callback_types);
+ }
+}
+
+} // namespace base
diff --git a/base/message_pump_io_ios.h b/base/message_pump_io_ios.h
new file mode 100644
index 0000000..407c1db
--- /dev/null
+++ b/base/message_pump_io_ios.h
@@ -0,0 +1,141 @@
+// Copyright 2012 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.
+
+#ifndef BASE_MESSAGE_PUMP_IO_IOS_H_
+#define BASE_MESSAGE_PUMP_IO_IOS_H_
+
+#include "base/base_export.h"
+#include "base/mac/scoped_cffiledescriptorref.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "base/message_pump_mac.h"
+#include "base/observer_list.h"
+
+namespace base {
+
+// This file introduces a class to monitor sockets and issue callbacks when
+// sockets are ready for I/O on iOS.
+class BASE_EXPORT MessagePumpIOSForIO : public MessagePumpNSRunLoop {
+ public:
+ class IOObserver {
+ public:
+ IOObserver() {}
+
+ // An IOObserver is an object that receives IO notifications from the
+ // MessagePump.
+ //
+ // NOTE: An IOObserver implementation should be extremely fast!
+ virtual void WillProcessIOEvent() = 0;
+ virtual void DidProcessIOEvent() = 0;
+
+ protected:
+ virtual ~IOObserver() {}
+ };
+
+ // Used with WatchFileDescriptor to asynchronously monitor the I/O readiness
+ // of a file descriptor.
+ class Watcher {
+ public:
+ // Called from MessageLoop::Run when an FD can be read from/written to
+ // without blocking
+ virtual void OnFileCanReadWithoutBlocking(int fd) = 0;
+ virtual void OnFileCanWriteWithoutBlocking(int fd) = 0;
+
+ protected:
+ virtual ~Watcher() {}
+ };
+
+ // Object returned by WatchFileDescriptor to manage further watching.
+ class FileDescriptorWatcher {
+ public:
+ FileDescriptorWatcher();
+ ~FileDescriptorWatcher(); // Implicitly calls StopWatchingFileDescriptor.
+
+ // NOTE: These methods aren't called StartWatching()/StopWatching() to
+ // avoid confusion with the win32 ObjectWatcher class.
+
+ // Stop watching the FD, always safe to call. No-op if there's nothing
+ // to do.
+ bool StopWatchingFileDescriptor();
+
+ private:
+ friend class MessagePumpIOSForIO;
+ friend class MessagePumpIOSForIOTest;
+
+ // Called by MessagePumpIOSForIO, ownership of |fdref| and |fd_source|
+ // is transferred to this object.
+ void Init(CFFileDescriptorRef fdref,
+ CFOptionFlags callback_types,
+ CFRunLoopSourceRef fd_source,
+ bool is_persistent);
+
+ void set_pump(MessagePumpIOSForIO* pump) { pump_ = pump; }
+ MessagePumpIOSForIO* pump() const { return pump_; }
+
+ void set_watcher(Watcher* watcher) { watcher_ = watcher; }
+
+ void OnFileCanReadWithoutBlocking(int fd, MessagePumpIOSForIO* pump);
+ void OnFileCanWriteWithoutBlocking(int fd, MessagePumpIOSForIO* pump);
+
+ bool is_persistent_; // false if this event is one-shot.
+ base::mac::ScopedCFFileDescriptorRef fdref_;
+ CFOptionFlags callback_types_;
+ base::mac::ScopedCFTypeRef<CFRunLoopSourceRef> fd_source_;
+ MessagePumpIOSForIO* pump_;
+ Watcher* watcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(FileDescriptorWatcher);
+ };
+
+ enum Mode {
+ WATCH_READ = 1 << 0,
+ WATCH_WRITE = 1 << 1,
+ WATCH_READ_WRITE = WATCH_READ | WATCH_WRITE
+ };
+
+ MessagePumpIOSForIO();
+
+ // Have the current thread's message loop watch for a a situation in which
+ // reading/writing to the FD can be performed without blocking.
+ // Callers must provide a preallocated FileDescriptorWatcher object which
+ // can later be used to manage the lifetime of this event.
+ // If a FileDescriptorWatcher is passed in which is already attached to
+ // an event, then the effect is cumulative i.e. after the call |controller|
+ // will watch both the previous event and the new one.
+ // If an error occurs while calling this method in a cumulative fashion, the
+ // event previously attached to |controller| is aborted.
+ // Returns true on success.
+ // Must be called on the same thread the message_pump is running on.
+ bool WatchFileDescriptor(int fd,
+ bool persistent,
+ int mode,
+ FileDescriptorWatcher *controller,
+ Watcher *delegate);
+
+ void RemoveRunLoopSource(CFRunLoopSourceRef source);
+
+ void AddIOObserver(IOObserver* obs);
+ void RemoveIOObserver(IOObserver* obs);
+
+ protected:
+ virtual ~MessagePumpIOSForIO();
+
+ private:
+ friend class MessagePumpIOSForIOTest;
+
+ void WillProcessIOEvent();
+ void DidProcessIOEvent();
+
+ static void HandleFdIOEvent(CFFileDescriptorRef fdref,
+ CFOptionFlags callback_types,
+ void* context);
+
+ ObserverList<IOObserver> io_observers_;
+ ThreadChecker watch_file_descriptor_caller_checker_;
+
+ DISALLOW_COPY_AND_ASSIGN(MessagePumpIOSForIO);
+};
+
+} // namespace base
+
+#endif // BASE_MESSAGE_PUMP_IO_IOS_H_
diff --git a/base/message_pump_io_ios_unittest.cc b/base/message_pump_io_ios_unittest.cc
new file mode 100644
index 0000000..ddf740f
--- /dev/null
+++ b/base/message_pump_io_ios_unittest.cc
@@ -0,0 +1,188 @@
+// Copyright 2012 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/message_pump_io_ios.h"
+
+#include <unistd.h>
+
+#include "base/message_loop.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/threading/thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+class MessagePumpIOSForIOTest : public testing::Test {
+ protected:
+ MessagePumpIOSForIOTest()
+ : ui_loop_(MessageLoop::TYPE_UI),
+ io_thread_("MessagePumpIOSForIOTestIOThread") {}
+ virtual ~MessagePumpIOSForIOTest() {}
+
+ virtual void SetUp() OVERRIDE {
+ Thread::Options options(MessageLoop::TYPE_IO, 0);
+ ASSERT_TRUE(io_thread_.StartWithOptions(options));
+ ASSERT_EQ(MessageLoop::TYPE_IO, io_thread_.message_loop()->type());
+ int ret = pipe(pipefds_);
+ ASSERT_EQ(0, ret);
+ ret = pipe(alternate_pipefds_);
+ ASSERT_EQ(0, ret);
+ }
+
+ virtual void TearDown() OVERRIDE {
+ if (HANDLE_EINTR(close(pipefds_[0])) < 0)
+ PLOG(ERROR) << "close";
+ if (HANDLE_EINTR(close(pipefds_[1])) < 0)
+ PLOG(ERROR) << "close";
+ }
+
+ MessageLoop* ui_loop() { return &ui_loop_; }
+ MessageLoopForIO* io_loop() const {
+ return static_cast<MessageLoopForIO*>(io_thread_.message_loop());
+ }
+
+ void HandleFdIOEvent(MessageLoopForIO::FileDescriptorWatcher* watcher) {
+ MessagePumpIOSForIO::HandleFdIOEvent(watcher->fdref_,
+ kCFFileDescriptorReadCallBack | kCFFileDescriptorWriteCallBack,
+ watcher);
+ }
+
+ int pipefds_[2];
+ int alternate_pipefds_[2];
+
+ private:
+ MessageLoop ui_loop_;
+ Thread io_thread_;
+
+ DISALLOW_COPY_AND_ASSIGN(MessagePumpIOSForIOTest);
+};
+
+namespace {
+
+// Concrete implementation of MessagePumpIOSForIO::Watcher that does
+// nothing useful.
+class StupidWatcher : public MessagePumpIOSForIO::Watcher {
+ public:
+ virtual ~StupidWatcher() {}
+
+ // base:MessagePumpIOSForIO::Watcher interface
+ virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE {}
+ virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE {}
+};
+
+#if GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
+
+// Test to make sure that we catch calling WatchFileDescriptor off of the
+// wrong thread.
+TEST_F(MessagePumpIOSForIOTest, TestWatchingFromBadThread) {
+ MessagePumpIOSForIO::FileDescriptorWatcher watcher;
+ StupidWatcher delegate;
+
+ ASSERT_DEBUG(io_loop()->WatchFileDescriptor(
+ STDOUT_FILENO, false, MessageLoopForIO::WATCH_READ, &watcher, &delegate),
+ "Check failed: "
+ "watch_file_descriptor_caller_checker_.CalledOnValidThread()");
+}
+
+#endif // GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
+
+class BaseWatcher : public MessagePumpIOSForIO::Watcher {
+ public:
+ BaseWatcher(MessagePumpIOSForIO::FileDescriptorWatcher* controller)
+ : controller_(controller) {
+ DCHECK(controller_);
+ }
+ virtual ~BaseWatcher() {}
+
+ // MessagePumpIOSForIO::Watcher interface
+ virtual void OnFileCanReadWithoutBlocking(int /* fd */) OVERRIDE {
+ NOTREACHED();
+ }
+
+ virtual void OnFileCanWriteWithoutBlocking(int /* fd */) OVERRIDE {
+ NOTREACHED();
+ }
+
+ protected:
+ MessagePumpIOSForIO::FileDescriptorWatcher* controller_;
+};
+
+class DeleteWatcher : public BaseWatcher {
+ public:
+ explicit DeleteWatcher(
+ MessagePumpIOSForIO::FileDescriptorWatcher* controller)
+ : BaseWatcher(controller) {}
+
+ virtual ~DeleteWatcher() {
+ DCHECK(!controller_);
+ }
+
+ virtual void OnFileCanWriteWithoutBlocking(int /* fd */) OVERRIDE {
+ DCHECK(controller_);
+ delete controller_;
+ controller_ = NULL;
+ }
+};
+
+TEST_F(MessagePumpIOSForIOTest, DeleteWatcher) {
+ scoped_refptr<MessagePumpIOSForIO> pump(new MessagePumpIOSForIO);
+ MessagePumpIOSForIO::FileDescriptorWatcher* watcher =
+ new MessagePumpIOSForIO::FileDescriptorWatcher;
+ DeleteWatcher delegate(watcher);
+ pump->WatchFileDescriptor(pipefds_[1],
+ false, MessagePumpIOSForIO::WATCH_READ_WRITE, watcher, &delegate);
+
+ // Spoof a callback.
+ HandleFdIOEvent(watcher);
+}
+
+class StopWatcher : public BaseWatcher {
+ public:
+ StopWatcher(MessagePumpIOSForIO::FileDescriptorWatcher* controller,
+ MessagePumpIOSForIO* pump,
+ int fd_to_start_watching = -1)
+ : BaseWatcher(controller),
+ pump_(pump),
+ fd_to_start_watching_(fd_to_start_watching) {}
+
+ virtual ~StopWatcher() {}
+
+ virtual void OnFileCanWriteWithoutBlocking(int /* fd */) OVERRIDE {
+ controller_->StopWatchingFileDescriptor();
+ if (fd_to_start_watching_ >= 0) {
+ pump_->WatchFileDescriptor(fd_to_start_watching_,
+ false, MessagePumpIOSForIO::WATCH_READ_WRITE, controller_, this);
+ }
+ }
+
+ private:
+ MessagePumpIOSForIO* pump_;
+ int fd_to_start_watching_;
+};
+
+TEST_F(MessagePumpIOSForIOTest, StopWatcher) {
+ scoped_refptr<MessagePumpIOSForIO> pump(new MessagePumpIOSForIO);
+ MessagePumpIOSForIO::FileDescriptorWatcher watcher;
+ StopWatcher delegate(&watcher, pump);
+ pump->WatchFileDescriptor(pipefds_[1],
+ false, MessagePumpIOSForIO::WATCH_READ_WRITE, &watcher, &delegate);
+
+ // Spoof a callback.
+ HandleFdIOEvent(&watcher);
+}
+
+TEST_F(MessagePumpIOSForIOTest, StopWatcherAndWatchSomethingElse) {
+ scoped_refptr<MessagePumpIOSForIO> pump(new MessagePumpIOSForIO);
+ MessagePumpIOSForIO::FileDescriptorWatcher watcher;
+ StopWatcher delegate(&watcher, pump, alternate_pipefds_[1]);
+ pump->WatchFileDescriptor(pipefds_[1],
+ false, MessagePumpIOSForIO::WATCH_READ_WRITE, &watcher, &delegate);
+
+ // Spoof a callback.
+ HandleFdIOEvent(&watcher);
+}
+
+} // namespace
+
+} // namespace base
diff --git a/base/message_pump_libevent.cc b/base/message_pump_libevent.cc
index 66acd86..14a4715 100644
--- a/base/message_pump_libevent.cc
+++ b/base/message_pump_libevent.cc
@@ -144,7 +144,7 @@ MessagePumpLibevent::~MessagePumpLibevent() {
bool MessagePumpLibevent::WatchFileDescriptor(int fd,
bool persistent,
- Mode mode,
+ int mode,
FileDescriptorWatcher *controller,
Watcher *delegate) {
DCHECK_GE(fd, 0);
@@ -156,10 +156,10 @@ bool MessagePumpLibevent::WatchFileDescriptor(int fd,
DCHECK(watch_file_descriptor_caller_checker_.CalledOnValidThread());
int event_mask = persistent ? EV_PERSIST : 0;
- if ((mode & WATCH_READ) != 0) {
+ if (mode & WATCH_READ) {
event_mask |= EV_READ;
}
- if ((mode & WATCH_WRITE) != 0) {
+ if (mode & WATCH_WRITE) {
event_mask |= EV_WRITE;
}
@@ -190,12 +190,12 @@ bool MessagePumpLibevent::WatchFileDescriptor(int fd,
event_set(evt.get(), fd, event_mask, OnLibeventNotification, controller);
// Tell libevent which message pump this socket will belong to when we add it.
- if (event_base_set(event_base_, evt.get()) != 0) {
+ if (event_base_set(event_base_, evt.get())) {
return false;
}
// Add this socket to the list of monitored sockets.
- if (event_add(evt.get(), NULL) != 0) {
+ if (event_add(evt.get(), NULL)) {
return false;
}
diff --git a/base/message_pump_libevent.h b/base/message_pump_libevent.h
index 7c689b9..90e8510 100644
--- a/base/message_pump_libevent.h
+++ b/base/message_pump_libevent.h
@@ -38,8 +38,6 @@ class BASE_EXPORT MessagePumpLibevent : public MessagePump {
virtual ~IOObserver() {}
};
- class FileDescriptorWatcher;
-
// Used with WatchFileDescriptor to asynchronously monitor the I/O readiness
// of a file descriptor.
class Watcher {
@@ -78,7 +76,7 @@ class BASE_EXPORT MessagePumpLibevent : public MessagePump {
event* ReleaseEvent();
void set_pump(MessagePumpLibevent* pump) { pump_ = pump; }
- MessagePumpLibevent* pump() { return pump_; }
+ MessagePumpLibevent* pump() const { return pump_; }
void set_watcher(Watcher* watcher) { watcher_ = watcher; }
@@ -115,7 +113,7 @@ class BASE_EXPORT MessagePumpLibevent : public MessagePump {
// TODO(dkegel): switch to edge-triggered readiness notification
bool WatchFileDescriptor(int fd,
bool persistent,
- Mode mode,
+ int mode,
FileDescriptorWatcher *controller,
Watcher *delegate);
diff --git a/base/message_pump_libevent_unittest.cc b/base/message_pump_libevent_unittest.cc
index 94245ce..e48a9c5 100644
--- a/base/message_pump_libevent_unittest.cc
+++ b/base/message_pump_libevent_unittest.cc
@@ -30,8 +30,8 @@ class MessagePumpLibeventTest : public testing::Test {
Thread::Options options(MessageLoop::TYPE_IO, 0);
ASSERT_TRUE(io_thread_.StartWithOptions(options));
ASSERT_EQ(MessageLoop::TYPE_IO, io_thread_.message_loop()->type());
- int err = pipe(pipefds_);
- ASSERT_EQ(0, err);
+ int ret = pipe(pipefds_);
+ ASSERT_EQ(0, ret);
}
virtual void TearDown() OVERRIDE {
@@ -52,9 +52,11 @@ class MessagePumpLibeventTest : public testing::Test {
pump->OnLibeventNotification(0, EV_WRITE | EV_READ, controller);
}
+ int pipefds_[2];
+
+ private:
MessageLoop ui_loop_;
Thread io_thread_;
- int pipefds_[2];
};
namespace {