diff --git a/content/media/gmp/GMPParent.cpp b/content/media/gmp/GMPParent.cpp index 277fa7b5db0f..7a25c5bb8233 100644 --- a/content/media/gmp/GMPParent.cpp +++ b/content/media/gmp/GMPParent.cpp @@ -174,6 +174,49 @@ GMPParent::LoadProcess() return NS_OK; } +void +AbortWaitingForGMPAsyncShutdown(nsITimer* aTimer, void* aClosure) +{ + NS_WARNING("Timed out waiting for GMP async shutdown!"); + GMPParent* parent = reinterpret_cast(aClosure); + nsRefPtr service = + GeckoMediaPluginService::GetGeckoMediaPluginService(); + if (service) { + service->AsyncShutdownComplete(parent); + } +} + +nsresult +GMPParent::EnsureAsyncShutdownTimeoutSet() +{ + if (mAsyncShutdownTimeout) { + return NS_OK; + } + + nsresult rv; + mAsyncShutdownTimeout = do_CreateInstance(NS_TIMER_CONTRACTID, &rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Set timer to abort waiting for plugin to shutdown if it takes + // too long. + rv = mAsyncShutdownTimeout->SetTarget(mGMPThread); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + int32_t timeout = GMP_DEFAULT_ASYNC_SHUTDONW_TIMEOUT; + nsRefPtr service = + GeckoMediaPluginService::GetGeckoMediaPluginService(); + if (service) { + timeout = service->AsyncShutdownTimeoutMs(); + } + return mAsyncShutdownTimeout->InitWithFuncCallback( + &AbortWaitingForGMPAsyncShutdown, this, timeout, + nsITimer::TYPE_ONE_SHOT); +} + void GMPParent::CloseIfUnused() { @@ -199,7 +242,8 @@ GMPParent::CloseIfUnused() LOGD(("%s::%s: %p sending async shutdown notification", __CLASS__, __FUNCTION__, this)); mAsyncShutdownInProgress = true; - if (!SendBeginAsyncShutdown()) { + if (!SendBeginAsyncShutdown() || + NS_FAILED(EnsureAsyncShutdownTimeoutSet())) { AbortAsyncShutdown(); } } @@ -219,6 +263,11 @@ GMPParent::AbortAsyncShutdown() MOZ_ASSERT(GMPThread() == NS_GetCurrentThread()); LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this)); + if (mAsyncShutdownTimeout) { + mAsyncShutdownTimeout->Cancel(); + mAsyncShutdownTimeout = nullptr; + } + if (!mAsyncShutdownRequired || !mAsyncShutdownInProgress) { return; } @@ -289,6 +338,8 @@ GMPParent::Shutdown() LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this)); MOZ_ASSERT(GMPThread() == NS_GetCurrentThread()); + MOZ_ASSERT(!mAsyncShutdownTimeout, "Should have canceled shutdown timeout"); + if (mAbnormalShutdownInProgress) { return; } diff --git a/content/media/gmp/GMPParent.h b/content/media/gmp/GMPParent.h index cb3c7cca738a..346f026fc1e6 100644 --- a/content/media/gmp/GMPParent.h +++ b/content/media/gmp/GMPParent.h @@ -170,6 +170,8 @@ private: virtual bool RecvAsyncShutdownComplete() MOZ_OVERRIDE; virtual bool RecvAsyncShutdownRequired() MOZ_OVERRIDE; + nsresult EnsureAsyncShutdownTimeoutSet(); + GMPState mState; nsCOMPtr mDirectory; // plugin directory on disk nsString mName; // base name of plugin on disk, UTF-16 because used for paths @@ -188,6 +190,7 @@ private: nsTArray> mTimers; nsTArray> mStorage; nsCOMPtr mGMPThread; + nsCOMPtr mAsyncShutdownTimeout; // GMP Thread only. // NodeId the plugin is assigned to, or empty if the the plugin is not // assigned to a NodeId. nsAutoCString mNodeId; diff --git a/content/media/gmp/GMPService.cpp b/content/media/gmp/GMPService.cpp index d73f9f4395e7..4172fc5042c2 100644 --- a/content/media/gmp/GMPService.cpp +++ b/content/media/gmp/GMPService.cpp @@ -136,8 +136,8 @@ GeckoMediaPluginService::GetGeckoMediaPluginService() NS_IMPL_ISUPPORTS(GeckoMediaPluginService, mozIGeckoMediaPluginService, nsIObserver) -#define GMP_DEFAULT_ASYNC_SHUTDONW_TIMEOUT 3000 static int32_t sMaxAsyncShutdownWaitMs = 0; +static bool sHaveSetTimeoutPrefCache = false; GeckoMediaPluginService::GeckoMediaPluginService() : mMutex("GeckoMediaPluginService::mMutex") @@ -147,9 +147,8 @@ GeckoMediaPluginService::GeckoMediaPluginService() , mWaitingForPluginsAsyncShutdown(false) { MOZ_ASSERT(NS_IsMainThread()); - static bool setTimeoutPrefCache = false; - if (!setTimeoutPrefCache) { - setTimeoutPrefCache = true; + if (!sHaveSetTimeoutPrefCache) { + sHaveSetTimeoutPrefCache = true; Preferences::AddIntVarCache(&sMaxAsyncShutdownWaitMs, "media.gmp.async-shutdown-timeout", GMP_DEFAULT_ASYNC_SHUTDONW_TIMEOUT); @@ -162,6 +161,13 @@ GeckoMediaPluginService::~GeckoMediaPluginService() MOZ_ASSERT(mAsyncShutdownPlugins.IsEmpty()); } +int32_t +GeckoMediaPluginService::AsyncShutdownTimeoutMs() +{ + MOZ_ASSERT(sHaveSetTimeoutPrefCache); + return sMaxAsyncShutdownWaitMs; +} + nsresult GeckoMediaPluginService::Init() { @@ -205,16 +211,6 @@ GeckoMediaPluginService::Init() return GetThread(getter_AddRefs(thread)); } -void -AbortWaitingForGMPAsyncShutdown(nsITimer* aTimer, void* aClosure) -{ - NS_WARNING("Timed out waiting for GMP async shutdown!"); - nsRefPtr service = sSingletonService.get(); - if (service) { - service->AbortAsyncShutdown(); - } -} - NS_IMETHODIMP GeckoMediaPluginService::Observe(nsISupports* aSubject, const char* aTopic, @@ -533,9 +529,11 @@ GeckoMediaPluginService::AsyncShutdownComplete(GMPParent* aParent) mAsyncShutdownPlugins.RemoveElement(aParent); if (mAsyncShutdownPlugins.IsEmpty() && mShuttingDownOnGMPThread) { - // The main thread is waiting for async shutdown of plugins, + // The main thread may be waiting for async shutdown of plugins, // which has completed. Break the main thread out of its waiting loop. - AbortAsyncShutdown(); + nsRefPtr task(NS_NewRunnableMethod( + this, &GeckoMediaPluginService::SetAsyncShutdownComplete)); + NS_DispatchToMainThread(task); } } @@ -546,47 +544,6 @@ GeckoMediaPluginService::SetAsyncShutdownComplete() mWaitingForPluginsAsyncShutdown = false; } -void -GeckoMediaPluginService::AbortAsyncShutdown() -{ - MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread); - for (size_t i = 0; i < mAsyncShutdownPlugins.Length(); i++) { - mAsyncShutdownPlugins[i]->AbortAsyncShutdown(); - } - mAsyncShutdownPlugins.Clear(); - if (mAsyncShutdownTimeout) { - mAsyncShutdownTimeout->Cancel(); - mAsyncShutdownTimeout = nullptr; - } - nsRefPtr task(NS_NewRunnableMethod( - this, &GeckoMediaPluginService::SetAsyncShutdownComplete)); - NS_DispatchToMainThread(task); -} - -nsresult -GeckoMediaPluginService::SetAsyncShutdownTimeout() -{ - MOZ_ASSERT(!mAsyncShutdownTimeout); - - nsresult rv; - mAsyncShutdownTimeout = do_CreateInstance(NS_TIMER_CONTRACTID, &rv); - if (NS_FAILED(rv)) { - NS_WARNING("Failed to create timer for async GMP shutdown"); - return NS_OK; - } - - // Set timer to abort waiting for plugins to shutdown if they take - // too long. - rv = mAsyncShutdownTimeout->SetTarget(mGMPThread); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return mAsyncShutdownTimeout->InitWithFuncCallback( - &AbortWaitingForGMPAsyncShutdown, nullptr, sMaxAsyncShutdownWaitMs, - nsITimer::TYPE_ONE_SHOT); -} - void GeckoMediaPluginService::UnloadPlugins() { @@ -607,16 +564,7 @@ GeckoMediaPluginService::UnloadPlugins() mPlugins.Clear(); } - if (!mAsyncShutdownPlugins.IsEmpty()) { - // We have plugins that require async shutdown. Set a timer to abort - // waiting if they take too long to shutdown. - if (NS_FAILED(SetAsyncShutdownTimeout())) { - mAsyncShutdownPlugins.Clear(); - } - } - if (mAsyncShutdownPlugins.IsEmpty()) { - mAsyncShutdownPlugins.Clear(); nsRefPtr task(NS_NewRunnableMethod( this, &GeckoMediaPluginService::SetAsyncShutdownComplete)); NS_DispatchToMainThread(task); diff --git a/content/media/gmp/GMPService.h b/content/media/gmp/GMPService.h index 6666aca0d8aa..fb0a98a0dc46 100644 --- a/content/media/gmp/GMPService.h +++ b/content/media/gmp/GMPService.h @@ -28,6 +28,8 @@ namespace gmp { class GMPParent; +#define GMP_DEFAULT_ASYNC_SHUTDONW_TIMEOUT 3000 + class GeckoMediaPluginService MOZ_FINAL : public mozIGeckoMediaPluginService , public nsIObserver { @@ -45,6 +47,8 @@ public: void AsyncShutdownComplete(GMPParent* aParent); void AbortAsyncShutdown(); + int32_t AsyncShutdownTimeoutMs(); + private: ~GeckoMediaPluginService(); @@ -122,7 +126,6 @@ private: MainThreadOnly mWaitingForPluginsAsyncShutdown; nsTArray> mAsyncShutdownPlugins; // GMP Thread only. - nsCOMPtr mAsyncShutdownTimeout; // GMP Thread only. #ifndef MOZ_WIDGET_GONK nsCOMPtr mStorageBaseDir;