ipc/chromium/src/chrome/common/ipc_channel_win.cc

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/ipc/chromium/src/chrome/common/ipc_channel_win.cc	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,510 @@
     1.4 +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
     1.5 +// Use of this source code is governed by a BSD-style license that can be
     1.6 +// found in the LICENSE file.
     1.7 +
     1.8 +#include "chrome/common/ipc_channel_win.h"
     1.9 +
    1.10 +#include <windows.h>
    1.11 +#include <sstream>
    1.12 +
    1.13 +#include "base/compiler_specific.h"
    1.14 +#include "base/logging.h"
    1.15 +#include "base/non_thread_safe.h"
    1.16 +#include "base/stats_counters.h"
    1.17 +#include "base/win_util.h"
    1.18 +#include "chrome/common/ipc_logging.h"
    1.19 +#include "chrome/common/ipc_message_utils.h"
    1.20 +#include "mozilla/ipc/ProtocolUtils.h"
    1.21 +
    1.22 +namespace IPC {
    1.23 +//------------------------------------------------------------------------------
    1.24 +
    1.25 +Channel::ChannelImpl::State::State(ChannelImpl* channel) : is_pending(false) {
    1.26 +  memset(&context.overlapped, 0, sizeof(context.overlapped));
    1.27 +  context.handler = channel;
    1.28 +}
    1.29 +
    1.30 +Channel::ChannelImpl::State::~State() {
    1.31 +  COMPILE_ASSERT(!offsetof(Channel::ChannelImpl::State, context),
    1.32 +                 starts_with_io_context);
    1.33 +}
    1.34 +
    1.35 +//------------------------------------------------------------------------------
    1.36 +
    1.37 +Channel::ChannelImpl::ChannelImpl(const std::wstring& channel_id, Mode mode,
    1.38 +                              Listener* listener)
    1.39 +    : ALLOW_THIS_IN_INITIALIZER_LIST(input_state_(this)),
    1.40 +      ALLOW_THIS_IN_INITIALIZER_LIST(output_state_(this)),
    1.41 +      ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)) {
    1.42 +  Init(mode, listener);
    1.43 +
    1.44 +  if (!CreatePipe(channel_id, mode)) {
    1.45 +    // The pipe may have been closed already.
    1.46 +    CHROMIUM_LOG(WARNING) << "Unable to create pipe named \"" << channel_id <<
    1.47 +                             "\" in " << (mode == 0 ? "server" : "client") << " mode.";
    1.48 +  }
    1.49 +}
    1.50 +
    1.51 +Channel::ChannelImpl::ChannelImpl(const std::wstring& channel_id,
    1.52 +                                  HANDLE server_pipe,
    1.53 +                                  Mode mode, Listener* listener)
    1.54 +    : ALLOW_THIS_IN_INITIALIZER_LIST(input_state_(this)),
    1.55 +      ALLOW_THIS_IN_INITIALIZER_LIST(output_state_(this)),
    1.56 +      ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)) {
    1.57 +  Init(mode, listener);
    1.58 +
    1.59 +  if (mode == MODE_SERVER) {
    1.60 +    // Use the existing handle that was dup'd to us
    1.61 +    pipe_ = server_pipe;
    1.62 +    EnqueueHelloMessage();
    1.63 +  } else {
    1.64 +    // Take the normal init path to connect to the server pipe
    1.65 +    CreatePipe(channel_id, mode);
    1.66 +  }
    1.67 +}
    1.68 +
    1.69 +void Channel::ChannelImpl::Init(Mode mode, Listener* listener) {
    1.70 +  pipe_ = INVALID_HANDLE_VALUE;
    1.71 +  listener_ = listener;
    1.72 +  waiting_connect_ = (mode == MODE_SERVER);
    1.73 +  processing_incoming_ = false;
    1.74 +  closed_ = false;
    1.75 +  output_queue_length_ = 0;
    1.76 +}
    1.77 +
    1.78 +void Channel::ChannelImpl::OutputQueuePush(Message* msg)
    1.79 +{
    1.80 +  output_queue_.push(msg);
    1.81 +  output_queue_length_++;
    1.82 +}
    1.83 +
    1.84 +void Channel::ChannelImpl::OutputQueuePop()
    1.85 +{
    1.86 +  output_queue_.pop();
    1.87 +  output_queue_length_--;
    1.88 +}
    1.89 +
    1.90 +HANDLE Channel::ChannelImpl::GetServerPipeHandle() const {
    1.91 +  return pipe_;
    1.92 +}
    1.93 +
    1.94 +void Channel::ChannelImpl::Close() {
    1.95 +  if (thread_check_.get()) {
    1.96 +    DCHECK(thread_check_->CalledOnValidThread());
    1.97 +  }
    1.98 +
    1.99 +  bool waited = false;
   1.100 +  if (input_state_.is_pending || output_state_.is_pending) {
   1.101 +    CancelIo(pipe_);
   1.102 +    waited = true;
   1.103 +  }
   1.104 +
   1.105 +  // Closing the handle at this point prevents us from issuing more requests
   1.106 +  // form OnIOCompleted().
   1.107 +  if (pipe_ != INVALID_HANDLE_VALUE) {
   1.108 +    CloseHandle(pipe_);
   1.109 +    pipe_ = INVALID_HANDLE_VALUE;
   1.110 +  }
   1.111 +
   1.112 +  while (input_state_.is_pending || output_state_.is_pending) {
   1.113 +    MessageLoopForIO::current()->WaitForIOCompletion(INFINITE, this);
   1.114 +  }
   1.115 +
   1.116 +  while (!output_queue_.empty()) {
   1.117 +    Message* m = output_queue_.front();
   1.118 +    OutputQueuePop();
   1.119 +    delete m;
   1.120 +  }
   1.121 +
   1.122 +  if (thread_check_.get())
   1.123 +    thread_check_.reset();
   1.124 +
   1.125 +  closed_ = true;
   1.126 +}
   1.127 +
   1.128 +bool Channel::ChannelImpl::Send(Message* message) {
   1.129 +  if (thread_check_.get()) {
   1.130 +    DCHECK(thread_check_->CalledOnValidThread());
   1.131 +  }
   1.132 +#ifdef IPC_MESSAGE_DEBUG_EXTRA
   1.133 +  DLOG(INFO) << "sending message @" << message << " on channel @" << this
   1.134 +             << " with type " << message->type()
   1.135 +             << " (" << output_queue_.size() << " in queue)";
   1.136 +#endif
   1.137 +
   1.138 +#ifdef IPC_MESSAGE_LOG_ENABLED
   1.139 +  Logging::current()->OnSendMessage(message, L"");
   1.140 +#endif
   1.141 +
   1.142 +  if (closed_) {
   1.143 +    if (mozilla::ipc::LoggingEnabled()) {
   1.144 +      fprintf(stderr, "Can't send message %s, because this channel is closed.\n",
   1.145 +              message->name());
   1.146 +    }
   1.147 +    delete message;
   1.148 +    return false;
   1.149 +  }
   1.150 +
   1.151 +  OutputQueuePush(message);
   1.152 +  // ensure waiting to write
   1.153 +  if (!waiting_connect_) {
   1.154 +    if (!output_state_.is_pending) {
   1.155 +      if (!ProcessOutgoingMessages(NULL, 0))
   1.156 +        return false;
   1.157 +    }
   1.158 +  }
   1.159 +
   1.160 +  return true;
   1.161 +}
   1.162 +
   1.163 +const std::wstring Channel::ChannelImpl::PipeName(
   1.164 +    const std::wstring& channel_id) const {
   1.165 +  std::wostringstream ss;
   1.166 +  // XXX(darin): get application name from somewhere else
   1.167 +  ss << L"\\\\.\\pipe\\chrome." << channel_id;
   1.168 +  return ss.str();
   1.169 +}
   1.170 +
   1.171 +bool Channel::ChannelImpl::CreatePipe(const std::wstring& channel_id,
   1.172 +                                      Mode mode) {
   1.173 +  DCHECK(pipe_ == INVALID_HANDLE_VALUE);
   1.174 +  const std::wstring pipe_name = PipeName(channel_id);
   1.175 +  if (mode == MODE_SERVER) {
   1.176 +    pipe_ = CreateNamedPipeW(pipe_name.c_str(),
   1.177 +                             PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED |
   1.178 +                                FILE_FLAG_FIRST_PIPE_INSTANCE,
   1.179 +                             PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
   1.180 +                             1,         // number of pipe instances
   1.181 +                             // output buffer size (XXX tune)
   1.182 +                             Channel::kReadBufferSize,
   1.183 +                             // input buffer size (XXX tune)
   1.184 +                             Channel::kReadBufferSize,
   1.185 +                             5000,      // timeout in milliseconds (XXX tune)
   1.186 +                             NULL);
   1.187 +  } else {
   1.188 +    pipe_ = CreateFileW(pipe_name.c_str(),
   1.189 +                        GENERIC_READ | GENERIC_WRITE,
   1.190 +                        0,
   1.191 +                        NULL,
   1.192 +                        OPEN_EXISTING,
   1.193 +                        SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION |
   1.194 +                            FILE_FLAG_OVERLAPPED,
   1.195 +                        NULL);
   1.196 +  }
   1.197 +  if (pipe_ == INVALID_HANDLE_VALUE) {
   1.198 +    // If this process is being closed, the pipe may be gone already.
   1.199 +    CHROMIUM_LOG(WARNING) << "failed to create pipe: " << GetLastError();
   1.200 +    return false;
   1.201 +  }
   1.202 +
   1.203 +  // Create the Hello message to be sent when Connect is called
   1.204 +  return EnqueueHelloMessage();
   1.205 +}
   1.206 +
   1.207 +bool Channel::ChannelImpl::EnqueueHelloMessage() {
   1.208 +  scoped_ptr<Message> m(new Message(MSG_ROUTING_NONE,
   1.209 +                                    HELLO_MESSAGE_TYPE,
   1.210 +                                    IPC::Message::PRIORITY_NORMAL));
   1.211 +  if (!m->WriteInt(GetCurrentProcessId())) {
   1.212 +    CloseHandle(pipe_);
   1.213 +    pipe_ = INVALID_HANDLE_VALUE;
   1.214 +    return false;
   1.215 +  }
   1.216 +
   1.217 +  OutputQueuePush(m.release());
   1.218 +  return true;
   1.219 +}
   1.220 +
   1.221 +bool Channel::ChannelImpl::Connect() {
   1.222 +  if (!thread_check_.get())
   1.223 +    thread_check_.reset(new NonThreadSafe());
   1.224 +
   1.225 +  if (pipe_ == INVALID_HANDLE_VALUE)
   1.226 +    return false;
   1.227 +
   1.228 +  MessageLoopForIO::current()->RegisterIOHandler(pipe_, this);
   1.229 +
   1.230 +  // Check to see if there is a client connected to our pipe...
   1.231 +  if (waiting_connect_)
   1.232 +    ProcessConnection();
   1.233 +
   1.234 +  if (!input_state_.is_pending) {
   1.235 +    // Complete setup asynchronously. By not setting input_state_.is_pending
   1.236 +    // to true, we indicate to OnIOCompleted that this is the special
   1.237 +    // initialization signal.
   1.238 +    MessageLoopForIO::current()->PostTask(FROM_HERE, factory_.NewRunnableMethod(
   1.239 +        &Channel::ChannelImpl::OnIOCompleted, &input_state_.context, 0, 0));
   1.240 +  }
   1.241 +
   1.242 +  if (!waiting_connect_)
   1.243 +    ProcessOutgoingMessages(NULL, 0);
   1.244 +  return true;
   1.245 +}
   1.246 +
   1.247 +bool Channel::ChannelImpl::ProcessConnection() {
   1.248 +  DCHECK(thread_check_->CalledOnValidThread());
   1.249 +  if (input_state_.is_pending)
   1.250 +    input_state_.is_pending = false;
   1.251 +
   1.252 +  // Do we have a client connected to our pipe?
   1.253 +  if (INVALID_HANDLE_VALUE == pipe_)
   1.254 +    return false;
   1.255 +
   1.256 +  BOOL ok = ConnectNamedPipe(pipe_, &input_state_.context.overlapped);
   1.257 +
   1.258 +  DWORD err = GetLastError();
   1.259 +  if (ok) {
   1.260 +    // Uhm, the API documentation says that this function should never
   1.261 +    // return success when used in overlapped mode.
   1.262 +    NOTREACHED();
   1.263 +    return false;
   1.264 +  }
   1.265 +
   1.266 +  switch (err) {
   1.267 +  case ERROR_IO_PENDING:
   1.268 +    input_state_.is_pending = true;
   1.269 +    break;
   1.270 +  case ERROR_PIPE_CONNECTED:
   1.271 +    waiting_connect_ = false;
   1.272 +    break;
   1.273 +  default:
   1.274 +    NOTREACHED();
   1.275 +    return false;
   1.276 +  }
   1.277 +
   1.278 +  return true;
   1.279 +}
   1.280 +
   1.281 +bool Channel::ChannelImpl::ProcessIncomingMessages(
   1.282 +    MessageLoopForIO::IOContext* context,
   1.283 +    DWORD bytes_read) {
   1.284 +  DCHECK(thread_check_->CalledOnValidThread());
   1.285 +  if (input_state_.is_pending) {
   1.286 +    input_state_.is_pending = false;
   1.287 +    DCHECK(context);
   1.288 +
   1.289 +    if (!context || !bytes_read)
   1.290 +      return false;
   1.291 +  } else {
   1.292 +    // This happens at channel initialization.
   1.293 +    DCHECK(!bytes_read && context == &input_state_.context);
   1.294 +  }
   1.295 +
   1.296 +  for (;;) {
   1.297 +    if (bytes_read == 0) {
   1.298 +      if (INVALID_HANDLE_VALUE == pipe_)
   1.299 +        return false;
   1.300 +
   1.301 +      // Read from pipe...
   1.302 +      BOOL ok = ReadFile(pipe_,
   1.303 +                         input_buf_,
   1.304 +                         Channel::kReadBufferSize,
   1.305 +                         &bytes_read,
   1.306 +                         &input_state_.context.overlapped);
   1.307 +      if (!ok) {
   1.308 +        DWORD err = GetLastError();
   1.309 +        if (err == ERROR_IO_PENDING) {
   1.310 +          input_state_.is_pending = true;
   1.311 +          return true;
   1.312 +        }
   1.313 +        CHROMIUM_LOG(ERROR) << "pipe error: " << err;
   1.314 +        return false;
   1.315 +      }
   1.316 +      input_state_.is_pending = true;
   1.317 +      return true;
   1.318 +    }
   1.319 +    DCHECK(bytes_read);
   1.320 +
   1.321 +    // Process messages from input buffer.
   1.322 +
   1.323 +    const char* p, *end;
   1.324 +    if (input_overflow_buf_.empty()) {
   1.325 +      p = input_buf_;
   1.326 +      end = p + bytes_read;
   1.327 +    } else {
   1.328 +      if (input_overflow_buf_.size() > (kMaximumMessageSize - bytes_read)) {
   1.329 +        input_overflow_buf_.clear();
   1.330 +        CHROMIUM_LOG(ERROR) << "IPC message is too big";
   1.331 +        return false;
   1.332 +      }
   1.333 +      input_overflow_buf_.append(input_buf_, bytes_read);
   1.334 +      p = input_overflow_buf_.data();
   1.335 +      end = p + input_overflow_buf_.size();
   1.336 +    }
   1.337 +
   1.338 +    while (p < end) {
   1.339 +      const char* message_tail = Message::FindNext(p, end);
   1.340 +      if (message_tail) {
   1.341 +        int len = static_cast<int>(message_tail - p);
   1.342 +        const Message m(p, len);
   1.343 +#ifdef IPC_MESSAGE_DEBUG_EXTRA
   1.344 +        DLOG(INFO) << "received message on channel @" << this <<
   1.345 +                      " with type " << m.type();
   1.346 +#endif
   1.347 +        if (m.routing_id() == MSG_ROUTING_NONE &&
   1.348 +            m.type() == HELLO_MESSAGE_TYPE) {
   1.349 +          // The Hello message contains only the process id.
   1.350 +          listener_->OnChannelConnected(MessageIterator(m).NextInt());
   1.351 +        } else {
   1.352 +          listener_->OnMessageReceived(m);
   1.353 +        }
   1.354 +        p = message_tail;
   1.355 +      } else {
   1.356 +        // Last message is partial.
   1.357 +        break;
   1.358 +      }
   1.359 +    }
   1.360 +    input_overflow_buf_.assign(p, end - p);
   1.361 +
   1.362 +    bytes_read = 0;  // Get more data.
   1.363 +  }
   1.364 +
   1.365 +  return true;
   1.366 +}
   1.367 +
   1.368 +bool Channel::ChannelImpl::ProcessOutgoingMessages(
   1.369 +    MessageLoopForIO::IOContext* context,
   1.370 +    DWORD bytes_written) {
   1.371 +  DCHECK(!waiting_connect_);  // Why are we trying to send messages if there's
   1.372 +                              // no connection?
   1.373 +  DCHECK(thread_check_->CalledOnValidThread());
   1.374 +
   1.375 +  if (output_state_.is_pending) {
   1.376 +    DCHECK(context);
   1.377 +    output_state_.is_pending = false;
   1.378 +    if (!context || bytes_written == 0) {
   1.379 +      DWORD err = GetLastError();
   1.380 +      CHROMIUM_LOG(ERROR) << "pipe error: " << err;
   1.381 +      return false;
   1.382 +    }
   1.383 +    // Message was sent.
   1.384 +    DCHECK(!output_queue_.empty());
   1.385 +    Message* m = output_queue_.front();
   1.386 +    OutputQueuePop();
   1.387 +    delete m;
   1.388 +  }
   1.389 +
   1.390 +  if (output_queue_.empty())
   1.391 +    return true;
   1.392 +
   1.393 +  if (INVALID_HANDLE_VALUE == pipe_)
   1.394 +    return false;
   1.395 +
   1.396 +  // Write to pipe...
   1.397 +  Message* m = output_queue_.front();
   1.398 +  BOOL ok = WriteFile(pipe_,
   1.399 +                      m->data(),
   1.400 +                      m->size(),
   1.401 +                      &bytes_written,
   1.402 +                      &output_state_.context.overlapped);
   1.403 +  if (!ok) {
   1.404 +    DWORD err = GetLastError();
   1.405 +    if (err == ERROR_IO_PENDING) {
   1.406 +      output_state_.is_pending = true;
   1.407 +
   1.408 +#ifdef IPC_MESSAGE_DEBUG_EXTRA
   1.409 +      DLOG(INFO) << "sent pending message @" << m << " on channel @" <<
   1.410 +                    this << " with type " << m->type();
   1.411 +#endif
   1.412 +
   1.413 +      return true;
   1.414 +    }
   1.415 +    CHROMIUM_LOG(ERROR) << "pipe error: " << err;
   1.416 +    return false;
   1.417 +  }
   1.418 +
   1.419 +#ifdef IPC_MESSAGE_DEBUG_EXTRA
   1.420 +  DLOG(INFO) << "sent message @" << m << " on channel @" << this <<
   1.421 +                " with type " << m->type();
   1.422 +#endif
   1.423 +
   1.424 +  output_state_.is_pending = true;
   1.425 +  return true;
   1.426 +}
   1.427 +
   1.428 +void Channel::ChannelImpl::OnIOCompleted(MessageLoopForIO::IOContext* context,
   1.429 +                            DWORD bytes_transfered, DWORD error) {
   1.430 +  bool ok;
   1.431 +  DCHECK(thread_check_->CalledOnValidThread());
   1.432 +  if (context == &input_state_.context) {
   1.433 +    if (waiting_connect_) {
   1.434 +      if (!ProcessConnection())
   1.435 +        return;
   1.436 +      // We may have some messages queued up to send...
   1.437 +      if (!output_queue_.empty() && !output_state_.is_pending)
   1.438 +        ProcessOutgoingMessages(NULL, 0);
   1.439 +      if (input_state_.is_pending)
   1.440 +        return;
   1.441 +      // else, fall-through and look for incoming messages...
   1.442 +    }
   1.443 +    // we don't support recursion through OnMessageReceived yet!
   1.444 +    DCHECK(!processing_incoming_);
   1.445 +    processing_incoming_ = true;
   1.446 +    ok = ProcessIncomingMessages(context, bytes_transfered);
   1.447 +    processing_incoming_ = false;
   1.448 +  } else {
   1.449 +    DCHECK(context == &output_state_.context);
   1.450 +    ok = ProcessOutgoingMessages(context, bytes_transfered);
   1.451 +  }
   1.452 +  if (!ok && INVALID_HANDLE_VALUE != pipe_) {
   1.453 +    // We don't want to re-enter Close().
   1.454 +    Close();
   1.455 +    listener_->OnChannelError();
   1.456 +  }
   1.457 +}
   1.458 +
   1.459 +bool Channel::ChannelImpl::Unsound_IsClosed() const
   1.460 +{
   1.461 +  return closed_;
   1.462 +}
   1.463 +
   1.464 +uint32_t Channel::ChannelImpl::Unsound_NumQueuedMessages() const
   1.465 +{
   1.466 +  return output_queue_length_;
   1.467 +}
   1.468 +
   1.469 +//------------------------------------------------------------------------------
   1.470 +// Channel's methods simply call through to ChannelImpl.
   1.471 +Channel::Channel(const std::wstring& channel_id, Mode mode,
   1.472 +                 Listener* listener)
   1.473 +    : channel_impl_(new ChannelImpl(channel_id, mode, listener)) {
   1.474 +}
   1.475 +
   1.476 +Channel::Channel(const std::wstring& channel_id, void* server_pipe,
   1.477 +                 Mode mode, Listener* listener)
   1.478 +   : channel_impl_(new ChannelImpl(channel_id, server_pipe, mode, listener)) {
   1.479 +}
   1.480 +
   1.481 +Channel::~Channel() {
   1.482 +  delete channel_impl_;
   1.483 +}
   1.484 +
   1.485 +bool Channel::Connect() {
   1.486 +  return channel_impl_->Connect();
   1.487 +}
   1.488 +
   1.489 +void Channel::Close() {
   1.490 +  channel_impl_->Close();
   1.491 +}
   1.492 +
   1.493 +void* Channel::GetServerPipeHandle() const {
   1.494 +  return channel_impl_->GetServerPipeHandle();
   1.495 +}
   1.496 +
   1.497 +Channel::Listener* Channel::set_listener(Listener* listener) {
   1.498 +  return channel_impl_->set_listener(listener);
   1.499 +}
   1.500 +
   1.501 +bool Channel::Send(Message* message) {
   1.502 +  return channel_impl_->Send(message);
   1.503 +}
   1.504 +
   1.505 +bool Channel::Unsound_IsClosed() const {
   1.506 +  return channel_impl_->Unsound_IsClosed();
   1.507 +}
   1.508 +
   1.509 +uint32_t Channel::Unsound_NumQueuedMessages() const {
   1.510 +  return channel_impl_->Unsound_NumQueuedMessages();
   1.511 +}
   1.512 +
   1.513 +}  // namespace IPC

mercurial