1 // Copyright (c) 2009 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.
5 #include "ipc/ipc_channel_win.h"
10 #include "base/auto_reset.h"
11 #include "base/compiler_specific.h"
12 #include "base/logging.h"
13 #include "base/non_thread_safe.h"
14 #include "base/stats_counters.h"
15 #include "base/win_util.h"
16 #include "ipc/ipc_logging.h"
17 #include "ipc/ipc_message_utils.h"
20 //------------------------------------------------------------------------------
22 Channel::ChannelImpl::State::State(ChannelImpl
* channel
) : is_pending(false) {
23 memset(&context
.overlapped
, 0, sizeof(context
.overlapped
));
24 context
.handler
= channel
;
27 Channel::ChannelImpl::State::~State() {
28 COMPILE_ASSERT(!offsetof(Channel::ChannelImpl::State
, context
),
29 starts_with_io_context
);
32 //------------------------------------------------------------------------------
34 Channel::ChannelImpl::ChannelImpl(const std::string
& channel_id
, Mode mode
,
36 : ALLOW_THIS_IN_INITIALIZER_LIST(input_state_(this)),
37 ALLOW_THIS_IN_INITIALIZER_LIST(output_state_(this)),
38 pipe_(INVALID_HANDLE_VALUE
),
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.";
50 void Channel::ChannelImpl::Close() {
51 if (thread_check_
.get()) {
52 DCHECK(thread_check_
->CalledOnValidThread());
55 if (input_state_
.is_pending
|| output_state_
.is_pending
)
58 // Closing the handle at this point prevents us from issuing more requests
59 // form OnIOCompleted().
60 if (pipe_
!= INVALID_HANDLE_VALUE
) {
62 pipe_
= INVALID_HANDLE_VALUE
;
65 // Make sure all IO has completed.
66 base::Time start
= base::Time::Now();
67 while (input_state_
.is_pending
|| output_state_
.is_pending
) {
68 MessageLoopForIO::current()->WaitForIOCompletion(INFINITE
, this);
71 while (!output_queue_
.empty()) {
72 Message
* m
= output_queue_
.front();
78 bool Channel::ChannelImpl::Send(Message
* message
) {
79 DCHECK(thread_check_
->CalledOnValidThread());
80 #ifdef IPC_MESSAGE_DEBUG_EXTRA
81 DLOG(INFO
) << "sending message @" << message
<< " on channel @" << this
82 << " with type " << message
->type()
83 << " (" << output_queue_
.size() << " in queue)";
86 #ifdef IPC_MESSAGE_LOG_ENABLED
87 Logging::current()->OnSendMessage(message
, "");
90 output_queue_
.push(message
);
91 // ensure waiting to write
92 if (!waiting_connect_
) {
93 if (!output_state_
.is_pending
) {
94 if (!ProcessOutgoingMessages(NULL
, 0))
102 const std::wstring
Channel::ChannelImpl::PipeName(
103 const std::string
& channel_id
) const {
104 std::wostringstream ss
;
105 // XXX(darin): get application name from somewhere else
106 ss
<< L
"\\\\.\\pipe\\chrome." << ASCIIToWide(channel_id
);
110 bool Channel::ChannelImpl::CreatePipe(const std::string
& channel_id
,
112 DCHECK(pipe_
== INVALID_HANDLE_VALUE
);
113 const std::wstring pipe_name
= PipeName(channel_id
);
114 if (mode
== MODE_SERVER
) {
115 SECURITY_ATTRIBUTES security_attributes
= {0};
116 security_attributes
.bInheritHandle
= FALSE
;
117 security_attributes
.nLength
= sizeof(SECURITY_ATTRIBUTES
);
118 if (!win_util::GetLogonSessionOnlyDACL(
119 reinterpret_cast<SECURITY_DESCRIPTOR
**>(
120 &security_attributes
.lpSecurityDescriptor
))) {
124 pipe_
= CreateNamedPipeW(pipe_name
.c_str(),
125 PIPE_ACCESS_DUPLEX
| FILE_FLAG_OVERLAPPED
|
126 FILE_FLAG_FIRST_PIPE_INSTANCE
,
127 PIPE_TYPE_BYTE
| PIPE_READMODE_BYTE
,
128 1, // number of pipe instances
129 // output buffer size (XXX tune)
130 Channel::kReadBufferSize
,
131 // input buffer size (XXX tune)
132 Channel::kReadBufferSize
,
133 5000, // timeout in milliseconds (XXX tune)
134 &security_attributes
);
135 LocalFree(security_attributes
.lpSecurityDescriptor
);
137 pipe_
= CreateFileW(pipe_name
.c_str(),
138 GENERIC_READ
| GENERIC_WRITE
,
142 SECURITY_SQOS_PRESENT
| SECURITY_IDENTIFICATION
|
143 FILE_FLAG_OVERLAPPED
,
146 if (pipe_
== INVALID_HANDLE_VALUE
) {
147 // If this process is being closed, the pipe may be gone already.
148 LOG(WARNING
) << "failed to create pipe: " << GetLastError();
152 // Create the Hello message to be sent when Connect is called
153 scoped_ptr
<Message
> m(new Message(MSG_ROUTING_NONE
,
155 IPC::Message::PRIORITY_NORMAL
));
156 if (!m
->WriteInt(GetCurrentProcessId())) {
158 pipe_
= INVALID_HANDLE_VALUE
;
162 output_queue_
.push(m
.release());
166 bool Channel::ChannelImpl::Connect() {
167 DLOG_IF(WARNING
, thread_check_
.get()) << "Connect called more than once";
169 if (!thread_check_
.get())
170 thread_check_
.reset(new NonThreadSafe());
172 if (pipe_
== INVALID_HANDLE_VALUE
)
175 MessageLoopForIO::current()->RegisterIOHandler(pipe_
, this);
177 // Check to see if there is a client connected to our pipe...
178 if (waiting_connect_
)
181 if (!input_state_
.is_pending
) {
182 // Complete setup asynchronously. By not setting input_state_.is_pending
183 // to true, we indicate to OnIOCompleted that this is the special
184 // initialization signal.
185 MessageLoopForIO::current()->PostTask(FROM_HERE
, factory_
.NewRunnableMethod(
186 &Channel::ChannelImpl::OnIOCompleted
, &input_state_
.context
, 0, 0));
189 if (!waiting_connect_
)
190 ProcessOutgoingMessages(NULL
, 0);
194 bool Channel::ChannelImpl::ProcessConnection() {
195 DCHECK(thread_check_
->CalledOnValidThread());
196 if (input_state_
.is_pending
)
197 input_state_
.is_pending
= false;
199 // Do we have a client connected to our pipe?
200 if (INVALID_HANDLE_VALUE
== pipe_
)
203 BOOL ok
= ConnectNamedPipe(pipe_
, &input_state_
.context
.overlapped
);
205 DWORD err
= GetLastError();
207 // Uhm, the API documentation says that this function should never
208 // return success when used in overlapped mode.
214 case ERROR_IO_PENDING
:
215 input_state_
.is_pending
= true;
217 case ERROR_PIPE_CONNECTED
:
218 waiting_connect_
= false;
221 // The pipe is being closed.
231 bool Channel::ChannelImpl::ProcessIncomingMessages(
232 MessageLoopForIO::IOContext
* context
,
234 DCHECK(thread_check_
->CalledOnValidThread());
235 if (input_state_
.is_pending
) {
236 input_state_
.is_pending
= false;
239 if (!context
|| !bytes_read
)
242 // This happens at channel initialization.
243 DCHECK(!bytes_read
&& context
== &input_state_
.context
);
247 if (bytes_read
== 0) {
248 if (INVALID_HANDLE_VALUE
== pipe_
)
252 BOOL ok
= ReadFile(pipe_
,
254 Channel::kReadBufferSize
,
256 &input_state_
.context
.overlapped
);
258 DWORD err
= GetLastError();
259 if (err
== ERROR_IO_PENDING
) {
260 input_state_
.is_pending
= true;
263 LOG(ERROR
) << "pipe error: " << err
;
266 input_state_
.is_pending
= true;
271 // Process messages from input buffer.
274 if (input_overflow_buf_
.empty()) {
276 end
= p
+ bytes_read
;
278 if (input_overflow_buf_
.size() > (kMaximumMessageSize
- bytes_read
)) {
279 input_overflow_buf_
.clear();
280 LOG(ERROR
) << "IPC message is too big";
283 input_overflow_buf_
.append(input_buf_
, bytes_read
);
284 p
= input_overflow_buf_
.data();
285 end
= p
+ input_overflow_buf_
.size();
289 const char* message_tail
= Message::FindNext(p
, end
);
291 int len
= static_cast<int>(message_tail
- p
);
292 const Message
m(p
, len
);
293 #ifdef IPC_MESSAGE_DEBUG_EXTRA
294 DLOG(INFO
) << "received message on channel @" << this <<
295 " with type " << m
.type();
297 if (m
.routing_id() == MSG_ROUTING_NONE
&&
298 m
.type() == HELLO_MESSAGE_TYPE
) {
299 // The Hello message contains only the process id.
300 listener_
->OnChannelConnected(MessageIterator(m
).NextInt());
302 listener_
->OnMessageReceived(m
);
306 // Last message is partial.
310 input_overflow_buf_
.assign(p
, end
- p
);
312 bytes_read
= 0; // Get more data.
318 bool Channel::ChannelImpl::ProcessOutgoingMessages(
319 MessageLoopForIO::IOContext
* context
,
320 DWORD bytes_written
) {
321 DCHECK(!waiting_connect_
); // Why are we trying to send messages if there's
323 DCHECK(thread_check_
->CalledOnValidThread());
325 if (output_state_
.is_pending
) {
327 output_state_
.is_pending
= false;
328 if (!context
|| bytes_written
== 0) {
329 DWORD err
= GetLastError();
330 LOG(ERROR
) << "pipe error: " << err
;
334 DCHECK(!output_queue_
.empty());
335 Message
* m
= output_queue_
.front();
340 if (output_queue_
.empty())
343 if (INVALID_HANDLE_VALUE
== pipe_
)
347 Message
* m
= output_queue_
.front();
348 BOOL ok
= WriteFile(pipe_
,
352 &output_state_
.context
.overlapped
);
354 DWORD err
= GetLastError();
355 if (err
== ERROR_IO_PENDING
) {
356 output_state_
.is_pending
= true;
358 #ifdef IPC_MESSAGE_DEBUG_EXTRA
359 DLOG(INFO
) << "sent pending message @" << m
<< " on channel @" <<
360 this << " with type " << m
->type();
365 LOG(ERROR
) << "pipe error: " << err
;
369 #ifdef IPC_MESSAGE_DEBUG_EXTRA
370 DLOG(INFO
) << "sent message @" << m
<< " on channel @" << this <<
371 " with type " << m
->type();
374 output_state_
.is_pending
= true;
378 void Channel::ChannelImpl::OnIOCompleted(MessageLoopForIO::IOContext
* context
,
379 DWORD bytes_transfered
, DWORD error
) {
381 DCHECK(thread_check_
->CalledOnValidThread());
382 if (context
== &input_state_
.context
) {
383 if (waiting_connect_
) {
384 if (!ProcessConnection())
386 // We may have some messages queued up to send...
387 if (!output_queue_
.empty() && !output_state_
.is_pending
)
388 ProcessOutgoingMessages(NULL
, 0);
389 if (input_state_
.is_pending
)
391 // else, fall-through and look for incoming messages...
393 // we don't support recursion through OnMessageReceived yet!
394 DCHECK(!processing_incoming_
);
395 AutoReset
auto_reset_processing_incoming(&processing_incoming_
, true);
396 ok
= ProcessIncomingMessages(context
, bytes_transfered
);
398 DCHECK(context
== &output_state_
.context
);
399 ok
= ProcessOutgoingMessages(context
, bytes_transfered
);
401 if (!ok
&& INVALID_HANDLE_VALUE
!= pipe_
) {
402 // We don't want to re-enter Close().
404 listener_
->OnChannelError();
408 //------------------------------------------------------------------------------
409 // Channel's methods simply call through to ChannelImpl.
410 Channel::Channel(const std::string
& channel_id
, Mode mode
,
412 : channel_impl_(new ChannelImpl(channel_id
, mode
, listener
)) {
415 Channel::~Channel() {
416 delete channel_impl_
;
419 bool Channel::Connect() {
420 return channel_impl_
->Connect();
423 void Channel::Close() {
424 channel_impl_
->Close();
427 void Channel::set_listener(Listener
* listener
) {
428 channel_impl_
->set_listener(listener
);
431 bool Channel::Send(Message
* message
) {
432 return channel_impl_
->Send(message
);