Bug 564086, part j: Add IPC::Channel support for getting OS-level pipe info and creating from existing pipe descriptors. r=bent
authorChris Jones <jones.chris.g@gmail.com>
Fri, 03 Jun 2011 13:33:55 -0500
changeset 70804 fed06cc42545f058b09c993ac7f48af23c2737b4
parent 70803 76bdbaa76db4cbee82c7f86620cc42569406a304
child 70805 55159c6e44cb285217fb55a73617d6883a31cb73
push idunknown
push userunknown
push dateunknown
reviewersbent
bugs564086
milestone7.0a1
Bug 564086, part j: Add IPC::Channel support for getting OS-level pipe info and creating from existing pipe descriptors. r=bent
ipc/chromium/src/chrome/common/ipc_channel.h
ipc/chromium/src/chrome/common/ipc_channel_posix.cc
ipc/chromium/src/chrome/common/ipc_channel_posix.h
ipc/chromium/src/chrome/common/ipc_channel_win.cc
ipc/chromium/src/chrome/common/ipc_channel_win.h
--- a/ipc/chromium/src/chrome/common/ipc_channel.h
+++ b/ipc/chromium/src/chrome/common/ipc_channel.h
@@ -54,16 +54,30 @@ class Channel : public Message::Sender {
   // client mode.  In server mode, the Channel is responsible for setting up the
   // IPC object, whereas in client mode, the Channel merely connects to the
   // already established IPC object.
   // |listener| receives a callback on the current thread for each newly
   // received message.
   //
   Channel(const std::wstring& channel_id, Mode mode, Listener* listener);
 
+#if defined(CHROMIUM_MOZILLA_BUILD)
+  // XXX it would nice not to have yet more platform-specific code in
+  // here but it's just not worth the trouble.
+# if defined(OS_POSIX)
+  // Connect to a pre-created channel |fd| as |mode|.
+  Channel(int fd, Mode mode, Listener* listener);
+# elif defined(OS_WIN)
+  // Connect to a pre-created channel as |mode|.  Clients connect to
+  // the pre-existing server pipe, and servers take over |server_pipe|.
+  Channel(const std::wstring& channel_id, void* server_pipe,
+	  Mode mode, Listener* listener);
+# endif
+#endif
+
   ~Channel();
 
   // Connect the pipe.  On the server side, this will initiate
   // waiting for connections.  On the client, it attempts to
   // connect to a pre-existing pipe.  Note, calling Connect()
   // will not block the calling thread and may complete
   // asynchronously.
   bool Connect();
@@ -94,16 +108,26 @@ class Channel : public Message::Sender {
   // FD # for the client end of the socket and the equivalent FD# to use for
   // mapping it into the Child process.
   // This method may only be called on the server side of a channel.
   //
   // If the kTestingChannelID flag is specified on the command line then
   // a named FIFO is used as the channel transport mechanism rather than a
   // socketpair() in which case this method returns -1 for both parameters.
   void GetClientFileDescriptorMapping(int *src_fd, int *dest_fd) const;
+
+# if defined(CHROMIUM_MOZILLA_BUILD)
+  // Return the server side of the socketpair.
+  int GetServerFileDescriptor() const;
+# endif
+#elif defined(OS_WIN)
+# if defined(CHROMIUM_MOZILLA_BUILD)
+  // Return the server pipe handle.
+  void* GetServerPipeHandle() const;
+# endif
 #endif  // defined(OS_POSIX)
 
  private:
   // PIMPL to which all channel calls are delegated.
   class ChannelImpl;
   ChannelImpl *channel_impl_;
 
   // The Hello message is internal to the Channel class.  It is sent
--- a/ipc/chromium/src/chrome/common/ipc_channel_posix.cc
+++ b/ipc/chromium/src/chrome/common/ipc_channel_posix.cc
@@ -252,36 +252,50 @@ bool SetCloseOnExec(int fd) {
 }
 #endif
 
 }  // namespace
 //------------------------------------------------------------------------------
 
 Channel::ChannelImpl::ChannelImpl(const std::wstring& channel_id, Mode mode,
                                   Listener* listener)
-    : mode_(mode),
-      is_blocked_on_write_(false),
-      message_send_bytes_written_(0),
-      uses_fifo_(CommandLine::ForCurrentProcess()->HasSwitch(
-                     switches::kIPCUseFIFO)),
-      server_listen_pipe_(-1),
-      pipe_(-1),
-      client_pipe_(-1),
-      listener_(listener),
-      waiting_connect_(true),
-      processing_incoming_(false),
-      factory_(this) {
+    : factory_(this) {
+  Init(mode, listener);
+  uses_fifo_ = CommandLine::ForCurrentProcess()->HasSwitch(switches::kIPCUseFIFO);
+
   if (!CreatePipe(channel_id, mode)) {
     // The pipe may have been closed already.
     LOG(WARNING) << "Unable to create pipe named \"" << channel_id <<
                     "\" in " << (mode == MODE_SERVER ? "server" : "client") <<
                     " mode error(" << strerror(errno) << ").";
   }
 }
 
+Channel::ChannelImpl::ChannelImpl(int fd, Mode mode, Listener* listener)
+    : factory_(this) {
+  Init(mode, listener);
+  pipe_ = fd;
+  waiting_connect_ = (MODE_SERVER == mode);
+
+  EnqueueHelloMessage();
+}
+
+void Channel::ChannelImpl::Init(Mode mode, Listener* listener) {
+  mode_ = mode;
+  is_blocked_on_write_ = false;
+  message_send_bytes_written_ = 0;
+  uses_fifo_ = false;
+  server_listen_pipe_ = -1;
+  pipe_ = -1;
+  client_pipe_ = -1;
+  listener_ = listener;
+  waiting_connect_ = true;
+  processing_incoming_ = false;
+}
+
 bool Channel::ChannelImpl::CreatePipe(const std::wstring& channel_id,
                                       Mode mode) {
   DCHECK(server_listen_pipe_ == -1 && pipe_ == -1);
 
   if (uses_fifo_) {
     // This only happens in unit tests; see the comment above PipeMap.
     // TODO(playmobil): We shouldn't need to create fifos on disk.
     // TODO(playmobil): If we do, they should be in the user data directory.
@@ -329,16 +343,20 @@ bool Channel::ChannelImpl::CreatePipe(co
     } else {
       pipe_ = ChannelNameToClientFD(pipe_name_);
       DCHECK(pipe_ > 0);
       waiting_connect_ = false;
     }
   }
 
   // Create the Hello message to be sent when Connect is called
+  return EnqueueHelloMessage();
+}
+
+bool Channel::ChannelImpl::EnqueueHelloMessage() {
   scoped_ptr<Message> msg(new Message(MSG_ROUTING_NONE,
                                       HELLO_MESSAGE_TYPE,
                                       IPC::Message::PRIORITY_NORMAL));
   if (!msg->WriteInt(base::GetCurrentProcId())) {
     Close();
     return false;
   }
 
@@ -791,16 +809,22 @@ void Channel::ChannelImpl::Close() {
 
 //------------------------------------------------------------------------------
 // Channel's methods simply call through to ChannelImpl.
 Channel::Channel(const std::wstring& channel_id, Mode mode,
                  Listener* listener)
     : channel_impl_(new ChannelImpl(channel_id, mode, listener)) {
 }
 
+#if defined(CHROMIUM_MOZILLA_BUILD)
+Channel::Channel(int fd, Mode mode, Listener* listener)
+    : channel_impl_(new ChannelImpl(fd, mode, listener)) {
+}
+#endif
+
 Channel::~Channel() {
   delete channel_impl_;
 }
 
 bool Channel::Connect() {
   return channel_impl_->Connect();
 }
 
@@ -821,9 +845,15 @@ void Channel::set_listener(Listener* lis
 bool Channel::Send(Message* message) {
   return channel_impl_->Send(message);
 }
 
 void Channel::GetClientFileDescriptorMapping(int *src_fd, int *dest_fd) const {
   return channel_impl_->GetClientFileDescriptorMapping(src_fd, dest_fd);
 }
 
+#ifdef CHROMIUM_MOZILLA_BUILD
+int Channel::GetServerFileDescriptor() const {
+  return channel_impl_->GetServerFileDescriptor();
+}
+#endif
+
 }  // namespace IPC
--- a/ipc/chromium/src/chrome/common/ipc_channel_posix.h
+++ b/ipc/chromium/src/chrome/common/ipc_channel_posix.h
@@ -19,33 +19,42 @@
 namespace IPC {
 
 // An implementation of ChannelImpl for POSIX systems that works via
 // socketpairs.  See the .cc file for an overview of the implementation.
 class Channel::ChannelImpl : public MessageLoopForIO::Watcher {
  public:
   // Mirror methods of Channel, see ipc_channel.h for description.
   ChannelImpl(const std::wstring& channel_id, Mode mode, Listener* listener);
+  ChannelImpl(int fd, Mode mode, Listener* listener);
   ~ChannelImpl() { Close(); }
   bool Connect();
   void Close();
 #ifdef CHROMIUM_MOZILLA_BUILD
   Listener* set_listener(Listener* listener) {
     Listener* old = listener_;
     listener_ = listener;
     return old;
   }
 #else
   void set_listener(Listener* listener) { listener_ = listener; }
 #endif
   bool Send(Message* message);
   void GetClientFileDescriptorMapping(int *src_fd, int *dest_fd) const;
+#ifdef CHROMIUM_MOZILLA_BUILD
+  int GetServerFileDescriptor() const {
+    DCHECK(mode_ == MODE_SERVER);
+    return pipe_;
+  }
+#endif
 
  private:
+  void Init(Mode mode, Listener* listener);
   bool CreatePipe(const std::wstring& channel_id, Mode mode);
+  bool EnqueueHelloMessage();
 
   bool ProcessIncomingMessages();
   bool ProcessOutgoingMessages();
 
   // MessageLoopForIO::Watcher implementation.
   virtual void OnFileCanReadWithoutBlocking(int fd);
   virtual void OnFileCanWriteWithoutBlocking(int fd);
 
--- a/ipc/chromium/src/chrome/common/ipc_channel_win.cc
+++ b/ipc/chromium/src/chrome/common/ipc_channel_win.cc
@@ -30,28 +30,57 @@ Channel::ChannelImpl::State::~State() {
 }
 
 //------------------------------------------------------------------------------
 
 Channel::ChannelImpl::ChannelImpl(const std::wstring& channel_id, Mode mode,
                               Listener* listener)
     : ALLOW_THIS_IN_INITIALIZER_LIST(input_state_(this)),
       ALLOW_THIS_IN_INITIALIZER_LIST(output_state_(this)),
-      pipe_(INVALID_HANDLE_VALUE),
-      listener_(listener),
-      waiting_connect_(mode == MODE_SERVER),
-      processing_incoming_(false),
       ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)) {
+  Init(mode, listener);
+
   if (!CreatePipe(channel_id, mode)) {
     // The pipe may have been closed already.
     LOG(WARNING) << "Unable to create pipe named \"" << channel_id <<
                     "\" in " << (mode == 0 ? "server" : "client") << " mode.";
   }
 }
 
+#if defined(CHROMIUM_MOZILLA_BUILD)
+Channel::ChannelImpl::ChannelImpl(const std::wstring& channel_id,
+                                  HANDLE server_pipe,
+                                  Mode mode, Listener* listener)
+    : ALLOW_THIS_IN_INITIALIZER_LIST(input_state_(this)),
+      ALLOW_THIS_IN_INITIALIZER_LIST(output_state_(this)),
+      ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)) {
+  Init(mode, listener);
+
+  if (mode == MODE_SERVER) {
+    // Use the existing handle that was dup'd to us
+    pipe_ = server_pipe;
+    EnqueueHelloMessage();
+  } else {
+    // Take the normal init path to connect to the server pipe
+    CreatePipe(channel_id, mode);
+  }
+}
+
+void Channel::ChannelImpl::Init(Mode mode, Listener* listener) {
+  pipe_ = INVALID_HANDLE_VALUE;
+  listener_ = listener;
+  waiting_connect_ = (mode == MODE_SERVER);
+  processing_incoming_ = false;
+}
+
+HANDLE Channel::ChannelImpl::GetServerPipeHandle() const {
+  return pipe_;
+}
+#endif
+
 void Channel::ChannelImpl::Close() {
   if (thread_check_.get()) {
     DCHECK(thread_check_->CalledOnValidThread());
   }
 
   bool waited = false;
   if (input_state_.is_pending || output_state_.is_pending) {
     CancelIo(pipe_);
@@ -159,16 +188,20 @@ bool Channel::ChannelImpl::CreatePipe(co
   }
   if (pipe_ == INVALID_HANDLE_VALUE) {
     // If this process is being closed, the pipe may be gone already.
     LOG(WARNING) << "failed to create pipe: " << GetLastError();
     return false;
   }
 
   // Create the Hello message to be sent when Connect is called
+  return EnqueueHelloMessage();
+}
+
+bool Channel::ChannelImpl::EnqueueHelloMessage() {
   scoped_ptr<Message> m(new Message(MSG_ROUTING_NONE,
                                     HELLO_MESSAGE_TYPE,
                                     IPC::Message::PRIORITY_NORMAL));
   if (!m->WriteInt(GetCurrentProcessId())) {
     CloseHandle(pipe_);
     pipe_ = INVALID_HANDLE_VALUE;
     return false;
   }
@@ -421,29 +454,40 @@ void Channel::ChannelImpl::OnIOCompleted
 
 //------------------------------------------------------------------------------
 // Channel's methods simply call through to ChannelImpl.
 Channel::Channel(const std::wstring& channel_id, Mode mode,
                  Listener* listener)
     : channel_impl_(new ChannelImpl(channel_id, mode, listener)) {
 }
 
+#ifdef CHROMIUM_MOZILLA_BUILD
+Channel::Channel(const std::wstring& channel_id, void* server_pipe,
+                 Mode mode, Listener* listener)
+   : channel_impl_(new ChannelImpl(channel_id, server_pipe, mode, listener)) {
+}
+#endif
+
 Channel::~Channel() {
   delete channel_impl_;
 }
 
 bool Channel::Connect() {
   return channel_impl_->Connect();
 }
 
 void Channel::Close() {
   channel_impl_->Close();
 }
 
 #ifdef CHROMIUM_MOZILLA_BUILD
+void* Channel::GetServerPipeHandle() const {
+  return channel_impl_->GetServerPipeHandle();
+}
+
 Channel::Listener* Channel::set_listener(Listener* listener) {
   return channel_impl_->set_listener(listener);
 }
 #else
 void Channel::set_listener(Listener* listener) {
   channel_impl_->set_listener(listener);
 }
 #endif
--- a/ipc/chromium/src/chrome/common/ipc_channel_win.h
+++ b/ipc/chromium/src/chrome/common/ipc_channel_win.h
@@ -15,36 +15,42 @@
 class NonThreadSafe;
 
 namespace IPC {
 
 class Channel::ChannelImpl : public MessageLoopForIO::IOHandler {
  public:
   // Mirror methods of Channel, see ipc_channel.h for description.
   ChannelImpl(const std::wstring& channel_id, Mode mode, Listener* listener);
+  ChannelImpl(const std::wstring& channel_id, HANDLE server_pipe,
+              Mode mode, Listener* listener);
   ~ChannelImpl() { 
     if (pipe_ != INVALID_HANDLE_VALUE) {
       Close();
     }
   }
   bool Connect();
   void Close();
 #ifdef CHROMIUM_MOZILLA_BUILD
+  HANDLE GetServerPipeHandle() const;
+
   Listener* set_listener(Listener* listener) {
     Listener* old = listener_;
     listener_ = listener;
     return old;
   }
 #else
   void set_listener(Listener* listener) { listener_ = listener; }
 #endif
   bool Send(Message* message);
  private:
+  void Init(Mode mode, Listener* listener);
   const std::wstring PipeName(const std::wstring& channel_id) const;
   bool CreatePipe(const std::wstring& channel_id, Mode mode);
+  bool EnqueueHelloMessage();
 
   bool ProcessConnection();
   bool ProcessIncomingMessages(MessageLoopForIO::IOContext* context,
                                DWORD bytes_read);
   bool ProcessOutgoingMessages(MessageLoopForIO::IOContext* context,
                                DWORD bytes_written);
 
   // MessageLoop::IOHandler implementation.