зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1173634 - Report pending async shutdowns in shutdown hangs. r=cpearce
--HG-- extra : rebase_source : 1b3f4d52abf0bacd066848834728783e9069a545
This commit is contained in:
Родитель
f4ccd23df1
Коммит
b383286615
|
@ -34,6 +34,10 @@
|
|||
#include "nsHashKeys.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsISimpleEnumerator.h"
|
||||
#if defined(MOZ_CRASHREPORTER)
|
||||
#include "nsExceptionHandler.h"
|
||||
#endif
|
||||
#include <limits>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
@ -79,7 +83,7 @@ static bool sHaveSetTimeoutPrefCache = false;
|
|||
GeckoMediaPluginServiceParent::GeckoMediaPluginServiceParent()
|
||||
: mShuttingDown(false)
|
||||
, mScannedPluginOnDisk(false)
|
||||
, mWaitingForPluginsAsyncShutdown(false)
|
||||
, mWaitingForPluginsSyncShutdown(false)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!sHaveSetTimeoutPrefCache) {
|
||||
|
@ -210,10 +214,11 @@ GeckoMediaPluginServiceParent::Observe(nsISupports* aSubject,
|
|||
// back to the GMPParent, which then registers the GMPParent by calling
|
||||
// GMPService::AsyncShutdownNeeded().
|
||||
//
|
||||
// On shutdown, we set mWaitingForPluginsAsyncShutdown to true, and then
|
||||
// On shutdown, we set mWaitingForPluginsSyncShutdown to true, and then
|
||||
// call UnloadPlugins on the GMPThread, and process events on the main
|
||||
// thread until an event sets mWaitingForPluginsAsyncShutdown=false on
|
||||
// the main thread.
|
||||
// thread until 1. An event sets mWaitingForPluginsSyncShutdown=false on
|
||||
// the main thread; then 2. All async-shutdown plugins have indicated
|
||||
// they have completed shutdown.
|
||||
//
|
||||
// UnloadPlugins() sends close messages for all plugins' API objects to
|
||||
// the GMP interfaces in the child process, and then sends the async
|
||||
|
@ -221,14 +226,14 @@ GeckoMediaPluginServiceParent::Observe(nsISupports* aSubject,
|
|||
// shutdown, it calls GMPAsyncShutdownHost::ShutdownComplete(), which
|
||||
// sends a message back to the parent, which calls
|
||||
// GMPService::AsyncShutdownComplete(). If all plugins requiring async
|
||||
// shutdown have called AsyncShutdownComplete() we stick an event on the
|
||||
// main thread to set mWaitingForPluginsAsyncShutdown=false. We must use
|
||||
// an event to do this, as we must ensure the main thread processes an
|
||||
// shutdown have called AsyncShutdownComplete() we stick a dummy event on
|
||||
// the main thread, where the list of pending plugins is checked. We must
|
||||
// use an event to do this, as we must ensure the main thread processes an
|
||||
// event to run its loop. This will unblock the main thread, and shutdown
|
||||
// of other components will proceed.
|
||||
//
|
||||
// We set a timer in UnloadPlugins(), and abort waiting for async
|
||||
// shutdown if the GMPs are taking too long to shutdown.
|
||||
// During shutdown, each GMPParent starts a timer, and pretends shutdown
|
||||
// is complete if it is taking too long.
|
||||
//
|
||||
// We shutdown in "profile-change-teardown", as the profile dir is
|
||||
// still writable then, and it's required for GMPStorage. We block the
|
||||
|
@ -238,7 +243,7 @@ GeckoMediaPluginServiceParent::Observe(nsISupports* aSubject,
|
|||
// GMPStorage needs to work up until the shutdown-complete notification
|
||||
// arrives from the GMP process.
|
||||
|
||||
mWaitingForPluginsAsyncShutdown = true;
|
||||
mWaitingForPluginsSyncShutdown = true;
|
||||
|
||||
nsCOMPtr<nsIThread> gmpThread;
|
||||
{
|
||||
|
@ -249,18 +254,56 @@ GeckoMediaPluginServiceParent::Observe(nsISupports* aSubject,
|
|||
}
|
||||
|
||||
if (gmpThread) {
|
||||
LOGD(("%s::%s Starting to unload plugins, waiting for first sync shutdown..."
|
||||
, __CLASS__, __FUNCTION__));
|
||||
gmpThread->Dispatch(
|
||||
NS_NewRunnableMethod(this,
|
||||
&GeckoMediaPluginServiceParent::UnloadPlugins),
|
||||
NS_DISPATCH_NORMAL);
|
||||
} else {
|
||||
MOZ_ASSERT(mPlugins.IsEmpty());
|
||||
mWaitingForPluginsAsyncShutdown = false;
|
||||
}
|
||||
|
||||
// Wait for plugins to do async shutdown...
|
||||
while (mWaitingForPluginsAsyncShutdown) {
|
||||
NS_ProcessNextEvent(NS_GetCurrentThread(), true);
|
||||
// Wait for UnloadPlugins() to do initial sync shutdown...
|
||||
while (mWaitingForPluginsSyncShutdown) {
|
||||
NS_ProcessNextEvent(NS_GetCurrentThread(), true);
|
||||
}
|
||||
|
||||
// Wait for other plugins (if any) to do async shutdown...
|
||||
auto syncShutdownPluginsRemaining =
|
||||
std::numeric_limits<decltype(mAsyncShutdownPlugins.Length())>::max();
|
||||
for (;;) {
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
if (mAsyncShutdownPlugins.IsEmpty()) {
|
||||
LOGD(("%s::%s Finished unloading all plugins"
|
||||
, __CLASS__, __FUNCTION__));
|
||||
#if defined(MOZ_CRASHREPORTER)
|
||||
CrashReporter::RemoveCrashReportAnnotation(
|
||||
NS_LITERAL_CSTRING("AsyncPluginShutdown"));
|
||||
#endif
|
||||
break;
|
||||
} else if (mAsyncShutdownPlugins.Length() < syncShutdownPluginsRemaining) {
|
||||
// First time here, or number of pending plugins has decreased.
|
||||
// -> Update list of pending plugins in crash report.
|
||||
syncShutdownPluginsRemaining = mAsyncShutdownPlugins.Length();
|
||||
LOGD(("%s::%s Still waiting for %d plugins to shutdown..."
|
||||
, __CLASS__, __FUNCTION__, (int)syncShutdownPluginsRemaining));
|
||||
#if defined(MOZ_CRASHREPORTER)
|
||||
nsAutoCString names;
|
||||
for (const auto& plugin : mAsyncShutdownPlugins) {
|
||||
if (!names.IsEmpty()) { names.Append(NS_LITERAL_CSTRING(", ")); }
|
||||
names.Append(plugin->GetDisplayName());
|
||||
}
|
||||
CrashReporter::AnnotateCrashReport(
|
||||
NS_LITERAL_CSTRING("AsyncPluginShutdown"),
|
||||
names);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
NS_ProcessNextEvent(NS_GetCurrentThread(), true);
|
||||
}
|
||||
} else {
|
||||
// GMP thread has already shutdown.
|
||||
MOZ_ASSERT(mPlugins.IsEmpty());
|
||||
mWaitingForPluginsSyncShutdown = false;
|
||||
}
|
||||
|
||||
} else if (!strcmp(NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, aTopic)) {
|
||||
|
@ -326,6 +369,7 @@ GeckoMediaPluginServiceParent::AsyncShutdownNeeded(GMPParent* aParent)
|
|||
LOGD(("%s::%s %p", __CLASS__, __FUNCTION__, aParent));
|
||||
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
|
||||
|
||||
MutexAutoLock lock(mMutex);
|
||||
MOZ_ASSERT(!mAsyncShutdownPlugins.Contains(aParent));
|
||||
mAsyncShutdownPlugins.AppendElement(aParent);
|
||||
}
|
||||
|
@ -333,39 +377,60 @@ GeckoMediaPluginServiceParent::AsyncShutdownNeeded(GMPParent* aParent)
|
|||
void
|
||||
GeckoMediaPluginServiceParent::AsyncShutdownComplete(GMPParent* aParent)
|
||||
{
|
||||
LOGD(("%s::%s %p", __CLASS__, __FUNCTION__, aParent));
|
||||
LOGD(("%s::%s %p '%s'", __CLASS__, __FUNCTION__,
|
||||
aParent, aParent->GetDisplayName().get()));
|
||||
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
|
||||
|
||||
mAsyncShutdownPlugins.RemoveElement(aParent);
|
||||
if (mAsyncShutdownPlugins.IsEmpty() && mShuttingDownOnGMPThread) {
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
mAsyncShutdownPlugins.RemoveElement(aParent);
|
||||
}
|
||||
|
||||
if (mShuttingDownOnGMPThread) {
|
||||
// The main thread may be waiting for async shutdown of plugins,
|
||||
// which has completed. Break the main thread out of its waiting loop.
|
||||
// one of which has completed. Wake up the main thread by sending a task.
|
||||
nsCOMPtr<nsIRunnable> task(NS_NewRunnableMethod(
|
||||
this, &GeckoMediaPluginServiceParent::SetAsyncShutdownComplete));
|
||||
this, &GeckoMediaPluginServiceParent::NotifyAsyncShutdownComplete));
|
||||
NS_DispatchToMainThread(task);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GeckoMediaPluginServiceParent::SetAsyncShutdownComplete()
|
||||
GeckoMediaPluginServiceParent::NotifyAsyncShutdownComplete()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mWaitingForPluginsAsyncShutdown = false;
|
||||
// Nothing to do, this task is just used to wake up the event loop in Observe().
|
||||
}
|
||||
|
||||
void
|
||||
GeckoMediaPluginServiceParent::NotifySyncShutdownComplete()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mWaitingForPluginsSyncShutdown = false;
|
||||
}
|
||||
|
||||
void
|
||||
GeckoMediaPluginServiceParent::UnloadPlugins()
|
||||
{
|
||||
LOGD(("%s::%s async_shutdown=%d", __CLASS__, __FUNCTION__,
|
||||
mAsyncShutdownPlugins.Length()));
|
||||
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
|
||||
|
||||
MOZ_ASSERT(!mShuttingDownOnGMPThread);
|
||||
mShuttingDownOnGMPThread = true;
|
||||
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
// Note: CloseActive is async; it will actually finish
|
||||
LOGD(("%s::%s plugins:%u including async:%u", __CLASS__, __FUNCTION__,
|
||||
mPlugins.Length(), mAsyncShutdownPlugins.Length()));
|
||||
#ifdef DEBUG
|
||||
for (const auto& plugin : mPlugins) {
|
||||
LOGD(("%s::%s plugin: '%s'", __CLASS__, __FUNCTION__,
|
||||
plugin->GetDisplayName().get()));
|
||||
}
|
||||
for (const auto& plugin : mAsyncShutdownPlugins) {
|
||||
LOGD(("%s::%s async plugin: '%s'", __CLASS__, __FUNCTION__,
|
||||
plugin->GetDisplayName().get()));
|
||||
}
|
||||
#endif
|
||||
// Note: CloseActive may be async; it could actually finish
|
||||
// shutting down when all the plugins have unloaded.
|
||||
for (size_t i = 0; i < mPlugins.Length(); i++) {
|
||||
mPlugins[i]->CloseActive(true);
|
||||
|
@ -373,11 +438,9 @@ GeckoMediaPluginServiceParent::UnloadPlugins()
|
|||
mPlugins.Clear();
|
||||
}
|
||||
|
||||
if (mAsyncShutdownPlugins.IsEmpty()) {
|
||||
nsCOMPtr<nsIRunnable> task(NS_NewRunnableMethod(
|
||||
this, &GeckoMediaPluginServiceParent::SetAsyncShutdownComplete));
|
||||
NS_DispatchToMainThread(task);
|
||||
}
|
||||
nsCOMPtr<nsIRunnable> task(NS_NewRunnableMethod(
|
||||
this, &GeckoMediaPluginServiceParent::NotifySyncShutdownComplete));
|
||||
NS_DispatchToMainThread(task);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -73,7 +73,8 @@ private:
|
|||
|
||||
void UnloadPlugins();
|
||||
void CrashPlugins();
|
||||
void SetAsyncShutdownComplete();
|
||||
void NotifySyncShutdownComplete();
|
||||
void NotifyAsyncShutdownComplete();
|
||||
|
||||
void LoadFromEnvironment();
|
||||
void ProcessPossiblePlugin(nsIFile* aDir);
|
||||
|
@ -138,6 +139,7 @@ private:
|
|||
// Protected by mMutex from the base class.
|
||||
nsTArray<nsRefPtr<GMPParent>> mPlugins;
|
||||
bool mShuttingDown;
|
||||
nsTArray<nsRefPtr<GMPParent>> mAsyncShutdownPlugins;
|
||||
|
||||
// True if we've inspected MOZ_GMP_PATH on the GMP thread and loaded any
|
||||
// plugins found there into mPlugins.
|
||||
|
@ -158,9 +160,7 @@ private:
|
|||
T mValue;
|
||||
};
|
||||
|
||||
MainThreadOnly<bool> mWaitingForPluginsAsyncShutdown;
|
||||
|
||||
nsTArray<nsRefPtr<GMPParent>> mAsyncShutdownPlugins; // GMP Thread only.
|
||||
MainThreadOnly<bool> mWaitingForPluginsSyncShutdown;
|
||||
|
||||
nsTArray<nsString> mPluginsWaitingForDeletion;
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче