Bug 771765 - Support template content process, part 2: IPC and glue changes. r=bent
authorThinker Lee <tlee@mozilla.com>, Cervantes Yu <cyu@mozilla.com>
Fri, 31 May 2013 21:16:54 +0800
changeset 157953 a3f41e00dc182c3155f8a7b8bd8828ce7ebba979
parent 157952 eeda15041decf2126f33d81caba03c25950992bf
child 157954 ae442e5de1ad8ac0d89819999173d04c222cf3f2
push idunknown
push userunknown
push dateunknown
reviewersbent
bugs771765
milestone27.0a1
Bug 771765 - Support template content process, part 2: IPC and glue changes. r=bent Changes include: * Getting/resetting platform thread ID. * Creating an IPC channel with existing file descriptor sent from the template process. * Child process host with existing process forked from the template.
ipc/chromium/src/base/platform_thread_posix.cc
ipc/chromium/src/base/process_util_posix.cc
ipc/chromium/src/base/thread.h
ipc/chromium/src/chrome/common/child_process_host.cc
ipc/chromium/src/chrome/common/child_process_host.h
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/glue/GeckoChildProcessHost.cpp
ipc/glue/GeckoChildProcessHost.h
ipc/glue/Transport_posix.cpp
--- a/ipc/chromium/src/base/platform_thread_posix.cc
+++ b/ipc/chromium/src/base/platform_thread_posix.cc
@@ -44,17 +44,21 @@ static void* ThreadFunc(void* closure) {
 PlatformThreadId PlatformThread::CurrentId() {
   // Pthreads doesn't have the concept of a thread ID, so we have to reach down
   // into the kernel.
 #if defined(OS_MACOSX)
   mach_port_t port = mach_thread_self();
   mach_port_deallocate(mach_task_self(), port);
   return port;
 #elif defined(OS_LINUX)
+#ifdef MOZ_WIDGET_GONK
+  return (intptr_t) (pthread_self());
+#else
   return syscall(__NR_gettid);
+#endif
 #elif defined(OS_OPENBSD) || defined(__GLIBC__)
   return (intptr_t) (pthread_self());
 #elif defined(OS_NETBSD)
   return _lwp_self();
 #elif defined(OS_DRAGONFLY)
   return lwp_gettid();
 #elif defined(OS_FREEBSD)
 #  if __FreeBSD_version > 900030
--- a/ipc/chromium/src/base/process_util_posix.cc
+++ b/ipc/chromium/src/base/process_util_posix.cc
@@ -243,16 +243,19 @@ ProcessMetrics* ProcessMetrics::CreatePr
 }
 
 ProcessMetrics::~ProcessMetrics() { }
 
 bool DidProcessCrash(bool* child_exited, ProcessHandle handle) {
   int status;
   const int result = HANDLE_EINTR(waitpid(handle, &status, WNOHANG));
   if (result == -1) {
+    // The dead process originally spawned from Nuwa might be taken as not
+    // crashed because the above waitpid() call returns -1 and ECHILD. The
+    // caller shouldn't behave incorrectly because of this false negative.
     LOG(ERROR) << "waitpid failed pid:" << handle << " errno:" << errno;
     if (child_exited)
       *child_exited = false;
     return false;
   } else if (result == 0) {
     // the child hasn't exited yet.
     if (child_exited)
       *child_exited = false;
--- a/ipc/chromium/src/base/thread.h
+++ b/ipc/chromium/src/base/thread.h
@@ -101,16 +101,22 @@ class Thread : PlatformThread::Delegate 
   const std::string &thread_name() { return name_; }
 
   // The native thread handle.
   PlatformThreadHandle thread_handle() { return thread_; }
 
   // The thread ID.
   PlatformThreadId thread_id() const { return thread_id_; }
 
+  // Reset thread ID as current thread.
+  PlatformThreadId reset_thread_id() {
+      thread_id_ = PlatformThread::CurrentId();
+      return thread_id_;
+  }
+
   // Returns true if the thread has been started, and not yet stopped.
   // When a thread is running, the thread_id_ is non-zero.
   bool IsRunning() const { return thread_id_ != 0; }
 
  protected:
   // Called just prior to starting the message loop
   virtual void Init() {}
 
--- a/ipc/chromium/src/chrome/common/child_process_host.cc
+++ b/ipc/chromium/src/chrome/common/child_process_host.cc
@@ -7,23 +7,25 @@
 #include "base/compiler_specific.h"
 #include "base/logging.h"
 #include "base/message_loop.h"
 #include "base/process_util.h"
 #include "base/singleton.h"
 #include "base/waitable_event.h"
 #include "mozilla/ipc/ProcessChild.h"
 #include "mozilla/ipc/BrowserProcessSubThread.h"
+#include "mozilla/ipc/Transport.h"
 typedef mozilla::ipc::BrowserProcessSubThread ChromeThread;
 #include "chrome/common/ipc_logging.h"
 #include "chrome/common/notification_service.h"
 #include "chrome/common/notification_type.h"
 #include "chrome/common/process_watcher.h"
 #include "chrome/common/result_codes.h"
 
+using mozilla::ipc::FileDescriptor;
 
 namespace {
 typedef std::list<ChildProcessHost*> ChildProcessList;
 
 // The NotificationTask is used to notify about plugin process connection/
 // disconnection. It is needed because the notifications in the
 // NotificationService must happen in the main thread.
 class ChildNotificationTask : public Task {
@@ -79,16 +81,32 @@ bool ChildProcessHost::CreateChannel() {
   if (!channel_->Connect())
     return false;
 
   opening_channel_ = true;
 
   return true;
 }
 
+bool ChildProcessHost::CreateChannel(FileDescriptor& aFileDescriptor) {
+  if (channel_.get()) {
+    channel_->Close();
+  }
+  channel_.reset(mozilla::ipc::OpenDescriptor(
+      aFileDescriptor, IPC::Channel::MODE_SERVER));
+  channel_->set_listener(&listener_);
+  if (!channel_->Connect()) {
+    return false;
+  }
+
+  opening_channel_ = true;
+
+  return true;
+}
+
 void ChildProcessHost::SetHandle(base::ProcessHandle process) {
 #if defined(OS_WIN)
   process_event_.reset(new base::WaitableEvent(process));
 
   DCHECK(!handle());
   set_handle(process);
   watcher_.StartWatching(process_event_.get(), this);
 #endif
--- a/ipc/chromium/src/chrome/common/child_process_host.h
+++ b/ipc/chromium/src/chrome/common/child_process_host.h
@@ -10,16 +10,22 @@
 #include <list>
 
 #include "base/basictypes.h"
 #include "base/scoped_ptr.h"
 #include "base/waitable_event_watcher.h"
 #include "chrome/common/child_process_info.h"
 #include "chrome/common/ipc_channel.h"
 
+namespace mozilla {
+namespace ipc {
+class FileDescriptor;
+}
+}
+
 class NotificationType;
 
 // Plugins/workers and other child processes that live on the IO thread should
 // derive from this class.
 class ChildProcessHost :
                          public IPC::Message::Sender,
                          public ChildProcessInfo,
                          public base::WaitableEventWatcher::Delegate,
@@ -54,16 +60,18 @@ class ChildProcessHost :
   explicit ChildProcessHost(ProcessType type);
 
   // Derived classes return true if it's ok to shut down the child process.
   virtual bool CanShutdown() = 0;
 
   // Creates the IPC channel.  Returns true iff it succeeded.
   bool CreateChannel();
 
+  bool CreateChannel(mozilla::ipc::FileDescriptor& aFileDescriptor);
+
   // Once the subclass gets a handle to the process, it needs to tell
   // ChildProcessHost using this function.
   void SetHandle(base::ProcessHandle handle);
 
   // Notifies us that an instance has been created on this child process.
   void InstanceCreated();
 
   // IPC::Channel::Listener implementation:
--- a/ipc/chromium/src/chrome/common/ipc_channel.h
+++ b/ipc/chromium/src/chrome/common/ipc_channel.h
@@ -112,18 +112,21 @@ class Channel : public Message::Sender {
   // 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;
 
-  // Return the server side of the socketpair.
-  int GetServerFileDescriptor() const;
+  // Return the file descriptor for communication with the peer.
+  int GetFileDescriptor() const;
+
+  // Reset the file descriptor for communication with the peer.
+  void ResetFileDescriptor(int fd);
 
   // Close the client side of the socketpair.
   void CloseClientFileDescriptor();
 
 #elif defined(OS_WIN)
   // Return the server pipe handle.
   void* GetServerPipeHandle() const;
 #endif  // defined(OS_POSIX)
--- a/ipc/chromium/src/chrome/common/ipc_channel_posix.cc
+++ b/ipc/chromium/src/chrome/common/ipc_channel_posix.cc
@@ -351,16 +351,25 @@ bool Channel::ChannelImpl::CreatePipe(co
       waiting_connect_ = false;
     }
   }
 
   // Create the Hello message to be sent when Connect is called
   return EnqueueHelloMessage();
 }
 
+/**
+ * Reset the file descriptor for communication with the peer.
+ */
+void Channel::ChannelImpl::ResetFileDescriptor(int fd) {
+  NS_ASSERTION(fd > 0 && fd == pipe_, "Invalid file descriptor");
+
+  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;
   }
@@ -966,18 +975,22 @@ Channel::Listener* Channel::set_listener
 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);
 }
 
-int Channel::GetServerFileDescriptor() const {
-  return channel_impl_->GetServerFileDescriptor();
+void Channel::ResetFileDescriptor(int fd) {
+  channel_impl_->ResetFileDescriptor(fd);
+}
+
+int Channel::GetFileDescriptor() const {
+    return channel_impl_->GetFileDescriptor();
 }
 
 void Channel::CloseClientFileDescriptor() {
   channel_impl_->CloseClientFileDescriptor();
 }
 
 bool Channel::Unsound_IsClosed() const {
   return channel_impl_->Unsound_IsClosed();
--- a/ipc/chromium/src/chrome/common/ipc_channel_posix.h
+++ b/ipc/chromium/src/chrome/common/ipc_channel_posix.h
@@ -31,19 +31,21 @@ class Channel::ChannelImpl : public Mess
   void Close();
   Listener* set_listener(Listener* listener) {
     Listener* old = listener_;
     listener_ = listener;
     return old;
   }
   bool Send(Message* message);
   void GetClientFileDescriptorMapping(int *src_fd, int *dest_fd) const;
-  int GetServerFileDescriptor() const {
-    DCHECK(mode_ == MODE_SERVER);
-    return pipe_;
+
+  void ResetFileDescriptor(int fd);
+
+  int GetFileDescriptor() const {
+      return pipe_;
   }
   void CloseClientFileDescriptor();
 
   // See the comment in ipc_channel.h for info on Unsound_IsClosed() and
   // Unsound_NumQueuedMessages().
   bool Unsound_IsClosed() const;
   uint32_t Unsound_NumQueuedMessages() const;
 
--- a/ipc/glue/GeckoChildProcessHost.cpp
+++ b/ipc/glue/GeckoChildProcessHost.cpp
@@ -29,16 +29,20 @@
 #include "mozilla/Omnijar.h"
 #include <sys/stat.h>
 
 #ifdef XP_WIN
 #include "nsIWinTaskbar.h"
 #define NS_TASKBAR_CONTRACTID "@mozilla.org/windows-taskbar;1"
 #endif
 
+#include "nsTArray.h"
+#include "nsClassHashtable.h"
+#include "nsHashKeys.h"
+
 using mozilla::MonitorAutoLock;
 using mozilla::ipc::GeckoChildProcessHost;
 
 #ifdef ANDROID
 // Like its predecessor in nsExceptionHandler.cpp, this is
 // the magic number of a file descriptor remapping we must
 // preserve for the child process.
 static const int kMagicAndroidSystemPropFd = 5;
@@ -84,21 +88,16 @@ GeckoChildProcessHost::GeckoChildProcess
     mProcessState(CREATING_CHANNEL),
     mDelegate(nullptr),
     mChildProcessHandle(0)
 #if defined(MOZ_WIDGET_COCOA)
   , mChildTask(MACH_PORT_NULL)
 #endif
 {
     MOZ_COUNT_CTOR(GeckoChildProcessHost);
-    
-    MessageLoop* ioLoop = XRE_GetIOMessageLoop();
-    ioLoop->PostTask(FROM_HERE,
-                     NewRunnableMethod(this,
-                                       &GeckoChildProcessHost::InitializeChannel));
 }
 
 GeckoChildProcessHost::~GeckoChildProcessHost()
 
 {
   AssertIOThread();
 
   MOZ_COUNT_DTOR(GeckoChildProcessHost);
@@ -282,17 +281,17 @@ GeckoChildProcessHost::SyncLaunch(std::v
 
   PRIntervalTime timeoutTicks = (aTimeoutMs > 0) ? 
     PR_MillisecondsToInterval(aTimeoutMs) : PR_INTERVAL_NO_TIMEOUT;
   MessageLoop* ioLoop = XRE_GetIOMessageLoop();
   NS_ASSERTION(MessageLoop::current() != ioLoop, "sync launch from the IO thread NYI");
 
   ioLoop->PostTask(FROM_HERE,
                    NewRunnableMethod(this,
-                                     &GeckoChildProcessHost::PerformAsyncLaunch,
+                                     &GeckoChildProcessHost::RunPerformAsyncLaunch,
                                      aExtraOpts, arch));
   // NB: this uses a different mechanism than the chromium parent
   // class.
   MonitorAutoLock lock(mMonitor);
   PRIntervalTime waitStart = PR_IntervalNow();
   PRIntervalTime current;
 
   // We'll receive several notifications, we need to exit when we
@@ -317,17 +316,17 @@ GeckoChildProcessHost::SyncLaunch(std::v
 bool
 GeckoChildProcessHost::AsyncLaunch(std::vector<std::string> aExtraOpts)
 {
   PrepareLaunch();
 
   MessageLoop* ioLoop = XRE_GetIOMessageLoop();
   ioLoop->PostTask(FROM_HERE,
                    NewRunnableMethod(this,
-                                     &GeckoChildProcessHost::PerformAsyncLaunch,
+                                     &GeckoChildProcessHost::RunPerformAsyncLaunch,
                                      aExtraOpts, base::GetCurrentProcessArchitecture()));
 
   // This may look like the sync launch wait, but we only delay as
   // long as it takes to create the channel.
   MonitorAutoLock lock(mMonitor);
   while (mProcessState < CHANNEL_INITIALIZED) {
     lock.Wait();
   }
@@ -338,17 +337,17 @@ GeckoChildProcessHost::AsyncLaunch(std::
 bool
 GeckoChildProcessHost::LaunchAndWaitForProcessHandle(StringVector aExtraOpts)
 {
   PrepareLaunch();
 
   MessageLoop* ioLoop = XRE_GetIOMessageLoop();
   ioLoop->PostTask(FROM_HERE,
                    NewRunnableMethod(this,
-                                     &GeckoChildProcessHost::PerformAsyncLaunch,
+                                     &GeckoChildProcessHost::RunPerformAsyncLaunch,
                                      aExtraOpts, base::GetCurrentProcessArchitecture()));
 
   MonitorAutoLock lock(mMonitor);
   while (mProcessState < PROCESS_CREATED) {
     lock.Wait();
   }
   MOZ_ASSERT(mProcessState == PROCESS_ERROR || mChildProcessHandle);
 
@@ -420,16 +419,24 @@ GeckoChildProcessHost::PerformAsyncLaunc
   bool retval = PerformAsyncLaunchInternal(aExtraOpts, arch);
 
   // Revert to original value
   PR_SetEnv(restoreOrigLogName);
 
   return retval;
 }
 
+bool
+GeckoChildProcessHost::RunPerformAsyncLaunch(std::vector<std::string> aExtraOpts,
+                                             base::ProcessArchitecture aArch)
+{
+  InitializeChannel();
+  return PerformAsyncLaunch(aExtraOpts, aArch);
+}
+
 void
 #if defined(XP_WIN)
 AddAppDirToCommandLine(CommandLine& aCmdLine)
 #else
 AddAppDirToCommandLine(std::vector<std::string>& aCmdLine)
 #endif
 {
   // Content processes need access to application resources, so pass
@@ -828,8 +835,57 @@ GeckoChildProcessHost::GetQueuedMessages
 void
 GeckoChildProcessHost::OnWaitableEventSignaled(base::WaitableEvent *event)
 {
   if (mDelegate) {
     mDelegate->OnWaitableEventSignaled(event);
   }
   ChildProcessHost::OnWaitableEventSignaled(event);
 }
+
+#ifdef MOZ_NUWA_PROCESS
+
+using mozilla::ipc::GeckoExistingProcessHost;
+using mozilla::ipc::FileDescriptor;
+
+GeckoExistingProcessHost::
+GeckoExistingProcessHost(GeckoProcessType aProcessType,
+                         base::ProcessHandle aProcess,
+                         const FileDescriptor& aFileDescriptor,
+                         ChildPrivileges aPrivileges)
+  : GeckoChildProcessHost(aProcessType, aPrivileges)
+  , mExistingProcessHandle(aProcess)
+  , mExistingFileDescriptor(aFileDescriptor)
+{
+  NS_ASSERTION(aFileDescriptor.IsValid(),
+               "Expected file descriptor to be valid");
+}
+
+GeckoExistingProcessHost::~GeckoExistingProcessHost()
+{
+}
+
+bool
+GeckoExistingProcessHost::PerformAsyncLaunch(StringVector aExtraOpts,
+                                             base::ProcessArchitecture aArch)
+{
+  SetHandle(mExistingProcessHandle);
+
+  OpenPrivilegedHandle(base::GetProcId(mExistingProcessHandle));
+
+  MonitorAutoLock lock(mMonitor);
+  mProcessState = PROCESS_CREATED;
+  lock.Notify();
+
+  return true;
+}
+
+void
+GeckoExistingProcessHost::InitializeChannel()
+{
+  CreateChannel(mExistingFileDescriptor);
+
+  MonitorAutoLock lock(mMonitor);
+  mProcessState = CHANNEL_INITIALIZED;
+  lock.Notify();
+}
+
+#endif /* MOZ_NUWA_PROCESS */
--- a/ipc/glue/GeckoChildProcessHost.h
+++ b/ipc/glue/GeckoChildProcessHost.h
@@ -6,16 +6,17 @@
 #define __IPC_GLUE_GECKOCHILDPROCESSHOST_H__
 
 #include "base/file_path.h"
 #include "base/process_util.h"
 #include "base/scoped_ptr.h"
 #include "base/waitable_event.h"
 #include "chrome/common/child_process_host.h"
 
+#include "mozilla/ipc/FileDescriptor.h"
 #include "mozilla/Monitor.h"
 
 #include "nsXULAppAPI.h"        // for GeckoProcessType
 #include "nsString.h"
 
 namespace mozilla {
 namespace ipc {
 
@@ -61,25 +62,25 @@ public:
 
   // Block until the child process has been created and it connects to
   // the IPC channel, meaning it's fully initialized.  (Or until an
   // error occurs.)
   bool SyncLaunch(StringVector aExtraOpts=StringVector(),
                   int32_t timeoutMs=0,
                   base::ProcessArchitecture arch=base::GetCurrentProcessArchitecture());
 
-  bool PerformAsyncLaunch(StringVector aExtraOpts=StringVector(),
-                          base::ProcessArchitecture arch=base::GetCurrentProcessArchitecture());
+  virtual bool PerformAsyncLaunch(StringVector aExtraOpts=StringVector(),
+                                  base::ProcessArchitecture aArch=base::GetCurrentProcessArchitecture());
 
   virtual void OnChannelConnected(int32_t peer_pid);
   virtual void OnMessageReceived(const IPC::Message& aMsg);
   virtual void OnChannelError();
   virtual void GetQueuedMessages(std::queue<IPC::Message>& queue);
 
-  void InitializeChannel();
+  virtual void InitializeChannel();
 
   virtual bool CanShutdown() { return true; }
 
   virtual void OnWaitableEventSignaled(base::WaitableEvent *event);
 
   IPC::Channel* GetChannel() {
     return channelp();
   }
@@ -141,31 +142,56 @@ protected:
 
   base::WaitableEventWatcher::Delegate* mDelegate;
 
   ProcessHandle mChildProcessHandle;
 #if defined(OS_MACOSX)
   task_t mChildTask;
 #endif
 
+  void OpenPrivilegedHandle(base::ProcessId aPid);
+
 private:
   DISALLOW_EVIL_CONSTRUCTORS(GeckoChildProcessHost);
 
   // Does the actual work for AsyncLaunch, on the IO thread.
   bool PerformAsyncLaunchInternal(std::vector<std::string>& aExtraOpts,
                                   base::ProcessArchitecture arch);
 
-  void OpenPrivilegedHandle(base::ProcessId aPid);
+  bool RunPerformAsyncLaunch(StringVector aExtraOpts=StringVector(),
+			     base::ProcessArchitecture aArch=base::GetCurrentProcessArchitecture());
 
   // In between launching the subprocess and handing off its IPC
   // channel, there's a small window of time in which *we* might still
   // be the channel listener, and receive messages.  That's bad
   // because we have no idea what to do with those messages.  So queue
   // them here until we hand off the eventual listener.
   //
   // FIXME/cjones: this strongly indicates bad design.  Shame on us.
   std::queue<IPC::Message> mQueue;
 };
 
+#ifdef MOZ_NUWA_PROCESS
+class GeckoExistingProcessHost MOZ_FINAL : public GeckoChildProcessHost
+{
+public:
+  GeckoExistingProcessHost(GeckoProcessType aProcessType,
+                           base::ProcessHandle aProcess,
+                           const FileDescriptor& aFileDescriptor,
+                           ChildPrivileges aPrivileges=base::PRIVILEGES_DEFAULT);
+
+  ~GeckoExistingProcessHost();
+
+  virtual bool PerformAsyncLaunch(StringVector aExtraOpts=StringVector(),
+          base::ProcessArchitecture aArch=base::GetCurrentProcessArchitecture()) MOZ_OVERRIDE;
+
+  virtual void InitializeChannel() MOZ_OVERRIDE;
+
+private:
+  base::ProcessHandle mExistingProcessHandle;
+  mozilla::ipc::FileDescriptor mExistingFileDescriptor;
+};
+#endif /* MOZ_NUWA_PROCESS */
+
 } /* namespace ipc */
 } /* namespace mozilla */
 
 #endif /* __IPC_GLUE_GECKOCHILDPROCESSHOST_H__ */
--- a/ipc/glue/Transport_posix.cpp
+++ b/ipc/glue/Transport_posix.cpp
@@ -24,17 +24,17 @@ CreateTransport(ProcessHandle /*unused*/
                 TransportDescriptor* aOne, TransportDescriptor* aTwo)
 {
   // Gecko doesn't care about this random ID, and the argument to this
   // function isn't really necessary, it can be just any random
   // pointer value
   wstring id = ChildProcessInfo::GenerateRandomChannelID(aOne);
   // Use MODE_SERVER to force creation of the socketpair
   Transport t(id, Transport::MODE_SERVER, nullptr);
-  int fd1 = t.GetServerFileDescriptor();
+  int fd1 = t.GetFileDescriptor();
   int fd2, dontcare;
   t.GetClientFileDescriptorMapping(&fd2, &dontcare);
   if (fd1 < 0 || fd2 < 0) {
     return false;
   }
 
   // The Transport closes these fds when it goes out of scope, so we
   // dup them here