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