Home | History | Annotate | Download | only in message_loop
      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