Bug 1262898: Keep the GeckoChildProcessHost alive for the lifetime of the CompositorBridge and ImageBridge parent actors. r=jimm r=nical
MozReview-Commit-ID: 1rsWqRpbhgN
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -1994,24 +1994,16 @@ ContentParent::RecvDeallocateLayerTreeId
// You can't deallocate layer tree ids that you didn't allocate
KillHard("DeallocateLayerTreeId");
}
return true;
}
namespace {
-void
-DelayedDeleteSubprocess(GeckoChildProcessHost* aSubprocess)
-{
- XRE_GetIOMessageLoop()
- ->PostTask(FROM_HERE,
- new DeleteTask<GeckoChildProcessHost>(aSubprocess));
-}
-
// This runnable only exists to delegate ownership of the
// ContentParent to this runnable, until it's deleted by the event
// system.
struct DelayedDeleteContentParentTask : public nsRunnable
{
explicit DelayedDeleteContentParentTask(ContentParent* aObj) : mObj(aObj) { }
// No-op
@@ -2133,20 +2125,20 @@ ContentParent::ActorDestroy(ActorDestroy
MOZ_ASSERT(idleService);
RefPtr<ParentIdleListener> listener;
for (int32_t i = mIdleListeners.Length() - 1; i >= 0; --i) {
listener = static_cast<ParentIdleListener*>(mIdleListeners[i].get());
idleService->RemoveIdleObserver(listener, listener->mTime);
}
mIdleListeners.Clear();
- MessageLoop::current()->
- PostTask(FROM_HERE,
- NewRunnableFunction(DelayedDeleteSubprocess, mSubprocess));
- mSubprocess = nullptr;
+ if (mSubprocess) {
+ mSubprocess->DissociateActor();
+ mSubprocess = nullptr;
+ }
// IPDL rules require actors to live on past ActorDestroy, but it
// may be that the kungFuDeathGrip above is the last reference to
// |this|. If so, when we go out of scope here, we're deleted and
// all hell breaks loose.
//
// This runnable ensures that a reference to |this| lives on at
// least until after the current task finishes running.
@@ -3348,31 +3340,31 @@ ContentParent::DeallocPAPZParent(PAPZPar
{
return true;
}
PCompositorBridgeParent*
ContentParent::AllocPCompositorBridgeParent(mozilla::ipc::Transport* aTransport,
base::ProcessId aOtherProcess)
{
- return CompositorBridgeParent::Create(aTransport, aOtherProcess);
+ return CompositorBridgeParent::Create(aTransport, aOtherProcess, mSubprocess);
}
gfx::PVRManagerParent*
ContentParent::AllocPVRManagerParent(Transport* aTransport,
ProcessId aOtherProcess)
{
return gfx::VRManagerParent::CreateCrossProcess(aTransport, aOtherProcess);
}
PImageBridgeParent*
ContentParent::AllocPImageBridgeParent(mozilla::ipc::Transport* aTransport,
base::ProcessId aOtherProcess)
{
- return ImageBridgeParent::Create(aTransport, aOtherProcess);
+ return ImageBridgeParent::Create(aTransport, aOtherProcess, mSubprocess);
}
PBackgroundParent*
ContentParent::AllocPBackgroundParent(Transport* aTransport,
ProcessId aOtherProcess)
{
return BackgroundParent::Alloc(this, aTransport, aOtherProcess);
}
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -62,16 +62,17 @@
#include "nsXULAppAPI.h" // for XRE_GetIOMessageLoop
#include "nsIXULRuntime.h" // for BrowserTabsRemoteAutostart
#ifdef XP_WIN
#include "mozilla/layers/CompositorD3D11.h"
#include "mozilla/layers/CompositorD3D9.h"
#endif
#include "GeckoProfiler.h"
#include "mozilla/ipc/ProtocolTypes.h"
+#include "mozilla/ipc/GeckoChildProcessHost.h"
#include "mozilla/unused.h"
#include "mozilla/Hal.h"
#include "mozilla/HalTypes.h"
#include "mozilla/StaticPtr.h"
#ifdef MOZ_ENABLE_PROFILER_SPS
#include "ProfilerMarkers.h"
#endif
#include "mozilla/VsyncDispatcher.h"
@@ -1892,16 +1893,17 @@ class CrossProcessCompositorBridgeParent
public ShadowLayersManager
{
friend class CompositorBridgeParent;
NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION(CrossProcessCompositorBridgeParent)
public:
explicit CrossProcessCompositorBridgeParent(Transport* aTransport)
: mTransport(aTransport)
+ , mSubprocess(nullptr)
, mNotifyAfterRemotePaint(false)
, mDestroyCalled(false)
{
MOZ_ASSERT(NS_IsMainThread());
}
// IToplevelProtocol::CloneToplevel()
virtual IToplevelProtocol*
@@ -2024,16 +2026,17 @@ private:
void DeferredDestroy();
// There can be many CPCPs, and IPDL-generated code doesn't hold a
// reference to top-level actors. So we hold a reference to
// ourself. This is released (deferred) in ActorDestroy().
RefPtr<CrossProcessCompositorBridgeParent> mSelfRef;
Transport* mTransport;
+ ipc::GeckoChildProcessHost* mSubprocess;
RefPtr<CompositorThreadHolder> mCompositorThreadHolder;
// If true, we should send a RemotePaintIsReady message when the layer transaction
// is received
bool mNotifyAfterRemotePaint;
bool mDestroyCalled;
};
@@ -2182,23 +2185,28 @@ OpenCompositor(CrossProcessCompositorBri
Transport* aTransport, ProcessId aOtherPid,
MessageLoop* aIOLoop)
{
DebugOnly<bool> ok = aCompositor->Open(aTransport, aOtherPid, aIOLoop);
MOZ_ASSERT(ok);
}
/*static*/ PCompositorBridgeParent*
-CompositorBridgeParent::Create(Transport* aTransport, ProcessId aOtherPid)
+CompositorBridgeParent::Create(Transport* aTransport, ProcessId aOtherPid, GeckoChildProcessHost* aProcessHost)
{
gfxPlatform::InitLayersIPC();
RefPtr<CrossProcessCompositorBridgeParent> cpcp =
new CrossProcessCompositorBridgeParent(aTransport);
+ if (aProcessHost) {
+ cpcp->mSubprocess = aProcessHost;
+ aProcessHost->AssociateActor();
+ }
+
cpcp->mSelfRef = cpcp;
CompositorLoop()->PostTask(
FROM_HERE,
NewRunnableFunction(OpenCompositor, cpcp.get(),
aTransport, aOtherPid, XRE_GetIOMessageLoop()));
// The return value is just compared to null for success checking,
// we're not sharing a ref.
return cpcp.get();
@@ -2246,16 +2254,22 @@ CrossProcessCompositorBridgeParent::Recv
return true;
}
void
CrossProcessCompositorBridgeParent::ActorDestroy(ActorDestroyReason aWhy)
{
RefPtr<CompositorLRU> lru = CompositorLRU::GetSingleton();
lru->Remove(this);
+
+ if (mSubprocess) {
+ mSubprocess->DissociateActor();
+ mSubprocess = nullptr;
+ }
+
// We must keep this object alive untill the code handling message
// reception is finished on this thread.
MessageLoop::current()->PostTask(FROM_HERE,
NewRunnableMethod(this, &CrossProcessCompositorBridgeParent::DeferredDestroy));
}
PLayerTransactionParent*
CrossProcessCompositorBridgeParent::AllocPLayerTransactionParent(
@@ -2721,17 +2735,17 @@ CrossProcessCompositorBridgeParent::Clon
base::ProcessHandle aPeerProcess,
mozilla::ipc::ProtocolCloneContext* aCtx)
{
for (unsigned int i = 0; i < aFds.Length(); i++) {
if (aFds[i].protocolId() == (unsigned)GetProtocolId()) {
Transport* transport = OpenDescriptor(aFds[i].fd(),
Transport::MODE_SERVER);
PCompositorBridgeParent* compositor =
- CompositorBridgeParent::Create(transport, base::GetProcId(aPeerProcess));
+ CompositorBridgeParent::Create(transport, base::GetProcId(aPeerProcess), mSubprocess);
compositor->CloneManagees(this, aCtx);
compositor->IToplevelProtocol::SetTransport(transport);
// The reference to the compositor thread is held in OnChannelConnected().
// We need to do this for cloned actors, too.
compositor->OnChannelConnected(base::GetProcId(aPeerProcess));
return compositor;
}
}
--- a/gfx/layers/ipc/CompositorBridgeParent.h
+++ b/gfx/layers/ipc/CompositorBridgeParent.h
@@ -43,16 +43,20 @@ class CancelableTask;
class MessageLoop;
class nsIWidget;
namespace mozilla {
namespace gfx {
class DrawTarget;
} // namespace gfx
+namespace ipc {
+class GeckoChildProcessHost;
+} // namespace ipc
+
namespace layers {
class APZCTreeManager;
class AsyncCompositionManager;
class Compositor;
class CompositorBridgeParent;
class LayerManagerComposite;
class LayerTransactionParent;
@@ -407,17 +411,17 @@ public:
*/
static APZCTreeManager* GetAPZCTreeManager(uint64_t aLayersId);
/**
* A new child process has been configured to push transactions
* directly to us. Transport is to its thread context.
*/
static PCompositorBridgeParent*
- Create(Transport* aTransport, ProcessId aOtherProcess);
+ Create(Transport* aTransport, ProcessId aOtherProcess, mozilla::ipc::GeckoChildProcessHost* aProcessHost);
struct LayerTreeState {
LayerTreeState();
~LayerTreeState();
RefPtr<Layer> mRoot;
RefPtr<GeckoContentController> mController;
CompositorBridgeParent* mParent;
LayerManagerComposite* mLayerManager;
--- a/gfx/layers/ipc/ImageBridgeParent.cpp
+++ b/gfx/layers/ipc/ImageBridgeParent.cpp
@@ -12,16 +12,17 @@
#include "base/task.h" // for CancelableTask, DeleteTask, etc
#include "base/tracked.h" // for FROM_HERE
#include "mozilla/gfx/Point.h" // for IntSize
#include "mozilla/Hal.h" // for hal::SetCurrentThreadPriority()
#include "mozilla/HalTypes.h" // for hal::THREAD_PRIORITY_COMPOSITOR
#include "mozilla/ipc/MessageChannel.h" // for MessageChannel, etc
#include "mozilla/ipc/ProtocolUtils.h"
#include "mozilla/ipc/Transport.h" // for Transport
+#include "mozilla/ipc/GeckoChildProcessHost.h"
#include "mozilla/media/MediaSystemResourceManagerParent.h" // for MediaSystemResourceManagerParent
#include "mozilla/layers/CompositableTransactionParent.h"
#include "mozilla/layers/CompositorBridgeParent.h" // for CompositorBridgeParent
#include "mozilla/layers/LayerManagerComposite.h"
#include "mozilla/layers/LayersMessages.h" // for EditReply
#include "mozilla/layers/LayersSurfaces.h" // for PGrallocBufferParent
#include "mozilla/layers/PCompositableParent.h"
#include "mozilla/layers/PImageBridgeParent.h"
@@ -54,16 +55,17 @@ CompositorThreadHolder* GetCompositorThr
ImageBridgeParent::ImageBridgeParent(MessageLoop* aLoop,
Transport* aTransport,
ProcessId aChildProcessId)
: mMessageLoop(aLoop)
, mTransport(aTransport)
, mSetChildThreadPriority(false)
, mClosed(false)
+ , mSubprocess(nullptr)
{
MOZ_ASSERT(NS_IsMainThread());
sMainLoop = MessageLoop::current();
// top-level actors must be destroyed on the main thread.
SetMessageLoopToPostDestructionTo(sMainLoop);
// creates the map only if it has not been created already, so it is safe
@@ -95,16 +97,21 @@ ImageBridgeParent::~ImageBridgeParent()
}
void
ImageBridgeParent::ActorDestroy(ActorDestroyReason aWhy)
{
// Can't alloc/dealloc shmems from now on.
mClosed = true;
+ if (mSubprocess) {
+ mSubprocess->DissociateActor();
+ mSubprocess = nullptr;
+ }
+
MessageLoop::current()->PostTask(
FROM_HERE,
NewRunnableMethod(this, &ImageBridgeParent::DeferredDestroy));
// It is very important that this method gets called at shutdown (be it a clean
// or an abnormal shutdown), because DeferredDestroy is what clears mSelfRef.
// If mSelfRef is not null and ActorDestroy is not called, the ImageBridgeParent
// is leaked which causes the CompositorThreadHolder to be leaked and
@@ -189,20 +196,26 @@ static void
ConnectImageBridgeInParentProcess(ImageBridgeParent* aBridge,
Transport* aTransport,
base::ProcessId aOtherPid)
{
aBridge->Open(aTransport, aOtherPid, XRE_GetIOMessageLoop(), ipc::ParentSide);
}
/*static*/ PImageBridgeParent*
-ImageBridgeParent::Create(Transport* aTransport, ProcessId aChildProcessId)
+ImageBridgeParent::Create(Transport* aTransport, ProcessId aChildProcessId, GeckoChildProcessHost* aProcessHost)
{
MessageLoop* loop = CompositorBridgeParent::CompositorLoop();
RefPtr<ImageBridgeParent> bridge = new ImageBridgeParent(loop, aTransport, aChildProcessId);
+
+ if (aProcessHost) {
+ bridge->mSubprocess = aProcessHost;
+ aProcessHost->AssociateActor();
+ }
+
loop->PostTask(FROM_HERE,
NewRunnableFunction(ConnectImageBridgeInParentProcess,
bridge.get(), aTransport, aChildProcessId));
return bridge.get();
}
bool ImageBridgeParent::RecvWillClose()
{
@@ -354,17 +367,17 @@ IToplevelProtocol*
ImageBridgeParent::CloneToplevel(const InfallibleTArray<ProtocolFdMapping>& aFds,
base::ProcessHandle aPeerProcess,
mozilla::ipc::ProtocolCloneContext* aCtx)
{
for (unsigned int i = 0; i < aFds.Length(); i++) {
if (aFds[i].protocolId() == unsigned(GetProtocolId())) {
Transport* transport = OpenDescriptor(aFds[i].fd(),
Transport::MODE_SERVER);
- PImageBridgeParent* bridge = Create(transport, base::GetProcId(aPeerProcess));
+ PImageBridgeParent* bridge = Create(transport, base::GetProcId(aPeerProcess), mSubprocess);
bridge->CloneManagees(this, aCtx);
bridge->IToplevelProtocol::SetTransport(transport);
// The reference to the compositor thread is held in OnChannelConnected().
// We need to do this for cloned actors, too.
bridge->OnChannelConnected(base::GetProcId(aPeerProcess));
return bridge;
}
}
--- a/gfx/layers/ipc/ImageBridgeParent.h
+++ b/gfx/layers/ipc/ImageBridgeParent.h
@@ -24,16 +24,17 @@ class MessageLoop;
namespace base {
class Thread;
} // namespace base
namespace mozilla {
namespace ipc {
class Shmem;
+class GeckoChildProcessHost;
} // namespace ipc
namespace layers {
/**
* ImageBridgeParent is the manager Protocol of ImageContainerParent.
* It's purpose is mainly to setup the IPDL connection. Most of the
* interesting stuff is in ImageContainerParent.
@@ -51,17 +52,17 @@ public:
ImageBridgeParent(MessageLoop* aLoop, Transport* aTransport, ProcessId aChildProcessId);
~ImageBridgeParent();
virtual ShmemAllocator* AsShmemAllocator() override { return this; }
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
static PImageBridgeParent*
- Create(Transport* aTransport, ProcessId aChildProcessId);
+ Create(Transport* aTransport, ProcessId aChildProcessId, ipc::GeckoChildProcessHost* aProcessHost);
// CompositableParentManager
virtual void SendFenceHandleIfPresent(PTextureParent* aTexture) override;
virtual void SendAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessage) override;
virtual base::ProcessId GetChildProcessId() override
{
@@ -151,16 +152,18 @@ private:
Transport* mTransport;
// This keeps us alive until ActorDestroy(), at which point we do a
// deferred destruction of ourselves.
RefPtr<ImageBridgeParent> mSelfRef;
bool mSetChildThreadPriority;
bool mClosed;
+ ipc::GeckoChildProcessHost* mSubprocess;
+
/**
* Map of all living ImageBridgeParent instances
*/
static std::map<base::ProcessId, ImageBridgeParent*> sImageBridges;
static MessageLoop* sMainLoop;
RefPtr<CompositorThreadHolder> mCompositorThreadHolder;
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -874,18 +874,18 @@ gfxPlatform::ShutdownLayersIPC()
}
sLayersIPCIsUp = false;
if (XRE_IsContentProcess()) {
gfx::VRManagerChild::ShutDown();
// cf bug 1215265.
if (gfxPrefs::ChildProcessShutdown()) {
+ layers::CompositorBridgeChild::ShutDown();
layers::ImageBridgeChild::ShutDown();
- layers::CompositorBridgeChild::ShutDown();
}
} else if (XRE_IsParentProcess()) {
gfx::VRManagerChild::ShutDown();
layers::ImageBridgeChild::ShutDown();
layers::CompositorBridgeChild::ShutDown();
--- a/ipc/glue/GeckoChildProcessHost.cpp
+++ b/ipc/glue/GeckoChildProcessHost.cpp
@@ -107,16 +107,17 @@ GeckoChildProcessHost::GeckoChildProcess
mEnableSandboxLogging(false),
mSandboxLevel(0),
#endif
mDelegate(nullptr),
mChildProcessHandle(0)
#if defined(MOZ_WIDGET_COCOA)
, mChildTask(MACH_PORT_NULL)
#endif
+ , mAssociatedActors(1)
{
MOZ_COUNT_CTOR(GeckoChildProcessHost);
}
GeckoChildProcessHost::~GeckoChildProcessHost()
{
AssertIOThread();
@@ -468,16 +469,38 @@ GeckoChildProcessHost::SetAlreadyDead()
if (mChildProcessHandle &&
mChildProcessHandle != kInvalidProcessHandle) {
base::CloseProcessHandle(mChildProcessHandle);
}
mChildProcessHandle = 0;
}
+namespace {
+
+void
+DelayedDeleteSubprocess(GeckoChildProcessHost* aSubprocess)
+{
+ XRE_GetIOMessageLoop()
+ ->PostTask(FROM_HERE,
+ new DeleteTask<GeckoChildProcessHost>(aSubprocess));
+}
+
+}
+
+void
+GeckoChildProcessHost::DissociateActor()
+{
+ if (!--mAssociatedActors) {
+ MessageLoop::current()->
+ PostTask(FROM_HERE,
+ NewRunnableFunction(DelayedDeleteSubprocess, this));
+ }
+}
+
int32_t GeckoChildProcessHost::mChildCounter = 0;
void
GeckoChildProcessHost::SetChildLogName(const char* varName, const char* origLogName)
{
// We currently have no portable way to launch child with environment
// different than parent. So temporarily change NSPR_LOG_FILE so child
// inherits value we want it to have. (NSPR only looks at NSPR_LOG_FILE at
--- a/ipc/glue/GeckoChildProcessHost.h
+++ b/ipc/glue/GeckoChildProcessHost.h
@@ -5,16 +5,17 @@
#ifndef __IPC_GLUE_GECKOCHILDPROCESSHOST_H__
#define __IPC_GLUE_GECKOCHILDPROCESSHOST_H__
#include "base/file_path.h"
#include "base/process_util.h"
#include "base/waitable_event.h"
#include "chrome/common/child_process_host.h"
+#include "mozilla/Atomics.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/ipc/FileDescriptor.h"
#include "mozilla/Monitor.h"
#include "mozilla/StaticPtr.h"
#include "nsCOMPtr.h"
#include "nsXULAppAPI.h" // for GeckoProcessType
#include "nsString.h"
@@ -120,16 +121,21 @@ public:
* Must run on the IO thread. Cause the OS process to exit and
* ensure its OS resources are cleaned up.
*/
void Join();
// For bug 943174: Skip the EnsureProcessTerminated call in the destructor.
void SetAlreadyDead();
+ // This associates an actor telling the process host to stay alive at least
+ // until DissociateActor has been called.
+ void AssociateActor() { mAssociatedActors++; }
+ void DissociateActor();
+
protected:
GeckoProcessType mProcessType;
ChildPrivileges mPrivileges;
Monitor mMonitor;
FilePath mProcessPath;
// This value must be accessed while holding mMonitor.
enum {
@@ -197,16 +203,20 @@ private:
// channel, there's a small window of time in which *we* might still
// be the channel listener, and receive messages. That's bad
// because we have no idea what to do with those messages. So queue
// them here until we hand off the eventual listener.
//
// FIXME/cjones: this strongly indicates bad design. Shame on us.
std::queue<IPC::Message> mQueue;
+ // This tracks how many actors are associated with this process that require
+ // it to stay alive and have not yet been destroyed.
+ Atomic<int32_t> mAssociatedActors;
+
static uint32_t sNextUniqueID;
};
#ifdef MOZ_NUWA_PROCESS
class GeckoExistingProcessHost final : public GeckoChildProcessHost
{
public:
GeckoExistingProcessHost(GeckoProcessType aProcessType,
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -4491,18 +4491,16 @@ pref("gfx.direct2d.disabled", false);
// blacklisting
pref("gfx.direct2d.force-enabled", false);
pref("layers.prefer-opengl", false);
pref("layers.prefer-d3d9", false);
pref("layers.d3d11.force-warp", false);
pref("layers.d3d11.disable-warp", true);
-// cf. Bug 1215265
-pref("layers.child-process-shutdown", false);
#endif
// Force all possible layers to be always active layers
pref("layers.force-active", false);
// Never use gralloc surfaces, even when they're available on this
// platform and are the optimal surface type.
pref("layers.gralloc.disable", false);