1 // Copyright 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "base/message_loop/message_pump_io_ios.h" 6 7 namespace base { 8 9 MessagePumpIOSForIO::FileDescriptorWatcher::FileDescriptorWatcher() 10 : is_persistent_(false), 11 fdref_(NULL), 12 callback_types_(0), 13 fd_source_(NULL), 14 watcher_(NULL) { 15 } 16 17 MessagePumpIOSForIO::FileDescriptorWatcher::~FileDescriptorWatcher() { 18 StopWatchingFileDescriptor(); 19 } 20 21 bool MessagePumpIOSForIO::FileDescriptorWatcher::StopWatchingFileDescriptor() { 22 if (fdref_ == NULL) 23 return true; 24 25 CFFileDescriptorDisableCallBacks(fdref_, callback_types_); 26 if (pump_) 27 pump_->RemoveRunLoopSource(fd_source_); 28 fd_source_.reset(); 29 fdref_.reset(); 30 callback_types_ = 0; 31 pump_.reset(); 32 watcher_ = NULL; 33 return true; 34 } 35 36 void MessagePumpIOSForIO::FileDescriptorWatcher::Init( 37 CFFileDescriptorRef fdref, 38 CFOptionFlags callback_types, 39 CFRunLoopSourceRef fd_source, 40 bool is_persistent) { 41 DCHECK(fdref); 42 DCHECK(!fdref_); 43 44 is_persistent_ = is_persistent; 45 fdref_.reset(fdref); 46 callback_types_ = callback_types; 47 fd_source_.reset(fd_source); 48 } 49 50 void MessagePumpIOSForIO::FileDescriptorWatcher::OnFileCanReadWithoutBlocking( 51 int fd, 52 MessagePumpIOSForIO* pump) { 53 DCHECK(callback_types_ & kCFFileDescriptorReadCallBack); 54 pump->WillProcessIOEvent(); 55 watcher_->OnFileCanReadWithoutBlocking(fd); 56 pump->DidProcessIOEvent(); 57 } 58 59 void MessagePumpIOSForIO::FileDescriptorWatcher::OnFileCanWriteWithoutBlocking( 60 int fd, 61 MessagePumpIOSForIO* pump) { 62 DCHECK(callback_types_ & kCFFileDescriptorWriteCallBack); 63 pump->WillProcessIOEvent(); 64 watcher_->OnFileCanWriteWithoutBlocking(fd); 65 pump->DidProcessIOEvent(); 66 } 67 68 MessagePumpIOSForIO::MessagePumpIOSForIO() : weak_factory_(this) { 69 } 70 71 MessagePumpIOSForIO::~MessagePumpIOSForIO() { 72 } 73 74 bool MessagePumpIOSForIO::WatchFileDescriptor( 75 int fd, 76 bool persistent, 77 int mode, 78 FileDescriptorWatcher *controller, 79 Watcher *delegate) { 80 DCHECK_GE(fd, 0); 81 DCHECK(controller); 82 DCHECK(delegate); 83 DCHECK(mode == WATCH_READ || mode == WATCH_WRITE || mode == WATCH_READ_WRITE); 84 85 // WatchFileDescriptor should be called on the pump thread. It is not 86 // threadsafe, and your watcher may never be registered. 87 DCHECK(watch_file_descriptor_caller_checker_.CalledOnValidThread()); 88 89 CFFileDescriptorContext source_context = {0}; 90 source_context.info = controller; 91 92 CFOptionFlags callback_types = 0; 93 if (mode & WATCH_READ) { 94 callback_types |= kCFFileDescriptorReadCallBack; 95 } 96 if (mode & WATCH_WRITE) { 97 callback_types |= kCFFileDescriptorWriteCallBack; 98 } 99 100 CFFileDescriptorRef fdref = controller->fdref_; 101 if (fdref == NULL) { 102 base::ScopedCFTypeRef<CFFileDescriptorRef> scoped_fdref( 103 CFFileDescriptorCreate( 104 kCFAllocatorDefault, fd, false, HandleFdIOEvent, &source_context)); 105 if (scoped_fdref == NULL) { 106 NOTREACHED() << "CFFileDescriptorCreate failed"; 107 return false; 108 } 109 110 CFFileDescriptorEnableCallBacks(scoped_fdref, callback_types); 111 112 // TODO(wtc): what should the 'order' argument be? 113 base::ScopedCFTypeRef<CFRunLoopSourceRef> scoped_fd_source( 114 CFFileDescriptorCreateRunLoopSource( 115 kCFAllocatorDefault, scoped_fdref, 0)); 116 if (scoped_fd_source == NULL) { 117 NOTREACHED() << "CFFileDescriptorCreateRunLoopSource failed"; 118 return false; 119 } 120 CFRunLoopAddSource(run_loop(), scoped_fd_source, kCFRunLoopCommonModes); 121 122 // Transfer ownership of scoped_fdref and fd_source to controller. 123 controller->Init(scoped_fdref.release(), callback_types, 124 scoped_fd_source.release(), persistent); 125 } else { 126 // It's illegal to use this function to listen on 2 separate fds with the 127 // same |controller|. 128 if (CFFileDescriptorGetNativeDescriptor(fdref) != fd) { 129 NOTREACHED() << "FDs don't match: " 130 << CFFileDescriptorGetNativeDescriptor(fdref) 131 << " != " << fd; 132 return false; 133 } 134 if (persistent != controller->is_persistent_) { 135 NOTREACHED() << "persistent doesn't match"; 136 return false; 137 } 138 139 // Combine old/new event masks. 140 CFFileDescriptorDisableCallBacks(fdref, controller->callback_types_); 141 controller->callback_types_ |= callback_types; 142 CFFileDescriptorEnableCallBacks(fdref, controller->callback_types_); 143 } 144 145 controller->set_watcher(delegate); 146 controller->set_pump(weak_factory_.GetWeakPtr()); 147 148 return true; 149 } 150 151 void MessagePumpIOSForIO::RemoveRunLoopSource(CFRunLoopSourceRef source) { 152 CFRunLoopRemoveSource(run_loop(), source, kCFRunLoopCommonModes); 153 } 154 155 void MessagePumpIOSForIO::AddIOObserver(IOObserver *obs) { 156 io_observers_.AddObserver(obs); 157 } 158 159 void MessagePumpIOSForIO::RemoveIOObserver(IOObserver *obs) { 160 io_observers_.RemoveObserver(obs); 161 } 162 163 void MessagePumpIOSForIO::WillProcessIOEvent() { 164 FOR_EACH_OBSERVER(IOObserver, io_observers_, WillProcessIOEvent()); 165 } 166 167 void MessagePumpIOSForIO::DidProcessIOEvent() { 168 FOR_EACH_OBSERVER(IOObserver, io_observers_, DidProcessIOEvent()); 169 } 170 171 // static 172 void MessagePumpIOSForIO::HandleFdIOEvent(CFFileDescriptorRef fdref, 173 CFOptionFlags callback_types, 174 void* context) { 175 FileDescriptorWatcher* controller = 176 static_cast<FileDescriptorWatcher*>(context); 177 DCHECK_EQ(fdref, controller->fdref_); 178 179 // Ensure that |fdref| will remain live for the duration of this function 180 // call even if |controller| is deleted or |StopWatchingFileDescriptor()| is 181 // called, either of which will cause |fdref| to be released. 182 ScopedCFTypeRef<CFFileDescriptorRef> scoped_fdref( 183 fdref, base::scoped_policy::RETAIN); 184 185 int fd = CFFileDescriptorGetNativeDescriptor(fdref); 186 MessagePumpIOSForIO* pump = controller->pump().get(); 187 DCHECK(pump); 188 if (callback_types & kCFFileDescriptorWriteCallBack) 189 controller->OnFileCanWriteWithoutBlocking(fd, pump); 190 191 // Perform the read callback only if the file descriptor has not been 192 // invalidated in the write callback. As |FileDescriptorWatcher| invalidates 193 // its file descriptor on destruction, the file descriptor being valid also 194 // guarantees that |controller| has not been deleted. 195 if (callback_types & kCFFileDescriptorReadCallBack && 196 CFFileDescriptorIsValid(fdref)) { 197 DCHECK_EQ(fdref, controller->fdref_); 198 controller->OnFileCanReadWithoutBlocking(fd, pump); 199 } 200 201 // Re-enable callbacks after the read/write if the file descriptor is still 202 // valid and the controller is persistent. 203 if (CFFileDescriptorIsValid(fdref) && controller->is_persistent_) { 204 DCHECK_EQ(fdref, controller->fdref_); 205 CFFileDescriptorEnableCallBacks(fdref, callback_types); 206 } 207 } 208 209 } // namespace base 210