Bug 998863: Asynchronous Plugin Initialization, Part 8: Plugin process launch changes; r=josh
authorAaron Klotz <aklotz@mozilla.com>
Mon, 29 Dec 2014 16:13:28 -0700
changeset 247411 eb6ac47db85693228e942143cfbe5a077e8d6127
parent 247410 23e07ce94eb7fe5cc319eca51041ba055539f4ac
child 247412 8a4c2bf5ec09977e94cc055267c0bc6280914966
push id4489
push userraliiev@mozilla.com
push dateMon, 23 Feb 2015 15:17:55 +0000
treeherdermozilla-beta@fd7c3dc24146 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjosh
bugs998863
milestone37.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 998863: Asynchronous Plugin Initialization, Part 8: Plugin process launch changes; r=josh
dom/plugins/ipc/PluginProcessParent.cpp
dom/plugins/ipc/PluginProcessParent.h
ipc/glue/GeckoChildProcessHost.cpp
ipc/glue/GeckoChildProcessHost.h
--- a/dom/plugins/ipc/PluginProcessParent.cpp
+++ b/dom/plugins/ipc/PluginProcessParent.cpp
@@ -7,16 +7,17 @@
 #include "mozilla/plugins/PluginProcessParent.h"
 
 #include "base/string_util.h"
 #include "base/process_util.h"
 
 #include "mozilla/ipc/BrowserProcessSubThread.h"
 #include "mozilla/plugins/PluginMessageUtils.h"
 #include "mozilla/Telemetry.h"
+#include "nsThreadUtils.h"
 
 using std::vector;
 using std::string;
 
 using mozilla::ipc::BrowserProcessSubThread;
 using mozilla::ipc::GeckoChildProcessHost;
 using mozilla::plugins::PluginProcessParent;
 using base::ProcessArchitecture;
@@ -25,26 +26,28 @@ template<>
 struct RunnableMethodTraits<PluginProcessParent>
 {
     static void RetainCallee(PluginProcessParent* obj) { }
     static void ReleaseCallee(PluginProcessParent* obj) { }
 };
 
 PluginProcessParent::PluginProcessParent(const std::string& aPluginFilePath) :
     GeckoChildProcessHost(GeckoProcessType_Plugin),
-    mPluginFilePath(aPluginFilePath)
+    mPluginFilePath(aPluginFilePath),
+    mMainMsgLoop(MessageLoop::current()),
+    mRunCompleteTaskImmediately(false)
 {
 }
 
 PluginProcessParent::~PluginProcessParent()
 {
 }
 
 bool
-PluginProcessParent::Launch(int32_t timeoutMs)
+PluginProcessParent::Launch(UniquePtr<LaunchCompleteTask> aLaunchCompleteTask)
 {
     ProcessArchitecture currentArchitecture = base::GetCurrentProcessArchitecture();
     uint32_t containerArchitectures = GetSupportedArchitecturesForProcessType(GeckoProcessType_Plugin);
 
     uint32_t pluginLibArchitectures = currentArchitecture;
 #ifdef XP_MACOSX
     nsresult rv = GetArchitecturesForBinary(mPluginFilePath.c_str(), &pluginLibArchitectures);
     if (NS_FAILED(rv)) {
@@ -69,28 +72,80 @@ PluginProcessParent::Launch(int32_t time
         else if (base::PROCESS_ARCH_ARM & pluginLibArchitectures & containerArchitectures) {
           selectedArchitecture = base::PROCESS_ARCH_ARM;
         }
         else {
             return false;
         }
     }
 
+    mLaunchCompleteTask = Move(aLaunchCompleteTask);
+
     vector<string> args;
     args.push_back(MungePluginDsoPath(mPluginFilePath));
-    Telemetry::AutoTimer<Telemetry::PLUGIN_STARTUP_MS> timer;
-    return SyncLaunch(args, timeoutMs, selectedArchitecture);
+
+    bool result = AsyncLaunch(args, selectedArchitecture);
+    if (!result) {
+        mLaunchCompleteTask = nullptr;
+    }
+    return result;
 }
 
 void
 PluginProcessParent::Delete()
 {
   MessageLoop* currentLoop = MessageLoop::current();
   MessageLoop* ioLoop = XRE_GetIOMessageLoop();
 
   if (currentLoop == ioLoop) {
       delete this;
       return;
   }
 
   ioLoop->PostTask(FROM_HERE,
                    NewRunnableMethod(this, &PluginProcessParent::Delete));
 }
+
+void
+PluginProcessParent::SetCallRunnableImmediately(bool aCallImmediately)
+{
+    mRunCompleteTaskImmediately = aCallImmediately;
+}
+
+bool
+PluginProcessParent::WaitUntilConnected(int32_t aTimeoutMs)
+{
+    bool result = GeckoChildProcessHost::WaitUntilConnected(aTimeoutMs);
+    if (mRunCompleteTaskImmediately && mLaunchCompleteTask) {
+        if (result) {
+            mLaunchCompleteTask->SetLaunchSucceeded();
+        }
+        mLaunchCompleteTask->Run();
+        mLaunchCompleteTask = nullptr;
+    }
+    return result;
+}
+
+void
+PluginProcessParent::OnChannelConnected(int32_t peer_pid)
+{
+    GeckoChildProcessHost::OnChannelConnected(peer_pid);
+    if (mLaunchCompleteTask && !mRunCompleteTaskImmediately) {
+        mMainMsgLoop->PostTask(FROM_HERE, mLaunchCompleteTask.release());
+    }
+}
+
+void
+PluginProcessParent::OnChannelError()
+{
+    GeckoChildProcessHost::OnChannelError();
+    if (mLaunchCompleteTask && !mRunCompleteTaskImmediately) {
+        mMainMsgLoop->PostTask(FROM_HERE, mLaunchCompleteTask.release());
+    }
+}
+
+bool
+PluginProcessParent::IsConnected()
+{
+    mozilla::MonitorAutoLock lock(mMonitor);
+    return mProcessState == PROCESS_CONNECTED;
+}
+
--- a/dom/plugins/ipc/PluginProcessParent.h
+++ b/dom/plugins/ipc/PluginProcessParent.h
@@ -6,52 +6,83 @@
 
 #ifndef dom_plugins_PluginProcessParent_h
 #define dom_plugins_PluginProcessParent_h 1
 
 #include "mozilla/Attributes.h"
 #include "base/basictypes.h"
 
 #include "base/file_path.h"
+#include "base/task.h"
 #include "base/thread.h"
 #include "base/waitable_event.h"
 #include "chrome/common/child_process_host.h"
 
 #include "mozilla/ipc/GeckoChildProcessHost.h"
+#include "mozilla/UniquePtr.h"
+#include "nsCOMPtr.h"
+#include "nsIRunnable.h"
 
 namespace mozilla {
 namespace plugins {
-//-----------------------------------------------------------------------------
+
+class LaunchCompleteTask : public Task
+{
+public:
+    LaunchCompleteTask()
+        : mLaunchSucceeded(false)
+    {
+    }
+
+    void SetLaunchSucceeded() { mLaunchSucceeded = true; }
+
+protected:
+    bool mLaunchSucceeded;
+};
 
 class PluginProcessParent : public mozilla::ipc::GeckoChildProcessHost
 {
 public:
     explicit PluginProcessParent(const std::string& aPluginFilePath);
     ~PluginProcessParent();
 
     /**
-     * Synchronously launch the plugin process. If the process fails to launch
-     * after timeoutMs, this method will return false.
+     * Launch the plugin process. If the process fails to launch,
+     * this method will return false.
+     *
+     * @param aLaunchCompleteTask Task that is executed on the main
+     * thread once the asynchonous launch has completed.
      */
-    bool Launch(int32_t timeoutMs);
+    bool Launch(UniquePtr<LaunchCompleteTask> aLaunchCompleteTask = UniquePtr<LaunchCompleteTask>());
 
     void Delete();
 
     virtual bool CanShutdown() MOZ_OVERRIDE
     {
         return true;
     }
 
     const std::string& GetPluginFilePath() { return mPluginFilePath; }
 
     using mozilla::ipc::GeckoChildProcessHost::GetShutDownEvent;
     using mozilla::ipc::GeckoChildProcessHost::GetChannel;
 
+    void SetCallRunnableImmediately(bool aCallImmediately);
+    virtual bool WaitUntilConnected(int32_t aTimeoutMs = 0) MOZ_OVERRIDE;
+
+    virtual void OnChannelConnected(int32_t peer_pid) MOZ_OVERRIDE;
+    virtual void OnChannelError() MOZ_OVERRIDE;
+
+    bool IsConnected();
+
 private:
     std::string mPluginFilePath;
+    UniquePtr<LaunchCompleteTask> mLaunchCompleteTask;
+    MessageLoop* mMainMsgLoop;
+    bool mRunCompleteTaskImmediately;
 
     DISALLOW_EVIL_CONSTRUCTORS(PluginProcessParent);
 };
 
 
 } // namespace plugins
 } // namespace mozilla
 
--- a/ipc/glue/GeckoChildProcessHost.cpp
+++ b/ipc/glue/GeckoChildProcessHost.cpp
@@ -310,27 +310,57 @@ void GeckoChildProcessHost::InitWindowsG
 }
 #endif
 
 bool
 GeckoChildProcessHost::SyncLaunch(std::vector<std::string> aExtraOpts, int aTimeoutMs, base::ProcessArchitecture arch)
 {
   PrepareLaunch();
 
-  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::RunPerformAsyncLaunch,
                                      aExtraOpts, arch));
+
+  return WaitUntilConnected(aTimeoutMs);
+}
+
+bool
+GeckoChildProcessHost::AsyncLaunch(std::vector<std::string> aExtraOpts,
+                                   base::ProcessArchitecture arch)
+{
+  PrepareLaunch();
+
+  MessageLoop* ioLoop = XRE_GetIOMessageLoop();
+  ioLoop->PostTask(FROM_HERE,
+                   NewRunnableMethod(this,
+                                     &GeckoChildProcessHost::RunPerformAsyncLaunch,
+                                     aExtraOpts, arch));
+
+  // 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();
+  }
+
+  return true;
+}
+
+bool
+GeckoChildProcessHost::WaitUntilConnected(int32_t aTimeoutMs)
+{
   // NB: this uses a different mechanism than the chromium parent
   // class.
+  PRIntervalTime timeoutTicks = (aTimeoutMs > 0) ? 
+    PR_MillisecondsToInterval(aTimeoutMs) : PR_INTERVAL_NO_TIMEOUT;
+
   MonitorAutoLock lock(mMonitor);
   PRIntervalTime waitStart = PR_IntervalNow();
   PRIntervalTime current;
 
   // We'll receive several notifications, we need to exit when we
   // have either successfully launched or have timed out.
   while (mProcessState != PROCESS_CONNECTED) {
     // If there was an error then return it, don't wait out the timeout.
@@ -350,37 +380,16 @@ GeckoChildProcessHost::SyncLaunch(std::v
       waitStart = current;
     }
   }
 
   return mProcessState == PROCESS_CONNECTED;
 }
 
 bool
-GeckoChildProcessHost::AsyncLaunch(std::vector<std::string> aExtraOpts)
-{
-  PrepareLaunch();
-
-  MessageLoop* ioLoop = XRE_GetIOMessageLoop();
-  ioLoop->PostTask(FROM_HERE,
-                   NewRunnableMethod(this,
-                                     &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();
-  }
-
-  return true;
-}
-
-bool
 GeckoChildProcessHost::LaunchAndWaitForProcessHandle(StringVector aExtraOpts)
 {
   PrepareLaunch();
 
   MessageLoop* ioLoop = XRE_GetIOMessageLoop();
   ioLoop->PostTask(FROM_HERE,
                    NewRunnableMethod(this,
                                      &GeckoChildProcessHost::RunPerformAsyncLaunch,
--- a/ipc/glue/GeckoChildProcessHost.h
+++ b/ipc/glue/GeckoChildProcessHost.h
@@ -47,17 +47,20 @@ public:
 
   static nsresult GetArchitecturesForBinary(const char *path, uint32_t *result);
 
   static uint32_t GetSupportedArchitecturesForProcessType(GeckoProcessType type);
 
   // Block until the IPC channel for our subprocess is initialized,
   // but no longer.  The child process may or may not have been
   // created when this method returns.
-  bool AsyncLaunch(StringVector aExtraOpts=StringVector());
+  bool AsyncLaunch(StringVector aExtraOpts=StringVector(),
+                   base::ProcessArchitecture arch=base::GetCurrentProcessArchitecture());
+
+  virtual bool WaitUntilConnected(int32_t aTimeoutMs = 0);
 
   // Block until the IPC channel for our subprocess is initialized and
   // the OS process is created.  The subprocess may or may not have
   // connected back to us when this method returns.
   //
   // NB: on POSIX, this method is relatively cheap, and doesn't
   // require disk IO.  On win32 however, it requires at least the
   // analogue of stat().  This difference induces a semantic