Index: base/message_pump_mac.mm |
=================================================================== |
--- base/message_pump_mac.mm (revision 90809) |
+++ base/message_pump_mac.mm (working copy) |
@@ -10,6 +10,7 @@ |
#include <limits> |
#include "base/logging.h" |
+#include "base/mac/scoped_cftyperef.h" |
#include "base/time.h" |
namespace { |
@@ -24,6 +25,78 @@ |
namespace base { |
+MessagePumpCFRunLoopBase::FileDescriptorWatcher::FileDescriptorWatcher() |
+ : is_persistent_(false), |
+ fdref_(NULL), |
+ callback_types_(0), |
+ fd_source_(NULL), |
+ pump_(NULL), |
+ watcher_(NULL) { |
+} |
+ |
+MessagePumpCFRunLoopBase::FileDescriptorWatcher::~FileDescriptorWatcher() { |
+ if (fdref_) { |
+ StopWatchingFileDescriptor(); |
+ } |
+} |
+ |
+bool MessagePumpCFRunLoopBase:: |
+ FileDescriptorWatcher::StopWatchingFileDescriptor() { |
+ CFFileDescriptorRef fdref = ReleaseCFFileDescriptor(); |
+ if (fdref == NULL) |
+ return true; |
+ |
+ CFFileDescriptorDisableCallBacks(fdref, callback_types_); |
+ pump_->RemoveRunLoopSource(fd_source_); |
+ CFRelease(fd_source_); |
+ fd_source_ = NULL; |
+ CFFileDescriptorInvalidate(fdref); |
+ CFRelease(fdref); |
+ callback_types_ = 0; |
+ pump_ = NULL; |
+ watcher_ = NULL; |
+ return true; |
+} |
+ |
+void MessagePumpCFRunLoopBase::FileDescriptorWatcher::Init( |
+ CFFileDescriptorRef fdref, |
+ CFOptionFlags callback_types, |
+ CFRunLoopSourceRef fd_source, |
+ bool is_persistent) { |
+ DCHECK(fdref); |
+ DCHECK(!fdref_); |
+ |
+ is_persistent_ = is_persistent; |
+ fdref_ = fdref; |
+ callback_types_ = callback_types; |
+ fd_source_ = fd_source; |
+} |
+ |
+CFFileDescriptorRef |
+MessagePumpCFRunLoopBase::FileDescriptorWatcher::ReleaseCFFileDescriptor() { |
+ CFFileDescriptorRef fdref = fdref_; |
+ fdref_ = NULL; |
+ return fdref; |
+} |
+ |
+void MessagePumpCFRunLoopBase:: |
+ FileDescriptorWatcher::OnFileCanReadWithoutBlocking( |
+ int fd, |
+ MessagePumpCFRunLoopBase* pump) { |
+ pump->WillProcessIOEvent(); |
+ watcher_->OnFileCanReadWithoutBlocking(fd); |
+ pump->DidProcessIOEvent(); |
+} |
+ |
+void MessagePumpCFRunLoopBase:: |
+ FileDescriptorWatcher::OnFileCanWriteWithoutBlocking( |
+ int fd, |
+ MessagePumpCFRunLoopBase* pump) { |
+ pump->WillProcessIOEvent(); |
+ watcher_->OnFileCanWriteWithoutBlocking(fd); |
+ pump->DidProcessIOEvent(); |
+} |
+ |
// A scoper for autorelease pools created from message pump run loops. |
// Avoids dirtying up the ScopedNSAutoreleasePool interface for the rare |
// case where an autorelease pool needs to be passed in. |
@@ -148,6 +221,124 @@ |
CFRelease(run_loop_); |
} |
+// static |
+void MessagePumpCFRunLoopBase::HandleFdIOEvent(CFFileDescriptorRef fdref, |
+ CFOptionFlags callback_types, |
+ void* info) { |
+ int fd = CFFileDescriptorGetNativeDescriptor(fdref); |
+ |
+ FileDescriptorWatcher* controller = |
+ static_cast<FileDescriptorWatcher*>(info); |
+ |
+ CHECK_EQ(fdref, controller->fdref_); |
+ bool persistent = controller->is_persistent_; |
+ |
+ MessagePumpCFRunLoopBase* pump = controller->pump(); |
+ |
+ if (callback_types & kCFFileDescriptorWriteCallBack) { |
+ controller->OnFileCanWriteWithoutBlocking(fd, pump); |
+ } |
+ if (callback_types & kCFFileDescriptorReadCallBack) { |
+ controller->OnFileCanReadWithoutBlocking(fd, pump); |
+ } |
+ |
+ // Must read/write from the fd before re-enabling the callbacks. |
+ // |controller| may have been deleted, and |fdref| may have been |
+ // invalidated. |
+ if (CFFileDescriptorIsValid(fdref) && persistent) |
+ CFFileDescriptorEnableCallBacks(fdref, callback_types); |
+} |
+ |
+bool MessagePumpCFRunLoopBase::WatchFileDescriptor( |
+ int fd, |
+ bool persistent, |
+ Mode 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 = CFFileDescriptorContext(); |
+ source_context.version = 0; |
+ source_context.info = controller; |
+ |
+ CFOptionFlags callback_types = 0; |
+ if ((mode & WATCH_READ) != 0) { |
+ callback_types |= kCFFileDescriptorReadCallBack; |
+ } |
+ if ((mode & WATCH_WRITE) != 0) { |
+ callback_types |= kCFFileDescriptorWriteCallBack; |
+ } |
+ |
+ base::mac::ScopedCFTypeRef<CFFileDescriptorRef> fdref( |
+ controller->ReleaseCFFileDescriptor()); |
+ if (fdref == NULL) { |
+ // Ownership is transferred to the controller. |
+ fdref.reset(CFFileDescriptorCreate(kCFAllocatorDefault, |
+ fd, false, |
+ HandleFdIOEvent, |
+ &source_context)); |
+ if (fdref == NULL) { |
+ NOTREACHED() << "CFFileDescriptorCreate failed"; |
+ return false; |
+ } |
+ CFFileDescriptorEnableCallBacks(fdref, callback_types); |
+ |
+ // TODO(wtc): what should the 'order' argument be? |
+ CFRunLoopSourceRef fd_source = CFFileDescriptorCreateRunLoopSource( |
+ kCFAllocatorDefault, fdref, 0); |
+ if (fd_source == NULL) { |
+ NOTREACHED() << "CFFileDescriptorCreateRunLoopSource failed"; |
+ return false; |
+ } |
+ CFRunLoopAddSource(run_loop_, fd_source, kCFRunLoopCommonModes); |
+ |
+ // Transfer ownership of fdref to controller. |
+ controller->Init(fdref.release(), callback_types, fd_source, 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 don'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->fdref_ = fdref.release(); |
+ } |
+ |
+ controller->set_watcher(delegate); |
+ controller->set_pump(this); |
+ |
+ return true; |
+} |
+ |
+void MessagePumpCFRunLoopBase::RemoveRunLoopSource( |
+ CFRunLoopSourceRef source) { |
+ CFRunLoopRemoveSource(run_loop_, source, kCFRunLoopCommonModes); |
+} |
+ |
+void MessagePumpCFRunLoopBase::AddIOObserver(IOObserver *obs) { |
+ io_observers_.AddObserver(obs); |
+} |
+ |
+void MessagePumpCFRunLoopBase::RemoveIOObserver(IOObserver *obs) { |
+ io_observers_.RemoveObserver(obs); |
+} |
+ |
// Must be called on the run loop thread. |
void MessagePumpCFRunLoopBase::Run(Delegate* delegate) { |
// nesting_level_ will be incremented in EnterExitRunLoop, so set |
@@ -213,6 +404,14 @@ |
CFRunLoopTimerSetNextFireDate(delayed_work_timer_, delayed_work_fire_time_); |
} |
+void MessagePumpCFRunLoopBase::WillProcessIOEvent() { |
+ FOR_EACH_OBSERVER(IOObserver, io_observers_, WillProcessIOEvent()); |
+} |
+ |
+void MessagePumpCFRunLoopBase::DidProcessIOEvent() { |
+ FOR_EACH_OBSERVER(IOObserver, io_observers_, DidProcessIOEvent()); |
+} |
+ |
// Called from the run loop. |
// static |
void MessagePumpCFRunLoopBase::RunDelayedWorkTimer(CFRunLoopTimerRef timer, |