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