Bug 1653060 - P4. Wait until all MediaRemoteDecoderManagerParent have closed before killing process. r=mattwoodrow.

We unfortunately can't use the AsyncShutdownService in either the GPU or RDD process.

So we add a little utility class AsyncBlockers that will resolve its promise once all services have deregistered from it.

We use it to temporily suspend the RDDParent or GPUParent from killing the process, up to 10s.

This allows for cleaner shutdown as the parent process doesn't guarantee the order in which processes are killed (even though it should).

Differential Revision: https://phabricator.services.mozilla.com/D90487
This commit is contained in:
Jean-Yves Avenard 2020-09-18 11:07:13 +00:00
parent e748bb2d7b
commit 298b6203a1
6 changed files with 181 additions and 58 deletions

View File

@ -53,7 +53,10 @@ RDDParent::RDDParent() : mLaunchTime(TimeStamp::Now()) { sRDDParent = this; }
RDDParent::~RDDParent() { sRDDParent = nullptr; }
/* static */
RDDParent* RDDParent::GetSingleton() { return sRDDParent; }
RDDParent* RDDParent::GetSingleton() {
MOZ_DIAGNOSTIC_ASSERT(sRDDParent);
return sRDDParent;
}
bool RDDParent::Init(base::ProcessId aParentPid, const char* aParentBuildID,
MessageLoop* aIOLoop, UniquePtr<IPC::Channel> aChannel) {
@ -210,23 +213,28 @@ void RDDParent::ActorDestroy(ActorDestroyReason aWhy) {
ProcessChild::QuickExit();
#endif
// Wait until all RemoteDecoderManagerParent have closed.
mShutdownBlockers.WaitUntilClear(10 * 1000 /* 10s timeout*/)
->Then(GetCurrentSerialEventTarget(), __func__, [this]() {
#if defined(XP_WIN)
RefPtr<DllServices> dllSvc(DllServices::Get());
dllSvc->DisableFull();
RefPtr<DllServices> dllSvc(DllServices::Get());
dllSvc->DisableFull();
#endif // defined(XP_WIN)
#ifdef MOZ_GECKO_PROFILER
if (mProfilerController) {
mProfilerController->Shutdown();
mProfilerController = nullptr;
}
if (mProfilerController) {
mProfilerController->Shutdown();
mProfilerController = nullptr;
}
#endif
RemoteDecoderManagerParent::ShutdownVideoBridge();
RemoteDecoderManagerParent::ShutdownVideoBridge();
gfxVars::Shutdown();
CrashReporterClient::DestroySingleton();
XRE_ShutdownChildProcess();
gfxVars::Shutdown();
CrashReporterClient::DestroySingleton();
XRE_ShutdownChildProcess();
});
}
} // namespace mozilla

View File

@ -8,6 +8,7 @@
#include "mozilla/PRDDParent.h"
#include "mozilla/RefPtr.h"
#include "mozilla/media/MediaUtils.h"
namespace mozilla {
@ -21,6 +22,8 @@ class RDDParent final : public PRDDParent {
static RDDParent* GetSingleton();
AsyncBlockers& AsyncShutdownService() { return mShutdownBlockers; }
bool Init(base::ProcessId aParentPid, const char* aParentBuildID,
MessageLoop* aIOLoop, UniquePtr<IPC::Channel> aChannel);
@ -51,6 +54,7 @@ class RDDParent final : public PRDDParent {
#ifdef MOZ_GECKO_PROFILER
RefPtr<ChildProfilerController> mProfilerController;
#endif
AsyncBlockers mShutdownBlockers;
};
} // namespace mozilla

View File

@ -16,6 +16,8 @@
#include "mozilla/SyncRunnable.h"
#include "mozilla/layers/ImageDataSerializer.h"
#include "mozilla/layers/VideoBridgeChild.h"
#include "mozilla/gfx/GPUParent.h"
#include "mozilla/RDDParent.h"
namespace mozilla {
@ -149,10 +151,18 @@ RemoteDecoderManagerParent::RemoteDecoderManagerParent(
nsISerialEventTarget* aThread)
: mThread(aThread) {
MOZ_COUNT_CTOR(RemoteDecoderManagerParent);
auto& registrar = XRE_IsGPUProcess()
? GPUParent::GetSingleton()->AsyncShutdownService()
: RDDParent::GetSingleton()->AsyncShutdownService();
registrar.Register(this);
}
RemoteDecoderManagerParent::~RemoteDecoderManagerParent() {
MOZ_COUNT_DTOR(RemoteDecoderManagerParent);
auto& registrar = XRE_IsGPUProcess()
? GPUParent::GetSingleton()->AsyncShutdownService()
: RDDParent::GetSingleton()->AsyncShutdownService();
registrar.Deregister(this);
}
void RemoteDecoderManagerParent::ActorDestroy(

View File

@ -7,9 +7,12 @@
#ifndef mozilla_MediaUtils_h
#define mozilla_MediaUtils_h
#include <map>
#include "mozilla/Assertions.h"
#include "mozilla/Monitor.h"
#include "mozilla/MozPromise.h"
#include "mozilla/Mutex.h"
#include "mozilla/RefPtr.h"
#include "mozilla/SharedThreadPool.h"
#include "mozilla/TaskQueue.h"
@ -293,6 +296,89 @@ AwaitAll(already_AddRefed<nsIEventTarget> aPool,
}
} // namespace media
/**
* AsyncBlockers provide a simple registration service that allows to suspend
* completion of a particular task until all registered entries have been
* cleared. This can be used to implement a similar service to
* nsAsyncShutdownService in processes where it wouldn't normally be available.
* This class is thread-safe.
*/
class AsyncBlockers {
public:
AsyncBlockers()
: mLock("AsyncRegistrar"),
mPromise(new GenericPromise::Private(__func__)) {}
void Register(void* aBlocker) {
MutexAutoLock lock(mLock);
if (mResolved) {
// Too late.
return;
}
mBlockers.insert({aBlocker, true});
}
void Deregister(void* aBlocker) {
MutexAutoLock lock(mLock);
if (mResolved) {
// Too late.
return;
}
auto it = mBlockers.find(aBlocker);
MOZ_ASSERT(it != mBlockers.end());
mBlockers.erase(it);
MaybeResolve();
}
RefPtr<GenericPromise> WaitUntilClear(uint32_t aTimeOutInMs = 0) {
if (!aTimeOutInMs) {
// We don't need to wait, resolve the promise right away.
MutexAutoLock lock(mLock);
if (!mResolved) {
mPromise->Resolve(true, __func__);
mResolved = true;
}
} else {
GetCurrentEventTarget()->DelayedDispatch(
NS_NewRunnableFunction("AsyncBlockers::WaitUntilClear",
[promise = mPromise]() {
// The AsyncBlockers object may have been
// deleted by now and the object isn't
// refcounted (nor do we want it to be). We
// can unconditionally resolve the promise
// even it has already been resolved as
// MozPromise are thread-safe and will just
// ignore the action if already resolved.
promise->Resolve(true, __func__);
}),
aTimeOutInMs);
}
return mPromise;
}
virtual ~AsyncBlockers() {
if (!mResolved) {
mPromise->Resolve(true, __func__);
}
}
private:
void MaybeResolve() {
mLock.AssertCurrentThreadOwns();
if (mResolved) {
return;
}
if (!mBlockers.empty()) {
return;
}
mPromise->Resolve(true, __func__);
mResolved = true;
}
Mutex mLock; // protects mBlockers and mResolved.
std::map<void*, bool> mBlockers;
bool mResolved = false;
const RefPtr<GenericPromise::Private> mPromise;
};
} // namespace mozilla
#endif // mozilla_MediaUtils_h

View File

@ -88,7 +88,10 @@ GPUParent::GPUParent() : mLaunchTime(TimeStamp::Now()) { sGPUParent = this; }
GPUParent::~GPUParent() { sGPUParent = nullptr; }
/* static */
GPUParent* GPUParent::GetSingleton() { return sGPUParent; }
GPUParent* GPUParent::GetSingleton() {
MOZ_DIAGNOSTIC_ASSERT(sGPUParent);
return sGPUParent;
}
bool GPUParent::Init(base::ProcessId aParentPid, const char* aParentBuildID,
MessageLoop* aIOLoop, UniquePtr<IPC::Channel> aChannel) {
@ -531,72 +534,80 @@ void GPUParent::ActorDestroy(ActorDestroyReason aWhy) {
ProcessChild::QuickExit();
}
#ifdef XP_WIN
wmf::MFShutdown();
#endif
#ifndef NS_FREE_PERMANENT_DATA
# ifdef XP_WIN
wmf::MFShutdown();
# endif
// No point in going through XPCOM shutdown because we don't keep persistent
// state.
ProcessChild::QuickExit();
#endif
#ifdef MOZ_GECKO_PROFILER
if (mProfilerController) {
mProfilerController->Shutdown();
mProfilerController = nullptr;
}
#endif
if (mVsyncBridge) {
mVsyncBridge->Shutdown();
mVsyncBridge = nullptr;
}
RemoteDecoderManagerParent::ShutdownVideoBridge();
CompositorThreadHolder::Shutdown();
// There is a case that RenderThread exists when gfxVars::UseWebRender() is
// false. This could happen when WebRender was fallbacked to compositor.
if (wr::RenderThread::Get()) {
wr::RenderThread::ShutDown();
}
// Wait until all RemoteDecoderManagerParent have closed.
mShutdownBlockers.WaitUntilClear(10 * 1000 /* 10s timeout*/)
->Then(GetCurrentSerialEventTarget(), __func__, [this]() {
#ifdef XP_WIN
if (widget::WinCompositorWindowThread::Get()) {
widget::WinCompositorWindowThread::ShutDown();
}
wmf::MFShutdown();
#endif
image::ImageMemoryReporter::ShutdownForWebRender();
#ifdef MOZ_GECKO_PROFILER
if (mProfilerController) {
mProfilerController->Shutdown();
mProfilerController = nullptr;
}
#endif
// Shut down the default GL context provider.
gl::GLContextProvider::Shutdown();
if (mVsyncBridge) {
mVsyncBridge->Shutdown();
mVsyncBridge = nullptr;
}
RemoteDecoderManagerParent::ShutdownVideoBridge();
CompositorThreadHolder::Shutdown();
// There is a case that RenderThread exists when gfxVars::UseWebRender()
// is false. This could happen when WebRender was fallbacked to
// compositor.
if (wr::RenderThread::Get()) {
wr::RenderThread::ShutDown();
}
#ifdef XP_WIN
if (widget::WinCompositorWindowThread::Get()) {
widget::WinCompositorWindowThread::ShutDown();
}
#endif
image::ImageMemoryReporter::ShutdownForWebRender();
// Shut down the default GL context provider.
gl::GLContextProvider::Shutdown();
#if defined(XP_WIN)
// The above shutdown calls operate on the available context providers on
// most platforms. Windows is a "special snowflake", though, and has three
// context providers available, so we have to shut all of them down.
// We should only support the default GL provider on Windows; then, this
// could go away. Unfortunately, we currently support WGL (the default) for
// WebGL on Optimus.
gl::GLContextProviderEGL::Shutdown();
// The above shutdown calls operate on the available context providers
// on most platforms. Windows is a "special snowflake", though, and has
// three context providers available, so we have to shut all of them
// down. We should only support the default GL provider on Windows;
// then, this could go away. Unfortunately, we currently support WGL
// (the default) for WebGL on Optimus.
gl::GLContextProviderEGL::Shutdown();
#endif
Factory::ShutDown();
Factory::ShutDown();
// We bypass gfxPlatform shutdown, so we must shutdown any libraries here
// that would normally be handled by it.
// We bypass gfxPlatform shutdown, so we must shutdown any libraries here
// that would normally be handled by it.
#ifdef NS_FREE_PERMANENT_DATA
SkGraphics::PurgeFontCache();
cairo_debug_reset_static_data();
SkGraphics::PurgeFontCache();
cairo_debug_reset_static_data();
#endif
#if defined(XP_WIN)
DeviceManagerDx::Shutdown();
DeviceManagerDx::Shutdown();
#endif
LayerTreeOwnerTracker::Shutdown();
gfxVars::Shutdown();
gfxConfig::Shutdown();
CrashReporterClient::DestroySingleton();
XRE_ShutdownChildProcess();
LayerTreeOwnerTracker::Shutdown();
gfxVars::Shutdown();
gfxConfig::Shutdown();
CrashReporterClient::DestroySingleton();
XRE_ShutdownChildProcess();
});
}
} // namespace mozilla::gfx

View File

@ -8,6 +8,7 @@
#include "mozilla/RefPtr.h"
#include "mozilla/gfx/PGPUParent.h"
#include "mozilla/media/MediaUtils.h"
namespace mozilla {
@ -25,6 +26,8 @@ class GPUParent final : public PGPUParent {
static GPUParent* GetSingleton();
AsyncBlockers& AsyncShutdownService() { return mShutdownBlockers; }
// Gets the name of the GPU process, in the format expected by about:memory.
// There must be a GPU process active, and the caller must be either in that
// process or the parent process.
@ -99,6 +102,7 @@ class GPUParent final : public PGPUParent {
#ifdef MOZ_GECKO_PROFILER
RefPtr<ChildProfilerController> mProfilerController;
#endif
AsyncBlockers mShutdownBlockers;
};
} // namespace gfx