diff options
-rw-r--r-- | base/base.gyp | 11 | ||||
-rw-r--r-- | base/mac/scoped_cffiledescriptorref.h | 75 | ||||
-rw-r--r-- | base/message_loop.cc | 22 | ||||
-rw-r--r-- | base/message_loop.h | 28 | ||||
-rw-r--r-- | base/message_pump_io_ios.cc | 209 | ||||
-rw-r--r-- | base/message_pump_io_ios.h | 141 | ||||
-rw-r--r-- | base/message_pump_io_ios_unittest.cc | 188 | ||||
-rw-r--r-- | base/message_pump_libevent.cc | 10 | ||||
-rw-r--r-- | base/message_pump_libevent.h | 6 | ||||
-rw-r--r-- | base/message_pump_libevent_unittest.cc | 8 |
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 { |