зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1035056 part 3 - Implement GMPAsyncShutdown interface. r=jesup
This commit is contained in:
Родитель
4da7fabe5c
Коммит
31a75b4662
|
@ -37,7 +37,8 @@ namespace mozilla {
|
|||
namespace gmp {
|
||||
|
||||
GMPChild::GMPChild()
|
||||
: mLib(nullptr)
|
||||
: mAsyncShutdown(nullptr)
|
||||
, mLib(nullptr)
|
||||
, mGetAPIFunc(nullptr)
|
||||
, mGMPMessageLoop(MessageLoop::current())
|
||||
{
|
||||
|
@ -183,6 +184,14 @@ GMPChild::LoadPluginLibrary(const std::string& aPluginPath)
|
|||
return false;
|
||||
}
|
||||
|
||||
void* sh = nullptr;
|
||||
GMPAsyncShutdownHost* host = static_cast<GMPAsyncShutdownHost*>(this);
|
||||
GMPErr err = mGetAPIFunc("async-shutdown", host, &sh);
|
||||
if (err == GMPNoErr && sh) {
|
||||
mAsyncShutdown = reinterpret_cast<GMPAsyncShutdown*>(sh);
|
||||
SendAsyncShutdownRequired();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -397,5 +406,24 @@ GMPChild::RecvCrashPluginNow()
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
GMPChild::RecvBeginAsyncShutdown()
|
||||
{
|
||||
MOZ_ASSERT(mGMPMessageLoop == MessageLoop::current());
|
||||
if (mAsyncShutdown) {
|
||||
mAsyncShutdown->BeginShutdown();
|
||||
} else {
|
||||
ShutdownComplete();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
GMPChild::ShutdownComplete()
|
||||
{
|
||||
MOZ_ASSERT(mGMPMessageLoop == MessageLoop::current());
|
||||
SendAsyncShutdownComplete();
|
||||
}
|
||||
|
||||
} // namespace gmp
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -9,14 +9,16 @@
|
|||
#include "mozilla/gmp/PGMPChild.h"
|
||||
#include "GMPSharedMemManager.h"
|
||||
#include "GMPTimerChild.h"
|
||||
#include "gmp-async-shutdown.h"
|
||||
#include "gmp-entrypoints.h"
|
||||
#include "prlink.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gmp {
|
||||
|
||||
class GMPChild : public PGMPChild,
|
||||
public GMPSharedMem
|
||||
class GMPChild : public PGMPChild
|
||||
, public GMPSharedMem
|
||||
, public GMPAsyncShutdownHost
|
||||
{
|
||||
public:
|
||||
GMPChild();
|
||||
|
@ -39,6 +41,9 @@ public:
|
|||
// GMPSharedMem
|
||||
virtual void CheckThread() MOZ_OVERRIDE;
|
||||
|
||||
// GMPAsyncShutdownHost
|
||||
void ShutdownComplete() MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
virtual PCrashReporterChild* AllocPCrashReporterChild(const NativeThreadId& aThread) MOZ_OVERRIDE;
|
||||
virtual bool DeallocPCrashReporterChild(PCrashReporterChild*) MOZ_OVERRIDE;
|
||||
|
@ -63,10 +68,12 @@ private:
|
|||
virtual bool DeallocPGMPTimerChild(PGMPTimerChild* aActor) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool RecvCrashPluginNow() MOZ_OVERRIDE;
|
||||
virtual bool RecvBeginAsyncShutdown() MOZ_OVERRIDE;
|
||||
|
||||
virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
|
||||
virtual void ProcessingError(Result aWhat) MOZ_OVERRIDE;
|
||||
|
||||
GMPAsyncShutdown* mAsyncShutdown;
|
||||
nsRefPtr<GMPTimerChild> mTimerChild;
|
||||
|
||||
PRLibrary* mLib;
|
||||
|
|
|
@ -55,6 +55,8 @@ GMPParent::GMPParent()
|
|||
, mProcess(nullptr)
|
||||
, mDeleteProcessOnlyOnUnload(false)
|
||||
, mAbnormalShutdownInProgress(false)
|
||||
, mAsyncShutdownRequired(false)
|
||||
, mAsyncShutdownInProgress(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -149,6 +151,8 @@ void
|
|||
GMPParent::CloseIfUnused()
|
||||
{
|
||||
MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
|
||||
LOGD(("%s::%s: %p mAsyncShutdownRequired=%d", __CLASS__, __FUNCTION__, this,
|
||||
mAsyncShutdownRequired));
|
||||
|
||||
if ((mDeleteProcessOnlyOnUnload ||
|
||||
mState == GMPStateLoaded ||
|
||||
|
@ -157,8 +161,32 @@ GMPParent::CloseIfUnused()
|
|||
mVideoEncoders.IsEmpty() &&
|
||||
mDecryptors.IsEmpty() &&
|
||||
mAudioDecoders.IsEmpty()) {
|
||||
if (mAsyncShutdownRequired) {
|
||||
if (!mAsyncShutdownInProgress) {
|
||||
LOGD(("%s::%s: %p sending async shutdown notification", __CLASS__,
|
||||
__FUNCTION__, this));
|
||||
mAsyncShutdownInProgress = true;
|
||||
if (!SendBeginAsyncShutdown()) {
|
||||
AbortAsyncShutdown();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GMPParent::AbortAsyncShutdown()
|
||||
{
|
||||
MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
|
||||
LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this));
|
||||
|
||||
nsRefPtr<GMPParent> kungFuDeathGrip(this);
|
||||
mService->AsyncShutdownComplete(this);
|
||||
mAsyncShutdownRequired = false;
|
||||
mAsyncShutdownInProgress = false;
|
||||
CloseIfUnused();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -229,7 +257,6 @@ GMPParent::Shutdown()
|
|||
return;
|
||||
}
|
||||
|
||||
mState = GMPStateClosing;
|
||||
DeleteProcess();
|
||||
// XXX Get rid of mDeleteProcessOnlyOnUnload and this code when
|
||||
// Bug 1043671 is fixed
|
||||
|
@ -245,10 +272,13 @@ void
|
|||
GMPParent::DeleteProcess()
|
||||
{
|
||||
LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this));
|
||||
|
||||
if (mState != GMPStateClosing) {
|
||||
// Don't Close() twice!
|
||||
// Probably remove when bug 1043671 is resolved
|
||||
MOZ_ASSERT(mState == GMPStateClosing);
|
||||
mState = GMPStateClosing;
|
||||
Close();
|
||||
}
|
||||
mProcess->Delete();
|
||||
LOGD(("%s::%s: Shut down process %p", __CLASS__, __FUNCTION__, (void *) mProcess));
|
||||
mProcess = nullptr;
|
||||
|
@ -532,8 +562,15 @@ GMPParent::ActorDestroy(ActorDestroyReason aWhy)
|
|||
|
||||
// Normal Shutdown() will delete the process on unwind.
|
||||
if (AbnormalShutdown == aWhy) {
|
||||
mState = GMPStateClosing;
|
||||
nsRefPtr<GMPParent> self(this);
|
||||
if (mAsyncShutdownRequired) {
|
||||
mService->AsyncShutdownComplete(this);
|
||||
mAsyncShutdownRequired = false;
|
||||
}
|
||||
// Must not call Close() again in DeleteProcess(), as we'll recurse
|
||||
// infinitely if we do.
|
||||
MOZ_ASSERT(mState == GMPStateClosing);
|
||||
DeleteProcess();
|
||||
// Note: final destruction will be Dispatched to ourself
|
||||
mService->ReAddOnGMPThread(self);
|
||||
}
|
||||
|
@ -631,7 +668,7 @@ PGMPTimerParent*
|
|||
GMPParent::AllocPGMPTimerParent()
|
||||
{
|
||||
GMPTimerParent* p = new GMPTimerParent(GMPThread());
|
||||
NS_ADDREF(p); // Released in DeallocPGMPTimerParent.
|
||||
mTimers.AppendElement(p); // Released in DeallocPGMPTimerParent, or on shutdown.
|
||||
return p;
|
||||
}
|
||||
|
||||
|
@ -639,7 +676,8 @@ bool
|
|||
GMPParent::DeallocPGMPTimerParent(PGMPTimerParent* aActor)
|
||||
{
|
||||
GMPTimerParent* p = static_cast<GMPTimerParent*>(aActor);
|
||||
NS_RELEASE(p);
|
||||
p->Shutdown();
|
||||
mTimers.RemoveElement(p);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -806,5 +844,24 @@ GMPParent::SetOrigin(const nsAString& aOrigin)
|
|||
mOrigin = aOrigin;
|
||||
}
|
||||
|
||||
bool
|
||||
GMPParent::RecvAsyncShutdownRequired()
|
||||
{
|
||||
LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this));
|
||||
mAsyncShutdownRequired = true;
|
||||
mService->AsyncShutdownNeeded(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
GMPParent::RecvAsyncShutdownComplete()
|
||||
{
|
||||
LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this));
|
||||
|
||||
MOZ_ASSERT(mAsyncShutdownRequired);
|
||||
AbortAsyncShutdown();
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace gmp
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -126,6 +126,8 @@ public:
|
|||
// GMPSharedMem
|
||||
virtual void CheckThread() MOZ_OVERRIDE;
|
||||
|
||||
void AbortAsyncShutdown();
|
||||
|
||||
private:
|
||||
~GMPParent();
|
||||
nsRefPtr<GeckoMediaPluginService> mService;
|
||||
|
@ -156,6 +158,9 @@ private:
|
|||
virtual PGMPTimerParent* AllocPGMPTimerParent() MOZ_OVERRIDE;
|
||||
virtual bool DeallocPGMPTimerParent(PGMPTimerParent* aActor) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool RecvAsyncShutdownComplete() MOZ_OVERRIDE;
|
||||
virtual bool RecvAsyncShutdownRequired() MOZ_OVERRIDE;
|
||||
|
||||
GMPState mState;
|
||||
nsCOMPtr<nsIFile> mDirectory; // plugin directory on disk
|
||||
nsString mName; // base name of plugin on disk, UTF-16 because used for paths
|
||||
|
@ -171,10 +176,14 @@ private:
|
|||
nsTArray<nsRefPtr<GMPVideoEncoderParent>> mVideoEncoders;
|
||||
nsTArray<nsRefPtr<GMPDecryptorParent>> mDecryptors;
|
||||
nsTArray<nsRefPtr<GMPAudioDecoderParent>> mAudioDecoders;
|
||||
nsTArray<nsRefPtr<GMPTimerParent>> mTimers;
|
||||
nsCOMPtr<nsIThread> mGMPThread;
|
||||
// Origin the plugin is assigned to, or empty if the the plugin is not
|
||||
// assigned to an origin.
|
||||
nsAutoString mOrigin;
|
||||
|
||||
bool mAsyncShutdownRequired;
|
||||
bool mAsyncShutdownInProgress;
|
||||
};
|
||||
|
||||
} // namespace gmp
|
||||
|
|
|
@ -30,10 +30,12 @@ GMPProcessParent::GMPProcessParent(const std::string& aGMPPath)
|
|||
: GeckoChildProcessHost(GeckoProcessType_GMPlugin),
|
||||
mGMPPath(aGMPPath)
|
||||
{
|
||||
MOZ_COUNT_CTOR(GMPProcessParent);
|
||||
}
|
||||
|
||||
GMPProcessParent::~GMPProcessParent()
|
||||
{
|
||||
MOZ_COUNT_DTOR(GMPProcessParent);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#include "mozilla/unused.h"
|
||||
#include "GMPDecryptorParent.h"
|
||||
#include "GMPAudioDecoderParent.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "runnable_utils.h"
|
||||
#if defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX)
|
||||
#include "mozilla/Sandbox.h"
|
||||
|
@ -127,17 +129,29 @@ GeckoMediaPluginService::GetGeckoMediaPluginService()
|
|||
|
||||
NS_IMPL_ISUPPORTS(GeckoMediaPluginService, mozIGeckoMediaPluginService, nsIObserver)
|
||||
|
||||
#define GMP_DEFAULT_ASYNC_SHUTDONW_TIMEOUT 3000
|
||||
static int32_t sMaxAsyncShutdownWaitMs = 0;
|
||||
|
||||
GeckoMediaPluginService::GeckoMediaPluginService()
|
||||
: mMutex("GeckoMediaPluginService::mMutex")
|
||||
, mShuttingDown(false)
|
||||
, mShuttingDownOnGMPThread(false)
|
||||
, mWaitingForPluginsAsyncShutdown(false)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
static bool setTimeoutPrefCache = false;
|
||||
if (!setTimeoutPrefCache) {
|
||||
setTimeoutPrefCache = true;
|
||||
Preferences::AddIntVarCache(&sMaxAsyncShutdownWaitMs,
|
||||
"media.gmp.async-shutdown-timeout",
|
||||
GMP_DEFAULT_ASYNC_SHUTDONW_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
GeckoMediaPluginService::~GeckoMediaPluginService()
|
||||
{
|
||||
MOZ_ASSERT(mPlugins.IsEmpty());
|
||||
MOZ_ASSERT(mAsyncShutdownPlugins.IsEmpty());
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -147,7 +161,7 @@ GeckoMediaPluginService::Init()
|
|||
|
||||
nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService();
|
||||
MOZ_ASSERT(obsService);
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(obsService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false)));
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(obsService->AddObserver(this, "profile-change-teardown", false)));
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(obsService->AddObserver(this, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, false)));
|
||||
|
||||
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
|
||||
|
@ -160,11 +174,22 @@ GeckoMediaPluginService::Init()
|
|||
unused << GetThread(getter_AddRefs(thread));
|
||||
}
|
||||
|
||||
void
|
||||
AbortWaitingForGMPAsyncShutdown(nsITimer* aTimer, void* aClosure)
|
||||
{
|
||||
NS_WARNING("Timed out waiting for GMP async shutdown!");
|
||||
nsRefPtr<GeckoMediaPluginService> service = sSingletonService.get();
|
||||
if (service) {
|
||||
service->AbortAsyncShutdown();
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
GeckoMediaPluginService::Observe(nsISupports* aSubject,
|
||||
const char* aTopic,
|
||||
const char16_t* aSomeData)
|
||||
{
|
||||
LOGD(("%s::%s: %s", __CLASS__, __FUNCTION__, aTopic));
|
||||
if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
|
||||
nsCOMPtr<nsIPrefBranch> branch( do_QueryInterface(aSubject) );
|
||||
if (branch) {
|
||||
|
@ -184,7 +209,50 @@ GeckoMediaPluginService::Observe(nsISupports* aSubject,
|
|||
}
|
||||
}
|
||||
}
|
||||
} else if (!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, aTopic)) {
|
||||
} else if (!strcmp("profile-change-teardown", aTopic)) {
|
||||
|
||||
// How shutdown works:
|
||||
//
|
||||
// Some GMPs require time to do bookkeeping upon shutdown. These GMPs
|
||||
// need to be given time to access storage during shutdown. To signal
|
||||
// that time to shutdown is required, those GMPs implement the
|
||||
// GMPAsyncShutdown interface.
|
||||
//
|
||||
// When we startup the child process, we query the GMP for the
|
||||
// GMPAsyncShutdown interface, and if it's present, we send a message
|
||||
// back to the GMPParent, which then registers the GMPParent by calling
|
||||
// GMPService::AsyncShutdownNeeded().
|
||||
//
|
||||
// On shutdown, we set mWaitingForPluginsAsyncShutdown 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.
|
||||
//
|
||||
// UnloadPlugins() sends close messages for all plugins' API objects to
|
||||
// the GMP interfaces in the child process, and then sends the async
|
||||
// shutdown notifications to child GMPs. When a GMP has completed its
|
||||
// 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
|
||||
// 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.
|
||||
//
|
||||
// We shutdown in "profile-change-teardown", as the profile dir is
|
||||
// still writable then, and it's required for GMPStorage. We block the
|
||||
// shutdown process by spinning the main thread event loop until all GMPs
|
||||
// have shutdown, or timeout has occurred.
|
||||
//
|
||||
// GMPStorage needs to work up until the shutdown-complete notification
|
||||
// arrives from the GMP process.
|
||||
|
||||
mWaitingForPluginsAsyncShutdown = true;
|
||||
|
||||
nsCOMPtr<nsIThread> gmpThread;
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
@ -194,11 +262,18 @@ GeckoMediaPluginService::Observe(nsISupports* aSubject,
|
|||
}
|
||||
|
||||
if (gmpThread) {
|
||||
gmpThread->Dispatch(NS_NewRunnableMethod(this, &GeckoMediaPluginService::UnloadPlugins),
|
||||
NS_DISPATCH_SYNC);
|
||||
gmpThread->Dispatch(
|
||||
NS_NewRunnableMethod(this, &GeckoMediaPluginService::UnloadPlugins),
|
||||
NS_DISPATCH_NORMAL);
|
||||
} else {
|
||||
MOZ_ASSERT(mPlugins.IsEmpty());
|
||||
}
|
||||
|
||||
// Wait for plugins to do async shutdown...
|
||||
while (mWaitingForPluginsAsyncShutdown) {
|
||||
NS_ProcessNextEvent(NS_GetCurrentThread(), true);
|
||||
}
|
||||
|
||||
} else if (!strcmp(NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, aTopic)) {
|
||||
nsCOMPtr<nsIThread> gmpThread;
|
||||
{
|
||||
|
@ -383,14 +458,88 @@ GeckoMediaPluginService::GetGMPDecryptor(nsTArray<nsCString>* aTags,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
GeckoMediaPluginService::AsyncShutdownNeeded(GMPParent* aParent)
|
||||
{
|
||||
LOGD(("%s::%s %p", __CLASS__, __FUNCTION__, aParent));
|
||||
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
|
||||
|
||||
mAsyncShutdownPlugins.AppendElement(aParent);
|
||||
}
|
||||
|
||||
void
|
||||
GeckoMediaPluginService::AsyncShutdownComplete(GMPParent* aParent)
|
||||
{
|
||||
LOGD(("%s::%s %p", __CLASS__, __FUNCTION__, aParent));
|
||||
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
|
||||
|
||||
mAsyncShutdownPlugins.RemoveElement(aParent);
|
||||
if (mAsyncShutdownPlugins.IsEmpty() && mShuttingDownOnGMPThread) {
|
||||
// The main thread is waiting for async shutdown of plugins,
|
||||
// which has completed. Break the main thread out of its waiting loop.
|
||||
AbortAsyncShutdown();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GeckoMediaPluginService::SetAsyncShutdownComplete()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
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<nsIRunnable> 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()
|
||||
{
|
||||
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
|
||||
// shutting down when all the plugins have unloaded.
|
||||
|
@ -398,11 +547,28 @@ GeckoMediaPluginService::UnloadPlugins()
|
|||
mPlugins[i]->CloseActive(true);
|
||||
}
|
||||
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<nsIRunnable> task(NS_NewRunnableMethod(
|
||||
this, &GeckoMediaPluginService::SetAsyncShutdownComplete));
|
||||
NS_DispatchToMainThread(task);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GeckoMediaPluginService::CrashPlugins()
|
||||
{
|
||||
LOGD(("%s::%s", __CLASS__, __FUNCTION__));
|
||||
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
|
||||
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
@ -645,7 +811,11 @@ GeckoMediaPluginService::ReAddOnGMPThread(nsRefPtr<GMPParent>& aOld)
|
|||
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
|
||||
LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, (void*) aOld));
|
||||
|
||||
nsRefPtr<GMPParent> gmp = ClonePlugin(aOld);
|
||||
nsRefPtr<GMPParent> gmp;
|
||||
if (!mShuttingDownOnGMPThread) {
|
||||
// Don't re-add plugin if we're shutting down. Let the old plugin die.
|
||||
gmp = ClonePlugin(aOld);
|
||||
}
|
||||
// Note: both are now in the list
|
||||
// Until we give up the GMPThread, we're safe even if we unlock temporarily
|
||||
// since off-main-thread users just test for existance; they don't modify the list.
|
||||
|
|
|
@ -10,11 +10,12 @@
|
|||
#include "mozIGeckoMediaPluginService.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsTArray.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/Monitor.h"
|
||||
#include "nsString.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIThread.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsITimer.h"
|
||||
|
||||
template <class> struct already_AddRefed;
|
||||
|
||||
|
@ -36,6 +37,10 @@ public:
|
|||
NS_DECL_MOZIGECKOMEDIAPLUGINSERVICE
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
void AsyncShutdownNeeded(GMPParent* aParent);
|
||||
void AsyncShutdownComplete(GMPParent* aParent);
|
||||
void AbortAsyncShutdown();
|
||||
|
||||
private:
|
||||
~GeckoMediaPluginService();
|
||||
|
||||
|
@ -45,12 +50,16 @@ private:
|
|||
|
||||
void UnloadPlugins();
|
||||
void CrashPlugins();
|
||||
void SetAsyncShutdownComplete();
|
||||
|
||||
void LoadFromEnvironment();
|
||||
void ProcessPossiblePlugin(nsIFile* aDir);
|
||||
|
||||
void AddOnGMPThread(const nsAString& aSearchDir);
|
||||
void RemoveOnGMPThread(const nsAString& aSearchDir);
|
||||
|
||||
nsresult SetAsyncShutdownTimeout();
|
||||
|
||||
protected:
|
||||
friend class GMPParent;
|
||||
void ReAddOnGMPThread(nsRefPtr<GMPParent>& aOld);
|
||||
|
@ -80,6 +89,26 @@ private:
|
|||
nsCOMPtr<nsIThread> mGMPThread;
|
||||
bool mShuttingDown;
|
||||
bool mShuttingDownOnGMPThread;
|
||||
|
||||
template<typename T>
|
||||
class MainThreadOnly {
|
||||
public:
|
||||
MainThreadOnly(T aValue)
|
||||
: mValue(aValue)
|
||||
{}
|
||||
operator T&() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return mValue;
|
||||
}
|
||||
|
||||
private:
|
||||
T mValue;
|
||||
};
|
||||
|
||||
MainThreadOnly<bool> mWaitingForPluginsAsyncShutdown;
|
||||
|
||||
nsTArray<nsRefPtr<GMPParent>> mAsyncShutdownPlugins; // GMP Thread only.
|
||||
nsCOMPtr<nsITimer> mAsyncShutdownTimeout; // GMP Thread only.
|
||||
};
|
||||
|
||||
} // namespace gmp
|
||||
|
|
|
@ -28,12 +28,16 @@ parent:
|
|||
async PCrashReporter(NativeThreadId tid);
|
||||
async PGMPTimer();
|
||||
|
||||
async AsyncShutdownComplete();
|
||||
async AsyncShutdownRequired();
|
||||
|
||||
child:
|
||||
async PGMPAudioDecoder();
|
||||
async PGMPDecryptor();
|
||||
async PGMPVideoDecoder();
|
||||
async PGMPVideoEncoder();
|
||||
|
||||
async BeginAsyncShutdown();
|
||||
async CrashPluginNow();
|
||||
};
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче