Bug 1279612 - Block xpcom-will-shutdown before GMPServiceParent instances are shut down. r=gerald a=ritu
authorCervantes Yu <cyu@mozilla.com>
Thu, 08 Sep 2016 11:08:27 +0800
changeset 357979 c796e9b24f3cb6fe9432122b05df0257c27496cb
parent 357978 5de999a139fc699a2db732821348657f12f25965
child 357980 5440c6019b43abcdc1d28304314b692a76106e10
push id1324
push usermtabara@mozilla.com
push dateMon, 16 Jan 2017 13:07:44 +0000
treeherdermozilla-release@a01c49833940 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgerald, ritu
bugs1279612
milestone51.0a2
Bug 1279612 - Block xpcom-will-shutdown before GMPServiceParent instances are shut down. r=gerald a=ritu MozReview-Commit-ID: HdF1RDxVXv1
dom/media/gmp/GMPServiceParent.cpp
dom/media/gmp/GMPServiceParent.h
--- a/dom/media/gmp/GMPServiceParent.cpp
+++ b/dom/media/gmp/GMPServiceParent.cpp
@@ -39,16 +39,18 @@
 #include "nsExceptionHandler.h"
 #include "nsPrintfCString.h"
 #endif
 #include "nsIXULRuntime.h"
 #include "GMPDecoderModule.h"
 #include <limits>
 #include "MediaPrefs.h"
 
+using mozilla::ipc::Transport;
+
 namespace mozilla {
 
 #ifdef LOG
 #undef LOG
 #endif
 
 #define LOGD(msg) MOZ_LOG(GetGMPLog(), mozilla::LogLevel::Debug, msg)
 #define LOG(level, msg) MOZ_LOG(GetGMPLog(), (level), msg)
@@ -75,27 +77,29 @@ GeckoMediaPluginServiceParent::GetSingle
     MOZ_ASSERT(chromeService);
   }
 #endif
   return service.forget().downcast<GeckoMediaPluginServiceParent>();
 }
 
 NS_IMPL_ISUPPORTS_INHERITED(GeckoMediaPluginServiceParent,
                             GeckoMediaPluginService,
-                            mozIGeckoMediaPluginChromeService)
+                            mozIGeckoMediaPluginChromeService,
+                            nsIAsyncShutdownBlocker)
 
 GeckoMediaPluginServiceParent::GeckoMediaPluginServiceParent()
   : mShuttingDown(false)
 #ifdef MOZ_CRASHREPORTER
   , mAsyncShutdownPluginStatesMutex("GeckoMediaPluginService::mAsyncShutdownPluginStatesMutex")
 #endif
   , mScannedPluginOnDisk(false)
   , mWaitingForPluginsSyncShutdown(false)
   , mInitPromiseMonitor("GeckoMediaPluginServiceParent::mInitPromiseMonitor")
   , mLoadPluginsFromDiskComplete(false)
+  , mServiceUserCount(0)
 {
   MOZ_ASSERT(NS_IsMainThread());
   mInitPromise.SetMonitor(&mInitPromiseMonitor);
 }
 
 GeckoMediaPluginServiceParent::~GeckoMediaPluginServiceParent()
 {
   MOZ_ASSERT(mPlugins.IsEmpty());
@@ -1833,16 +1837,71 @@ GeckoMediaPluginServiceParent::ForgetThi
       this, &GeckoMediaPluginServiceParent::ForgetThisSiteOnGMPThread,
       NS_ConvertUTF16toUTF8(aSite), aPattern));
 }
 
 static bool IsNodeIdValid(GMPParent* aParent) {
   return !aParent->GetNodeId().IsEmpty();
 }
 
+static nsCOMPtr<nsIAsyncShutdownClient>
+GetShutdownBarrier()
+{
+  nsCOMPtr<nsIAsyncShutdownService> svc = services::GetAsyncShutdown();
+  MOZ_RELEASE_ASSERT(svc);
+
+  nsCOMPtr<nsIAsyncShutdownClient> barrier;
+  nsresult rv = svc->GetXpcomWillShutdown(getter_AddRefs(barrier));
+
+  MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
+  MOZ_RELEASE_ASSERT(barrier);
+  return barrier.forget();
+}
+
+NS_IMETHODIMP
+GeckoMediaPluginServiceParent::GetName(nsAString& aName)
+{
+  aName = NS_LITERAL_STRING("GeckoMediaPluginServiceParent: shutdown");
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+GeckoMediaPluginServiceParent::GetState(nsIPropertyBag**)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+GeckoMediaPluginServiceParent::BlockShutdown(nsIAsyncShutdownClient*)
+{
+  return NS_OK;
+}
+
+void
+GeckoMediaPluginServiceParent::ServiceUserCreated()
+{
+  MOZ_ASSERT(mServiceUserCount >= 0);
+  if (++mServiceUserCount == 1) {
+    nsresult rv = GetShutdownBarrier()->AddBlocker(
+      this, NS_LITERAL_STRING(__FILE__), __LINE__,
+      NS_LITERAL_STRING("GeckoMediaPluginServiceParent shutdown"));
+    MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
+  }
+}
+
+void
+GeckoMediaPluginServiceParent::ServiceUserDestroyed()
+{
+  MOZ_ASSERT(mServiceUserCount > 0);
+  if (--mServiceUserCount == 0) {
+    nsresult rv = GetShutdownBarrier()->RemoveBlocker(this);
+    MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
+  }
+}
+
 void
 GeckoMediaPluginServiceParent::ClearStorage()
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
   LOGD(("%s::%s", __CLASS__, __FUNCTION__));
 
   // Kill plugins with valid nodeIDs.
   KillPlugins(mPlugins, mMutex, &IsNodeIdValid);
@@ -1872,16 +1931,19 @@ GeckoMediaPluginServiceParent::GetById(u
       return do_AddRef(gmp);
     }
   }
   return nullptr;
 }
 
 GMPServiceParent::~GMPServiceParent()
 {
+  NS_DispatchToMainThread(
+    NewRunnableMethod(mService.get(),
+                      &GeckoMediaPluginServiceParent::ServiceUserDestroyed));
 }
 
 bool
 GMPServiceParent::RecvSelectGMP(const nsCString& aNodeId,
                                 const nsCString& aAPI,
                                 nsTArray<nsCString>&& aTags,
                                 uint32_t* aOutPluginId,
                                 nsresult* aOutRv)
@@ -1971,19 +2033,48 @@ public:
   {
     return NS_OK;
   }
 
 private:
   nsAutoPtr<GMPServiceParent> mToDelete;
 };
 
+void GMPServiceParent::CloseTransport(Monitor* aSyncMonitor, bool* aCompleted)
+{
+  MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
+
+  MonitorAutoLock lock(*aSyncMonitor);
+
+  // This deletes the transport.
+  SetTransport(nullptr);
+
+  *aCompleted = true;
+  lock.NotifyAll();
+}
+
 void
 GMPServiceParent::ActorDestroy(ActorDestroyReason aWhy)
 {
+  Monitor monitor("DeleteGMPServiceParent");
+  bool completed = false;
+
+  // Make sure the IPC channel is closed before destroying mToDelete.
+  MonitorAutoLock lock(monitor);
+  RefPtr<Runnable> task =
+    NewNonOwningRunnableMethod<Monitor*, bool*>(this,
+                                                &GMPServiceParent::CloseTransport,
+                                                &monitor,
+                                                &completed);
+  XRE_GetIOMessageLoop()->PostTask(Move(task.forget()));
+
+  while (!completed) {
+    lock.Wait();
+  }
+
   NS_DispatchToCurrentThread(new DeleteGMPServiceParent(this));
 }
 
 class OpenPGMPServiceParent : public mozilla::Runnable
 {
 public:
   OpenPGMPServiceParent(GMPServiceParent* aGMPServiceParent,
                         mozilla::ipc::Transport* aTransport,
@@ -2012,22 +2103,27 @@ private:
 
 /* static */
 PGMPServiceParent*
 GMPServiceParent::Create(Transport* aTransport, ProcessId aOtherPid)
 {
   RefPtr<GeckoMediaPluginServiceParent> gmp =
     GeckoMediaPluginServiceParent::GetSingleton();
 
-  nsAutoPtr<GMPServiceParent> serviceParent(new GMPServiceParent(gmp));
+  if (gmp->mShuttingDown) {
+    // Shutdown is initiated. There is no point creating a new actor.
+    return nullptr;
+  }
 
   nsCOMPtr<nsIThread> gmpThread;
   nsresult rv = gmp->GetThread(getter_AddRefs(gmpThread));
   NS_ENSURE_SUCCESS(rv, nullptr);
 
+  nsAutoPtr<GMPServiceParent> serviceParent(new GMPServiceParent(gmp));
+
   bool ok;
   rv = gmpThread->Dispatch(new OpenPGMPServiceParent(serviceParent,
                                                      aTransport,
                                                      aOtherPid, &ok),
                            NS_DISPATCH_SYNC);
   if (NS_FAILED(rv) || !ok) {
     return nullptr;
   }
--- a/dom/media/gmp/GMPServiceParent.h
+++ b/dom/media/gmp/GMPServiceParent.h
@@ -7,37 +7,40 @@
 #define GMPServiceParent_h_
 
 #include "GMPService.h"
 #include "mozilla/gmp/PGMPServiceParent.h"
 #include "mozIGeckoMediaPluginChromeService.h"
 #include "nsClassHashtable.h"
 #include "nsDataHashtable.h"
 #include "mozilla/Atomics.h"
+#include "nsIAsyncShutdown.h"
 #include "nsThreadUtils.h"
 #include "mozilla/MozPromise.h"
 #include "GMPStorage.h"
 
 template <class> struct already_AddRefed;
 
 namespace mozilla {
 namespace gmp {
 
 class GMPParent;
 
 class GeckoMediaPluginServiceParent final : public GeckoMediaPluginService
                                           , public mozIGeckoMediaPluginChromeService
+                                          , public nsIAsyncShutdownBlocker
 {
 public:
   static already_AddRefed<GeckoMediaPluginServiceParent> GetSingleton();
 
   GeckoMediaPluginServiceParent();
   nsresult Init() override;
 
   NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_NSIASYNCSHUTDOWNBLOCKER
 
   // mozIGeckoMediaPluginService
   NS_IMETHOD GetPluginVersionForAPI(const nsACString& aAPI,
                                     nsTArray<nsCString>* aTags,
                                     bool* aHasPlugin,
                                     nsACString& aOutVersion) override;
   NS_IMETHOD GetNodeId(const nsAString& aOrigin,
                        const nsAString& aTopLevelOrigin,
@@ -60,16 +63,20 @@ public:
 
   // GMP thread access only
   bool IsShuttingDown();
 
   already_AddRefed<GMPStorage> GetMemoryStorageFor(const nsACString& aNodeId);
   nsresult ForgetThisSiteNative(const nsAString& aSite,
                                 const mozilla::OriginAttributesPattern& aPattern);
 
+  // Notifies that some user of this class is created/destroyed.
+  void ServiceUserCreated();
+  void ServiceUserDestroyed();
+
 private:
   friend class GMPServiceParent;
 
   virtual ~GeckoMediaPluginServiceParent();
 
   void ClearStorage();
 
   already_AddRefed<GMPParent> SelectPluginForAPI(const nsACString& aNodeId,
@@ -212,29 +219,34 @@ private:
   // Synchronization for barrier that ensures we've loaded GMPs from
   // MOZ_GMP_PATH before allowing GetContentParentFrom() to proceed.
   Monitor mInitPromiseMonitor;
   MozPromiseHolder<GenericPromise> mInitPromise;
   bool mLoadPluginsFromDiskComplete;
 
   // Hashes nodeId to the hashtable of storage for that nodeId.
   nsRefPtrHashtable<nsCStringHashKey, GMPStorage> mTempGMPStorage;
+
+  // Tracks how many users are running (on the GMP thread). Only when this count
+  // drops to 0 can we safely shut down the thread.
+  MainThreadOnly<int32_t> mServiceUserCount;
 };
 
 nsresult ReadSalt(nsIFile* aPath, nsACString& aOutData);
 bool MatchOrigin(nsIFile* aPath,
                  const nsACString& aSite,
                  const mozilla::OriginAttributesPattern& aPattern);
 
 class GMPServiceParent final : public PGMPServiceParent
 {
 public:
   explicit GMPServiceParent(GeckoMediaPluginServiceParent* aService)
     : mService(aService)
   {
+    mService->ServiceUserCreated();
   }
   virtual ~GMPServiceParent();
 
   bool RecvGetGMPNodeId(const nsString& aOrigin,
                         const nsString& aTopLevelOrigin,
                         const nsString& aGMPName,
                         const bool& aInPrivateBrowsing,
                         nsCString* aID) override;
@@ -254,15 +266,17 @@ public:
 
   bool RecvLaunchGMP(const uint32_t& aPluginId,
                      nsTArray<ProcessId>&& aAlreadyBridgedTo,
                      ProcessId* aOutID,
                      nsCString* aOutDisplayName,
                      nsresult* aOutRv) override;
 
 private:
+  void CloseTransport(Monitor* aSyncMonitor, bool* aCompleted);
+
   RefPtr<GeckoMediaPluginServiceParent> mService;
 };
 
 } // namespace gmp
 } // namespace mozilla
 
 #endif // GMPServiceParent_h_