diff --git a/dom/media/gmp/GMPServiceParent.cpp b/dom/media/gmp/GMPServiceParent.cpp index 0427f50afb6a..92aed2806d22 100644 --- a/dom/media/gmp/GMPServiceParent.cpp +++ b/dom/media/gmp/GMPServiceParent.cpp @@ -44,6 +44,8 @@ #include #include "MediaPrefs.h" +using mozilla::ipc::Transport; + namespace mozilla { #ifdef LOG @@ -80,7 +82,8 @@ GeckoMediaPluginServiceParent::GetSingleton() NS_IMPL_ISUPPORTS_INHERITED(GeckoMediaPluginServiceParent, GeckoMediaPluginService, - mozIGeckoMediaPluginChromeService) + mozIGeckoMediaPluginChromeService, + nsIAsyncShutdownBlocker) GeckoMediaPluginServiceParent::GeckoMediaPluginServiceParent() : mShuttingDown(false) @@ -91,6 +94,7 @@ GeckoMediaPluginServiceParent::GeckoMediaPluginServiceParent() , mWaitingForPluginsSyncShutdown(false) , mInitPromiseMonitor("GeckoMediaPluginServiceParent::mInitPromiseMonitor") , mLoadPluginsFromDiskComplete(false) + , mServiceUserCount(0) { MOZ_ASSERT(NS_IsMainThread()); mInitPromise.SetMonitor(&mInitPromiseMonitor); @@ -1838,6 +1842,61 @@ static bool IsNodeIdValid(GMPParent* aParent) { return !aParent->GetNodeId().IsEmpty(); } +static nsCOMPtr +GetShutdownBarrier() +{ + nsCOMPtr svc = services::GetAsyncShutdown(); + MOZ_RELEASE_ASSERT(svc); + + nsCOMPtr 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() { @@ -1877,6 +1936,9 @@ GeckoMediaPluginServiceParent::GetById(uint32_t aPluginId) GMPServiceParent::~GMPServiceParent() { + NS_DispatchToMainThread( + NewRunnableMethod(mService.get(), + &GeckoMediaPluginServiceParent::ServiceUserDestroyed)); } bool @@ -1976,9 +2038,38 @@ private: nsAutoPtr 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 task = + NewNonOwningRunnableMethod(this, + &GMPServiceParent::CloseTransport, + &monitor, + &completed); + XRE_GetIOMessageLoop()->PostTask(Move(task.forget())); + + while (!completed) { + lock.Wait(); + } + NS_DispatchToCurrentThread(new DeleteGMPServiceParent(this)); } @@ -2017,12 +2108,17 @@ GMPServiceParent::Create(Transport* aTransport, ProcessId aOtherPid) RefPtr gmp = GeckoMediaPluginServiceParent::GetSingleton(); - nsAutoPtr serviceParent(new GMPServiceParent(gmp)); + if (gmp->mShuttingDown) { + // Shutdown is initiated. There is no point creating a new actor. + return nullptr; + } nsCOMPtr gmpThread; nsresult rv = gmp->GetThread(getter_AddRefs(gmpThread)); NS_ENSURE_SUCCESS(rv, nullptr); + nsAutoPtr serviceParent(new GMPServiceParent(gmp)); + bool ok; rv = gmpThread->Dispatch(new OpenPGMPServiceParent(serviceParent, aTransport, diff --git a/dom/media/gmp/GMPServiceParent.h b/dom/media/gmp/GMPServiceParent.h index aefd26cbf532..3a8d613c37e6 100644 --- a/dom/media/gmp/GMPServiceParent.h +++ b/dom/media/gmp/GMPServiceParent.h @@ -12,6 +12,7 @@ #include "nsClassHashtable.h" #include "nsDataHashtable.h" #include "mozilla/Atomics.h" +#include "nsIAsyncShutdown.h" #include "nsThreadUtils.h" #include "mozilla/MozPromise.h" #include "GMPStorage.h" @@ -25,6 +26,7 @@ class GMPParent; class GeckoMediaPluginServiceParent final : public GeckoMediaPluginService , public mozIGeckoMediaPluginChromeService + , public nsIAsyncShutdownBlocker { public: static already_AddRefed GetSingleton(); @@ -33,6 +35,7 @@ public: nsresult Init() override; NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSIASYNCSHUTDOWNBLOCKER // mozIGeckoMediaPluginService NS_IMETHOD GetPluginVersionForAPI(const nsACString& aAPI, @@ -65,6 +68,10 @@ public: 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; @@ -217,6 +224,10 @@ private: // Hashes nodeId to the hashtable of storage for that nodeId. nsRefPtrHashtable 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 mServiceUserCount; }; nsresult ReadSalt(nsIFile* aPath, nsACString& aOutData); @@ -230,6 +241,7 @@ public: explicit GMPServiceParent(GeckoMediaPluginServiceParent* aService) : mService(aService) { + mService->ServiceUserCreated(); } virtual ~GMPServiceParent(); @@ -259,6 +271,8 @@ public: nsresult* aOutRv) override; private: + void CloseTransport(Monitor* aSyncMonitor, bool* aCompleted); + RefPtr mService; };