OLD | NEW |
| (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 | |
OLD | NEW |