| 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,
|
|
|