Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(786)

Side by Side Diff: chrome/common/ipc_channel_win.cc

Issue 155905: Separates ipc code from common (http://crbug.com/16829) (Closed)
Patch Set: Fixes reference to 'common_message_traits' it's actually 'common_param_traits' Created 11 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « chrome/common/ipc_channel_win.h ('k') | chrome/common/ipc_fuzzing_tests.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2006-2008 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 "chrome/common/ipc_channel_win.h"
6
7 #include <windows.h>
8 #include <sstream>
9
10 #include "base/compiler_specific.h"
11 #include "base/logging.h"
12 #include "base/non_thread_safe.h"
13 #include "base/stats_counters.h"
14 #include "base/win_util.h"
15 #include "chrome/common/chrome_counters.h"
16 #include "chrome/common/ipc_logging.h"
17 #include "chrome/common/ipc_message_utils.h"
18
19 namespace IPC {
20 //------------------------------------------------------------------------------
21
22 Channel::ChannelImpl::State::State(ChannelImpl* channel) : is_pending(false) {
23 memset(&context.overlapped, 0, sizeof(context.overlapped));
24 context.handler = channel;
25 }
26
27 Channel::ChannelImpl::State::~State() {
28 COMPILE_ASSERT(!offsetof(Channel::ChannelImpl::State, context),
29 starts_with_io_context);
30 }
31
32 //------------------------------------------------------------------------------
33
34 Channel::ChannelImpl::ChannelImpl(const std::string& channel_id, Mode mode,
35 Listener* listener)
36 : ALLOW_THIS_IN_INITIALIZER_LIST(input_state_(this)),
37 ALLOW_THIS_IN_INITIALIZER_LIST(output_state_(this)),
38 pipe_(INVALID_HANDLE_VALUE),
39 listener_(listener),
40 waiting_connect_(mode == MODE_SERVER),
41 processing_incoming_(false),
42 ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)) {
43 if (!CreatePipe(channel_id, mode)) {
44 // The pipe may have been closed already.
45 LOG(WARNING) << "Unable to create pipe named \"" << channel_id <<
46 "\" in " << (mode == 0 ? "server" : "client") << " mode.";
47 }
48 }
49
50 void Channel::ChannelImpl::Close() {
51 if (thread_check_.get()) {
52 DCHECK(thread_check_->CalledOnValidThread());
53 }
54
55 bool waited = false;
56 if (input_state_.is_pending || output_state_.is_pending) {
57 CancelIo(pipe_);
58 waited = true;
59 }
60
61 // Closing the handle at this point prevents us from issuing more requests
62 // form OnIOCompleted().
63 if (pipe_ != INVALID_HANDLE_VALUE) {
64 CloseHandle(pipe_);
65 pipe_ = INVALID_HANDLE_VALUE;
66 }
67
68 // Make sure all IO has completed.
69 base::Time start = base::Time::Now();
70 while (input_state_.is_pending || output_state_.is_pending) {
71 MessageLoopForIO::current()->WaitForIOCompletion(INFINITE, this);
72 }
73 if (waited) {
74 // We want to see if we block the message loop for too long.
75 UMA_HISTOGRAM_TIMES("AsyncIO.IPCChannelClose", base::Time::Now() - start);
76 }
77
78 while (!output_queue_.empty()) {
79 Message* m = output_queue_.front();
80 output_queue_.pop();
81 delete m;
82 }
83 }
84
85 bool Channel::ChannelImpl::Send(Message* message) {
86 DCHECK(thread_check_->CalledOnValidThread());
87 chrome::Counters::ipc_send_counter().Increment();
88 #ifdef IPC_MESSAGE_DEBUG_EXTRA
89 DLOG(INFO) << "sending message @" << message << " on channel @" << this
90 << " with type " << message->type()
91 << " (" << output_queue_.size() << " in queue)";
92 #endif
93
94 #ifdef IPC_MESSAGE_LOG_ENABLED
95 Logging::current()->OnSendMessage(message, "");
96 #endif
97
98 output_queue_.push(message);
99 // ensure waiting to write
100 if (!waiting_connect_) {
101 if (!output_state_.is_pending) {
102 if (!ProcessOutgoingMessages(NULL, 0))
103 return false;
104 }
105 }
106
107 return true;
108 }
109
110 const std::wstring Channel::ChannelImpl::PipeName(
111 const std::string& channel_id) const {
112 std::wostringstream ss;
113 // XXX(darin): get application name from somewhere else
114 ss << L"\\\\.\\pipe\\chrome." << ASCIIToWide(channel_id);
115 return ss.str();
116 }
117
118 bool Channel::ChannelImpl::CreatePipe(const std::string& channel_id,
119 Mode mode) {
120 DCHECK(pipe_ == INVALID_HANDLE_VALUE);
121 const std::wstring pipe_name = PipeName(channel_id);
122 if (mode == MODE_SERVER) {
123 SECURITY_ATTRIBUTES security_attributes = {0};
124 security_attributes.bInheritHandle = FALSE;
125 security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES);
126 if (!win_util::GetLogonSessionOnlyDACL(
127 reinterpret_cast<SECURITY_DESCRIPTOR**>(
128 &security_attributes.lpSecurityDescriptor))) {
129 NOTREACHED();
130 }
131
132 pipe_ = CreateNamedPipeW(pipe_name.c_str(),
133 PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED |
134 FILE_FLAG_FIRST_PIPE_INSTANCE,
135 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
136 1, // number of pipe instances
137 // output buffer size (XXX tune)
138 Channel::kReadBufferSize,
139 // input buffer size (XXX tune)
140 Channel::kReadBufferSize,
141 5000, // timeout in milliseconds (XXX tune)
142 &security_attributes);
143 LocalFree(security_attributes.lpSecurityDescriptor);
144 } else {
145 pipe_ = CreateFileW(pipe_name.c_str(),
146 GENERIC_READ | GENERIC_WRITE,
147 0,
148 NULL,
149 OPEN_EXISTING,
150 SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION |
151 FILE_FLAG_OVERLAPPED,
152 NULL);
153 }
154 if (pipe_ == INVALID_HANDLE_VALUE) {
155 // If this process is being closed, the pipe may be gone already.
156 LOG(WARNING) << "failed to create pipe: " << GetLastError();
157 return false;
158 }
159
160 // Create the Hello message to be sent when Connect is called
161 scoped_ptr<Message> m(new Message(MSG_ROUTING_NONE,
162 HELLO_MESSAGE_TYPE,
163 IPC::Message::PRIORITY_NORMAL));
164 if (!m->WriteInt(GetCurrentProcessId())) {
165 CloseHandle(pipe_);
166 pipe_ = INVALID_HANDLE_VALUE;
167 return false;
168 }
169
170 output_queue_.push(m.release());
171 return true;
172 }
173
174 bool Channel::ChannelImpl::Connect() {
175 DLOG(WARNING) << "Connect called twice";
176
177 if (!thread_check_.get())
178 thread_check_.reset(new NonThreadSafe());
179
180 if (pipe_ == INVALID_HANDLE_VALUE)
181 return false;
182
183 MessageLoopForIO::current()->RegisterIOHandler(pipe_, this);
184
185 // Check to see if there is a client connected to our pipe...
186 if (waiting_connect_)
187 ProcessConnection();
188
189 if (!input_state_.is_pending) {
190 // Complete setup asynchronously. By not setting input_state_.is_pending
191 // to true, we indicate to OnIOCompleted that this is the special
192 // initialization signal.
193 MessageLoopForIO::current()->PostTask(FROM_HERE, factory_.NewRunnableMethod(
194 &Channel::ChannelImpl::OnIOCompleted, &input_state_.context, 0, 0));
195 }
196
197 if (!waiting_connect_)
198 ProcessOutgoingMessages(NULL, 0);
199 return true;
200 }
201
202 bool Channel::ChannelImpl::ProcessConnection() {
203 DCHECK(thread_check_->CalledOnValidThread());
204 if (input_state_.is_pending)
205 input_state_.is_pending = false;
206
207 // Do we have a client connected to our pipe?
208 if (INVALID_HANDLE_VALUE == pipe_)
209 return false;
210
211 BOOL ok = ConnectNamedPipe(pipe_, &input_state_.context.overlapped);
212
213 DWORD err = GetLastError();
214 if (ok) {
215 // Uhm, the API documentation says that this function should never
216 // return success when used in overlapped mode.
217 NOTREACHED();
218 return false;
219 }
220
221 switch (err) {
222 case ERROR_IO_PENDING:
223 input_state_.is_pending = true;
224 break;
225 case ERROR_PIPE_CONNECTED:
226 waiting_connect_ = false;
227 break;
228 case ERROR_NO_DATA:
229 // The pipe is being closed.
230 return false;
231 default:
232 NOTREACHED();
233 return false;
234 }
235
236 return true;
237 }
238
239 bool Channel::ChannelImpl::ProcessIncomingMessages(
240 MessageLoopForIO::IOContext* context,
241 DWORD bytes_read) {
242 DCHECK(thread_check_->CalledOnValidThread());
243 if (input_state_.is_pending) {
244 input_state_.is_pending = false;
245 DCHECK(context);
246
247 if (!context || !bytes_read)
248 return false;
249 } else {
250 // This happens at channel initialization.
251 DCHECK(!bytes_read && context == &input_state_.context);
252 }
253
254 for (;;) {
255 if (bytes_read == 0) {
256 if (INVALID_HANDLE_VALUE == pipe_)
257 return false;
258
259 // Read from pipe...
260 BOOL ok = ReadFile(pipe_,
261 input_buf_,
262 Channel::kReadBufferSize,
263 &bytes_read,
264 &input_state_.context.overlapped);
265 if (!ok) {
266 DWORD err = GetLastError();
267 if (err == ERROR_IO_PENDING) {
268 input_state_.is_pending = true;
269 return true;
270 }
271 LOG(ERROR) << "pipe error: " << err;
272 return false;
273 }
274 input_state_.is_pending = true;
275 return true;
276 }
277 DCHECK(bytes_read);
278
279 // Process messages from input buffer.
280
281 const char* p, *end;
282 if (input_overflow_buf_.empty()) {
283 p = input_buf_;
284 end = p + bytes_read;
285 } else {
286 if (input_overflow_buf_.size() > (kMaximumMessageSize - bytes_read)) {
287 input_overflow_buf_.clear();
288 LOG(ERROR) << "IPC message is too big";
289 return false;
290 }
291 input_overflow_buf_.append(input_buf_, bytes_read);
292 p = input_overflow_buf_.data();
293 end = p + input_overflow_buf_.size();
294 }
295
296 while (p < end) {
297 const char* message_tail = Message::FindNext(p, end);
298 if (message_tail) {
299 int len = static_cast<int>(message_tail - p);
300 const Message m(p, len);
301 #ifdef IPC_MESSAGE_DEBUG_EXTRA
302 DLOG(INFO) << "received message on channel @" << this <<
303 " with type " << m.type();
304 #endif
305 if (m.routing_id() == MSG_ROUTING_NONE &&
306 m.type() == HELLO_MESSAGE_TYPE) {
307 // The Hello message contains only the process id.
308 listener_->OnChannelConnected(MessageIterator(m).NextInt());
309 } else {
310 listener_->OnMessageReceived(m);
311 }
312 p = message_tail;
313 } else {
314 // Last message is partial.
315 break;
316 }
317 }
318 input_overflow_buf_.assign(p, end - p);
319
320 bytes_read = 0; // Get more data.
321 }
322
323 return true;
324 }
325
326 bool Channel::ChannelImpl::ProcessOutgoingMessages(
327 MessageLoopForIO::IOContext* context,
328 DWORD bytes_written) {
329 DCHECK(!waiting_connect_); // Why are we trying to send messages if there's
330 // no connection?
331 DCHECK(thread_check_->CalledOnValidThread());
332
333 if (output_state_.is_pending) {
334 DCHECK(context);
335 output_state_.is_pending = false;
336 if (!context || bytes_written == 0) {
337 DWORD err = GetLastError();
338 LOG(ERROR) << "pipe error: " << err;
339 return false;
340 }
341 // Message was sent.
342 DCHECK(!output_queue_.empty());
343 Message* m = output_queue_.front();
344 output_queue_.pop();
345 delete m;
346 }
347
348 if (output_queue_.empty())
349 return true;
350
351 if (INVALID_HANDLE_VALUE == pipe_)
352 return false;
353
354 // Write to pipe...
355 Message* m = output_queue_.front();
356 BOOL ok = WriteFile(pipe_,
357 m->data(),
358 m->size(),
359 &bytes_written,
360 &output_state_.context.overlapped);
361 if (!ok) {
362 DWORD err = GetLastError();
363 if (err == ERROR_IO_PENDING) {
364 output_state_.is_pending = true;
365
366 #ifdef IPC_MESSAGE_DEBUG_EXTRA
367 DLOG(INFO) << "sent pending message @" << m << " on channel @" <<
368 this << " with type " << m->type();
369 #endif
370
371 return true;
372 }
373 LOG(ERROR) << "pipe error: " << err;
374 return false;
375 }
376
377 #ifdef IPC_MESSAGE_DEBUG_EXTRA
378 DLOG(INFO) << "sent message @" << m << " on channel @" << this <<
379 " with type " << m->type();
380 #endif
381
382 output_state_.is_pending = true;
383 return true;
384 }
385
386 void Channel::ChannelImpl::OnIOCompleted(MessageLoopForIO::IOContext* context,
387 DWORD bytes_transfered, DWORD error) {
388 bool ok;
389 DCHECK(thread_check_->CalledOnValidThread());
390 if (context == &input_state_.context) {
391 if (waiting_connect_) {
392 if (!ProcessConnection())
393 return;
394 // We may have some messages queued up to send...
395 if (!output_queue_.empty() && !output_state_.is_pending)
396 ProcessOutgoingMessages(NULL, 0);
397 if (input_state_.is_pending)
398 return;
399 // else, fall-through and look for incoming messages...
400 }
401 // we don't support recursion through OnMessageReceived yet!
402 DCHECK(!processing_incoming_);
403 processing_incoming_ = true;
404 ok = ProcessIncomingMessages(context, bytes_transfered);
405 processing_incoming_ = false;
406 } else {
407 DCHECK(context == &output_state_.context);
408 ok = ProcessOutgoingMessages(context, bytes_transfered);
409 }
410 if (!ok && INVALID_HANDLE_VALUE != pipe_) {
411 // We don't want to re-enter Close().
412 Close();
413 listener_->OnChannelError();
414 }
415 }
416
417 //------------------------------------------------------------------------------
418 // Channel's methods simply call through to ChannelImpl.
419 Channel::Channel(const std::string& channel_id, Mode mode,
420 Listener* listener)
421 : channel_impl_(new ChannelImpl(channel_id, mode, listener)) {
422 }
423
424 Channel::~Channel() {
425 delete channel_impl_;
426 }
427
428 bool Channel::Connect() {
429 return channel_impl_->Connect();
430 }
431
432 void Channel::Close() {
433 channel_impl_->Close();
434 }
435
436 void Channel::set_listener(Listener* listener) {
437 channel_impl_->set_listener(listener);
438 }
439
440 bool Channel::Send(Message* message) {
441 return channel_impl_->Send(message);
442 }
443
444 } // namespace IPC
OLDNEW
« no previous file with comments | « chrome/common/ipc_channel_win.h ('k') | chrome/common/ipc_fuzzing_tests.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698