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 return false;
100 }
101
102 CFFileDescriptorEnableCallBacks(scoped_fdref, callback_types);
103
104 // TODO(wtc): what should the 'order' argument be?
105 apple::ScopedCFTypeRef<CFRunLoopSourceRef> scoped_fd_source(
106 CFFileDescriptorCreateRunLoopSource(kCFAllocatorDefault, scoped_fdref,
107 0));
108 if (scoped_fd_source == NULL) {
109 NOTREACHED() << "CFFileDescriptorCreateRunLoopSource failed";
110 return false;
111 }
112 CFRunLoopAddSource(run_loop(), scoped_fd_source, kCFRunLoopCommonModes);
113
114 // Transfer ownership of scoped_fdref and fd_source to controller.
115 controller->Init(scoped_fdref.release(), callback_types,
116 scoped_fd_source.release(), persistent);
117 } else {
118 // It's illegal to use this function to listen on 2 separate fds with the
119 // same |controller|.
120 if (CFFileDescriptorGetNativeDescriptor(fdref) != fd) {
121 NOTREACHED() << "FDs don't match: "
122 << CFFileDescriptorGetNativeDescriptor(fdref)
123 << " != " << fd;
124 return false;
125 }
126 if (persistent != controller->is_persistent_) {
127 NOTREACHED() << "persistent doesn't match";
128 return false;
129 }
130
131 // Combine old/new event masks.
132 CFFileDescriptorDisableCallBacks(fdref, controller->callback_types_);
133 controller->callback_types_ |= callback_types;
134 CFFileDescriptorEnableCallBacks(fdref, controller->callback_types_);
135 }
136
137 controller->set_watcher(delegate);
138 controller->set_pump(weak_factory_.GetWeakPtr());
139
140 return true;
141 }
142
RemoveRunLoopSource(CFRunLoopSourceRef source)143 void MessagePumpIOSForIO::RemoveRunLoopSource(CFRunLoopSourceRef source) {
144 CFRunLoopRemoveSource(run_loop(), source, kCFRunLoopCommonModes);
145 }
146
147 // static
HandleFdIOEvent(CFFileDescriptorRef fdref,CFOptionFlags callback_types,void * context)148 void MessagePumpIOSForIO::HandleFdIOEvent(CFFileDescriptorRef fdref,
149 CFOptionFlags callback_types,
150 void* context) {
151 FdWatchController* controller = static_cast<FdWatchController*>(context);
152 DCHECK_EQ(fdref, controller->fdref_.get());
153
154 // Ensure that |fdref| will remain live for the duration of this function
155 // call even if |controller| is deleted or |StopWatchingFileDescriptor()| is
156 // called, either of which will cause |fdref| to be released.
157 apple::ScopedCFTypeRef<CFFileDescriptorRef> scoped_fdref(
158 fdref, base::scoped_policy::RETAIN);
159
160 int fd = CFFileDescriptorGetNativeDescriptor(fdref);
161 MessagePumpIOSForIO* pump = controller->pump().get();
162 DCHECK(pump);
163
164 // Inform ThreadController of this native work item for tracking and tracing
165 // purposes.
166 Delegate::ScopedDoWorkItem scoped_do_work_item;
167 if (pump->delegate()) {
168 scoped_do_work_item = pump->delegate()->BeginWorkItem();
169 }
170
171 if (callback_types & kCFFileDescriptorWriteCallBack)
172 controller->OnFileCanWriteWithoutBlocking(fd, pump);
173
174 // Perform the read callback only if the file descriptor has not been
175 // invalidated in the write callback. As |FdWatchController| invalidates
176 // its file descriptor on destruction, the file descriptor being valid also
177 // guarantees that |controller| has not been deleted.
178 if (callback_types & kCFFileDescriptorReadCallBack &&
179 CFFileDescriptorIsValid(fdref)) {
180 DCHECK_EQ(fdref, controller->fdref_.get());
181 controller->OnFileCanReadWithoutBlocking(fd, pump);
182 }
183
184 // Re-enable callbacks after the read/write if the file descriptor is still
185 // valid and the controller is persistent.
186 if (CFFileDescriptorIsValid(fdref) && controller->is_persistent_) {
187 DCHECK_EQ(fdref, controller->fdref_.get());
188 CFFileDescriptorEnableCallBacks(fdref, callback_types);
189 }
190 }
191
192 } // namespace base
193