Index: base/message_pump_io_ios.mm |
diff --git a/base/message_pump_io_ios.mm b/base/message_pump_io_ios.mm |
new file mode 100644 |
index 0000000000000000000000000000000000000000..4a789d512b448eed4acb64e8b1f7cc38d00f7f3d |
--- /dev/null |
+++ b/base/message_pump_io_ios.mm |
@@ -0,0 +1,199 @@ |
+// Copyright 2012 The Chromium Authors. All rights reserved. |
wtc
2012/11/21 18:31:05
This file is named message_pump_io_ios.mm, but I t
stuartmorgan
2012/11/21 21:16:13
AFAIK our policy has always been to name Mac (and
|
+// 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) { |
Mark Mentovai
2012/11/20 22:29:27
Do you want to DCHECK that you’re really listening
blundell
2012/11/21 16:56:52
Done.
|
+ pump->WillProcessIOEvent(); |
+ watcher_->OnFileCanReadWithoutBlocking(fd); |
+ pump->DidProcessIOEvent(); |
+} |
+ |
+void MessagePumpIOSForIO::FileDescriptorWatcher::OnFileCanWriteWithoutBlocking( |
+ int fd, |
+ MessagePumpIOSForIO* pump) { |
+ pump->WillProcessIOEvent(); |
+ watcher_->OnFileCanWriteWithoutBlocking(fd); |
+ pump->DidProcessIOEvent(); |
+} |
+ |
+MessagePumpIOSForIO::MessagePumpIOSForIO() { |
+} |
+ |
+MessagePumpIOSForIO::~MessagePumpIOSForIO() { |
+} |
+ |
+bool MessagePumpIOSForIO::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 | WATCH_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) != 0) { |
+ callback_types |= kCFFileDescriptorReadCallBack; |
+ } |
+ if ((mode & WATCH_WRITE) != 0) { |
+ 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; |
Mark Mentovai
2012/11/20 22:29:27
Does this “else” block ever get entered in practic
blundell
2012/11/21 16:56:52
I don't know whether it ever gets entered in pract
|
+ 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* info) { |
+ int fd = CFFileDescriptorGetNativeDescriptor(fdref); |
+ |
+ FileDescriptorWatcher* controller = |
+ static_cast<FileDescriptorWatcher*>(info); |
+ |
+ CHECK_EQ(fdref, controller->fdref_); |
Mark Mentovai
2012/11/20 22:29:27
What justifies this being a CHECK and not a DCHECK
blundell
2012/11/21 16:56:52
Changed.
On 2012/11/20 22:29:27, Mark Mentovai wr
wtc
2012/11/21 18:31:05
I may have used a CHECK to try to expose bugs usin
|
+ bool persistent = controller->is_persistent_; |
+ |
+ MessagePumpIOSForIO* pump = controller->pump(); |
+ if (callback_types & kCFFileDescriptorWriteCallBack) { |
Mark Mentovai
2012/11/20 22:29:27
Your style in this file has varied between (v & k)
blundell
2012/11/21 16:56:52
Changed to consistently use the (v & k) form both
|
+ controller->OnFileCanWriteWithoutBlocking(fd, pump); |
+ } |
+ if (callback_types & kCFFileDescriptorReadCallBack) { |
+ controller->OnFileCanReadWithoutBlocking(fd, pump); |
wtc
2012/11/21 18:31:05
I may have filed a bug report about this. |control
|
+ } |
+ |
+ // Must read/write from the fd before re-enabling the callbacks. |
+ // |controller| may have been deleted, so we test a copy of |
+ // controller->persistent. |fdref| may have been invalidated. |
Mark Mentovai
2012/11/20 22:29:27
If you’re concerned that controller may be gone bu
blundell
2012/11/21 16:56:52
My understanding is that the |persistent| argument
wtc
2012/11/21 18:31:05
At this point, |controller| may have been destroye
|
+ CHECK_GT(CFGetRetainCount(fdref), 0); |
+ if (CFFileDescriptorIsValid(fdref) && persistent) |
+ CFFileDescriptorEnableCallBacks(fdref, callback_types); |
+} |
+ |
+} // namespace base |