зеркало из https://github.com/mozilla/gecko-dev.git
Bug 924622 - Make sure gfx's ipc shutdown happens before shutting down xpcom threads. r=bsmedberg, sotaro
This commit is contained in:
Родитель
1c7db51a21
Коммит
7c865568d2
|
@ -169,8 +169,10 @@ CompositorChild::ActorDestroy(ActorDestroyReason aWhy)
|
|||
NS_RUNTIMEABORT("ActorDestroy by IPC channel failure at CompositorChild");
|
||||
}
|
||||
#endif
|
||||
|
||||
sCompositor = nullptr;
|
||||
if (sCompositor) {
|
||||
sCompositor->Release();
|
||||
sCompositor = nullptr;
|
||||
}
|
||||
// We don't want to release the ref to sCompositor here, during
|
||||
// cleanup, because that will cause it to be deleted while it's
|
||||
// still being used. So defer the deletion to after it's not in
|
||||
|
|
|
@ -86,6 +86,9 @@ static MessageLoop* sMainLoop = nullptr;
|
|||
static PlatformThreadId sCompositorThreadID = 0;
|
||||
static MessageLoop* sCompositorLoop = nullptr;
|
||||
|
||||
// See ImageBridgeChild.cpp
|
||||
void ReleaseImageBridgeParentSingleton();
|
||||
|
||||
static void DeferredDeleteCompositorParent(CompositorParent* aNowReadyToDie)
|
||||
{
|
||||
aNowReadyToDie->Release();
|
||||
|
@ -94,6 +97,7 @@ static void DeferredDeleteCompositorParent(CompositorParent* aNowReadyToDie)
|
|||
static void DeleteCompositorThread()
|
||||
{
|
||||
if (NS_IsMainThread()){
|
||||
ReleaseImageBridgeParentSingleton();
|
||||
delete sCompositorThread;
|
||||
sCompositorThread = nullptr;
|
||||
sCompositorLoop = nullptr;
|
||||
|
@ -1243,6 +1247,7 @@ RemoveIndirectTree(uint64_t aId)
|
|||
void
|
||||
CrossProcessCompositorParent::ActorDestroy(ActorDestroyReason aWhy)
|
||||
{
|
||||
fprintf(stderr, " --- CrossProcessCompositorParent ActorDestroy\n");
|
||||
MessageLoop::current()->PostTask(
|
||||
FROM_HERE,
|
||||
NewRunnableMethod(this, &CrossProcessCompositorParent::DeferredDestroy));
|
||||
|
@ -1395,6 +1400,8 @@ CrossProcessCompositorParent::GetCompositionManager(LayerTransactionParent* aLay
|
|||
void
|
||||
CrossProcessCompositorParent::DeferredDestroy()
|
||||
{
|
||||
|
||||
fprintf(stderr, " --- CrossProcessCompositorParent DeferredDestroy\n");
|
||||
CrossProcessCompositorParent* self;
|
||||
mSelfRef.forget(&self);
|
||||
|
||||
|
@ -1405,6 +1412,8 @@ CrossProcessCompositorParent::DeferredDestroy()
|
|||
|
||||
CrossProcessCompositorParent::~CrossProcessCompositorParent()
|
||||
{
|
||||
fprintf(stderr, " --- CrossProcessCompositorParent destructor\n");
|
||||
|
||||
XRE_GetIOMessageLoop()->PostTask(FROM_HERE,
|
||||
new DeleteTask<Transport>(mTransport));
|
||||
}
|
||||
|
|
|
@ -165,29 +165,50 @@ static StaticRefPtr<ImageBridgeChild> sImageBridgeChildSingleton;
|
|||
static StaticRefPtr<ImageBridgeParent> sImageBridgeParentSingleton;
|
||||
static Thread *sImageBridgeChildThread = nullptr;
|
||||
|
||||
void ReleaseImageBridgeParentSingleton() {
|
||||
sImageBridgeParentSingleton = nullptr;
|
||||
}
|
||||
|
||||
|
||||
// dispatched function
|
||||
static void StopImageBridgeSync(ReentrantMonitor *aBarrier, bool *aDone)
|
||||
static void ImageBridgeShutdownStep1(ReentrantMonitor *aBarrier, bool *aDone)
|
||||
{
|
||||
ReentrantMonitorAutoEnter autoMon(*aBarrier);
|
||||
|
||||
NS_ABORT_IF_FALSE(InImageBridgeChildThread(),
|
||||
"Should be in ImageBridgeChild thread.");
|
||||
if (sImageBridgeChildSingleton) {
|
||||
sImageBridgeChildSingleton->SendStop();
|
||||
// Force all managed protocols to shut themselves down cleanly
|
||||
InfallibleTArray<PCompositableChild*> compositables;
|
||||
sImageBridgeChildSingleton->ManagedPCompositableChild(compositables);
|
||||
for (int i = compositables.Length() - 1; i >= 0; --i) {
|
||||
CompositableClient::FromIPDLActor(compositables[i])->Destroy();
|
||||
}
|
||||
InfallibleTArray<PTextureChild*> textures;
|
||||
sImageBridgeChildSingleton->ManagedPTextureChild(textures);
|
||||
for (int i = textures.Length() - 1; i >= 0; --i) {
|
||||
TextureClient::AsTextureClient(textures[i])->ForceRemove();
|
||||
}
|
||||
sImageBridgeChildSingleton->SendWillStop();
|
||||
sImageBridgeChildSingleton->MarkShutDown();
|
||||
// From now on, no message can be sent through the image bridge from the
|
||||
// client side except the final Stop message.
|
||||
}
|
||||
*aDone = true;
|
||||
aBarrier->NotifyAll();
|
||||
}
|
||||
|
||||
// dispatched function
|
||||
static void DeleteImageBridgeSync(ReentrantMonitor *aBarrier, bool *aDone)
|
||||
static void ImageBridgeShutdownStep2(ReentrantMonitor *aBarrier, bool *aDone)
|
||||
{
|
||||
ReentrantMonitorAutoEnter autoMon(*aBarrier);
|
||||
|
||||
NS_ABORT_IF_FALSE(InImageBridgeChildThread(),
|
||||
"Should be in ImageBridgeChild thread.");
|
||||
|
||||
sImageBridgeChildSingleton->SendStop();
|
||||
|
||||
sImageBridgeChildSingleton = nullptr;
|
||||
sImageBridgeParentSingleton = nullptr;
|
||||
*aDone = true;
|
||||
aBarrier->NotifyAll();
|
||||
}
|
||||
|
@ -212,6 +233,7 @@ static void ConnectImageBridge(ImageBridgeChild * child, ImageBridgeParent * par
|
|||
}
|
||||
|
||||
ImageBridgeChild::ImageBridgeChild()
|
||||
: mShuttingDown(false)
|
||||
{
|
||||
mTxn = new CompositableTransaction();
|
||||
}
|
||||
|
@ -220,10 +242,18 @@ ImageBridgeChild::~ImageBridgeChild()
|
|||
delete mTxn;
|
||||
}
|
||||
|
||||
void
|
||||
ImageBridgeChild::MarkShutDown()
|
||||
{
|
||||
MOZ_ASSERT(!mShuttingDown);
|
||||
mShuttingDown = true;
|
||||
}
|
||||
|
||||
void
|
||||
ImageBridgeChild::Connect(CompositableClient* aCompositable)
|
||||
{
|
||||
MOZ_ASSERT(aCompositable);
|
||||
MOZ_ASSERT(!mShuttingDown);
|
||||
uint64_t id = 0;
|
||||
PCompositableChild* child =
|
||||
SendPCompositableConstructor(aCompositable->GetTextureInfo(), &id);
|
||||
|
@ -234,6 +264,7 @@ ImageBridgeChild::Connect(CompositableClient* aCompositable)
|
|||
PCompositableChild*
|
||||
ImageBridgeChild::AllocPCompositableChild(const TextureInfo& aInfo, uint64_t* aID)
|
||||
{
|
||||
MOZ_ASSERT(!mShuttingDown);
|
||||
return CompositableClient::CreateIPDLActor();
|
||||
}
|
||||
|
||||
|
@ -297,6 +328,17 @@ static void ReleaseImageClientNow(ImageClient* aClient)
|
|||
// static
|
||||
void ImageBridgeChild::DispatchReleaseImageClient(ImageClient* aClient)
|
||||
{
|
||||
if (!IsCreated()) {
|
||||
// CompositableClient::Release should normally happen in the ImageBridgeChild
|
||||
// thread because it usually generate some IPDL messages.
|
||||
// However, if we take this branch it means that the ImageBridgeChild
|
||||
// has already shut down, along with the CompositableChild, which means no
|
||||
// message will be sent and it is safe to run this code from any thread.
|
||||
MOZ_ASSERT(aClient->GetIPDLActor() == nullptr);
|
||||
aClient->Release();
|
||||
return;
|
||||
}
|
||||
|
||||
sImageBridgeChildSingleton->GetMessageLoop()->PostTask(
|
||||
FROM_HERE,
|
||||
NewRunnableFunction(&ReleaseImageClientNow, aClient));
|
||||
|
@ -311,6 +353,17 @@ static void ReleaseTextureClientNow(TextureClient* aClient)
|
|||
// static
|
||||
void ImageBridgeChild::DispatchReleaseTextureClient(TextureClient* aClient)
|
||||
{
|
||||
if (!IsCreated()) {
|
||||
// TextureClient::Release should normally happen in the ImageBridgeChild
|
||||
// thread because it usually generate some IPDL messages.
|
||||
// However, if we take this branch it means that the ImageBridgeChild
|
||||
// has already shut down, along with the TextureChild, which means no
|
||||
// message will be sent and it is safe to run this code from any thread.
|
||||
MOZ_ASSERT(aClient->GetIPDLActor() == nullptr);
|
||||
aClient->Release();
|
||||
return;
|
||||
}
|
||||
|
||||
sImageBridgeChildSingleton->GetMessageLoop()->PostTask(
|
||||
FROM_HERE,
|
||||
NewRunnableFunction(&ReleaseTextureClientNow, aClient));
|
||||
|
@ -330,6 +383,10 @@ static void UpdateImageClientNow(ImageClient* aClient, ImageContainer* aContaine
|
|||
void ImageBridgeChild::DispatchImageClientUpdate(ImageClient* aClient,
|
||||
ImageContainer* aContainer)
|
||||
{
|
||||
if (!IsCreated()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (InImageBridgeChildThread()) {
|
||||
UpdateImageClientNow(aClient, aContainer);
|
||||
return;
|
||||
|
@ -357,7 +414,11 @@ static void FlushAllImagesSync(ImageClient* aClient, ImageContainer* aContainer,
|
|||
//static
|
||||
void ImageBridgeChild::FlushAllImages(ImageClient* aClient, ImageContainer* aContainer, bool aExceptFront)
|
||||
{
|
||||
if (!IsCreated()) {
|
||||
return;
|
||||
}
|
||||
MOZ_ASSERT(aClient);
|
||||
MOZ_ASSERT(!sImageBridgeChildSingleton->mShuttingDown);
|
||||
MOZ_ASSERT(!InImageBridgeChildThread());
|
||||
if (InImageBridgeChildThread()) {
|
||||
NS_ERROR("ImageBridgeChild::FlushAllImages() is called on ImageBridge thread.");
|
||||
|
@ -376,6 +437,7 @@ void ImageBridgeChild::FlushAllImages(ImageClient* aClient, ImageContainer* aCon
|
|||
void
|
||||
ImageBridgeChild::BeginTransaction()
|
||||
{
|
||||
MOZ_ASSERT(!mShuttingDown);
|
||||
MOZ_ASSERT(mTxn->Finished(), "uncommitted txn?");
|
||||
mTxn->Begin();
|
||||
}
|
||||
|
@ -397,6 +459,7 @@ private:
|
|||
void
|
||||
ImageBridgeChild::EndTransaction()
|
||||
{
|
||||
MOZ_ASSERT(!mShuttingDown);
|
||||
MOZ_ASSERT(!mTxn->Finished(), "forgot BeginTransaction?");
|
||||
|
||||
AutoEndTransaction _(mTxn);
|
||||
|
@ -491,9 +554,34 @@ ImageBridgeChild::StartUpInChildProcess(Transport* aTransport,
|
|||
|
||||
void ImageBridgeChild::ShutDown()
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on the main Thread!");
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Should be on the main Thread!");
|
||||
if (ImageBridgeChild::IsCreated()) {
|
||||
ImageBridgeChild::DestroyBridge();
|
||||
MOZ_ASSERT(!sImageBridgeChildSingleton->mShuttingDown);
|
||||
|
||||
{
|
||||
ReentrantMonitor barrier("ImageBridge ShutdownStep1 lock");
|
||||
ReentrantMonitorAutoEnter autoMon(barrier);
|
||||
|
||||
bool done = false;
|
||||
sImageBridgeChildSingleton->GetMessageLoop()->PostTask(FROM_HERE,
|
||||
NewRunnableFunction(&ImageBridgeShutdownStep1, &barrier, &done));
|
||||
while (!done) {
|
||||
barrier.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
ReentrantMonitor barrier("ImageBridge ShutdownStep2 lock");
|
||||
ReentrantMonitorAutoEnter autoMon(barrier);
|
||||
|
||||
bool done = false;
|
||||
sImageBridgeChildSingleton->GetMessageLoop()->PostTask(FROM_HERE,
|
||||
NewRunnableFunction(&ImageBridgeShutdownStep2, &barrier, &done));
|
||||
while (!done) {
|
||||
barrier.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
delete sImageBridgeChildThread;
|
||||
sImageBridgeChildThread = nullptr;
|
||||
}
|
||||
|
@ -517,35 +605,6 @@ bool ImageBridgeChild::StartUpOnThread(Thread* aThread)
|
|||
}
|
||||
}
|
||||
|
||||
void ImageBridgeChild::DestroyBridge()
|
||||
{
|
||||
if (!IsCreated()) {
|
||||
return;
|
||||
}
|
||||
NS_ABORT_IF_FALSE(!InImageBridgeChildThread(),
|
||||
"This method must not be called in this thread.");
|
||||
// ...because we are about to dispatch synchronous messages to the
|
||||
// ImageBridgeChild thread.
|
||||
|
||||
ReentrantMonitor barrier("ImageBridgeDestroyTask lock");
|
||||
ReentrantMonitorAutoEnter autoMon(barrier);
|
||||
|
||||
bool done = false;
|
||||
sImageBridgeChildSingleton->GetMessageLoop()->PostTask(FROM_HERE,
|
||||
NewRunnableFunction(&StopImageBridgeSync, &barrier, &done));
|
||||
while (!done) {
|
||||
barrier.Wait();
|
||||
}
|
||||
|
||||
done = false;
|
||||
sImageBridgeChildSingleton->GetMessageLoop()->PostTask(FROM_HERE,
|
||||
NewRunnableFunction(&DeleteImageBridgeSync, &barrier, &done));
|
||||
while (!done) {
|
||||
barrier.Wait();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool InImageBridgeChildThread()
|
||||
{
|
||||
return ImageBridgeChild::IsCreated() &&
|
||||
|
@ -595,6 +654,7 @@ ImageBridgeChild::CreateImageClient(CompositableType aType)
|
|||
TemporaryRef<ImageClient>
|
||||
ImageBridgeChild::CreateImageClientNow(CompositableType aType)
|
||||
{
|
||||
MOZ_ASSERT(!sImageBridgeChildSingleton->mShuttingDown);
|
||||
RefPtr<ImageClient> client
|
||||
= ImageClient::CreateImageClient(aType, this, TextureFlags::NO_FLAGS);
|
||||
MOZ_ASSERT(client, "failed to create ImageClient");
|
||||
|
@ -609,6 +669,7 @@ ImageBridgeChild::AllocUnsafeShmem(size_t aSize,
|
|||
ipc::SharedMemory::SharedMemoryType aType,
|
||||
ipc::Shmem* aShmem)
|
||||
{
|
||||
MOZ_ASSERT(!mShuttingDown);
|
||||
if (InImageBridgeChildThread()) {
|
||||
return PImageBridgeChild::AllocUnsafeShmem(aSize, aType, aShmem);
|
||||
} else {
|
||||
|
@ -621,6 +682,7 @@ ImageBridgeChild::AllocShmem(size_t aSize,
|
|||
ipc::SharedMemory::SharedMemoryType aType,
|
||||
ipc::Shmem* aShmem)
|
||||
{
|
||||
MOZ_ASSERT(!mShuttingDown);
|
||||
if (InImageBridgeChildThread()) {
|
||||
return PImageBridgeChild::AllocShmem(aSize, aType, aShmem);
|
||||
} else {
|
||||
|
@ -729,6 +791,7 @@ PTextureChild*
|
|||
ImageBridgeChild::AllocPTextureChild(const SurfaceDescriptor&,
|
||||
const TextureFlags&)
|
||||
{
|
||||
MOZ_ASSERT(!mShuttingDown);
|
||||
return TextureClient::CreateIPDLActor();
|
||||
}
|
||||
|
||||
|
@ -775,6 +838,7 @@ PTextureChild*
|
|||
ImageBridgeChild::CreateTexture(const SurfaceDescriptor& aSharedData,
|
||||
TextureFlags aFlags)
|
||||
{
|
||||
MOZ_ASSERT(!mShuttingDown);
|
||||
return SendPTextureConstructor(aSharedData, aFlags);
|
||||
}
|
||||
|
||||
|
@ -782,6 +846,7 @@ void
|
|||
ImageBridgeChild::RemoveTextureFromCompositable(CompositableClient* aCompositable,
|
||||
TextureClient* aTexture)
|
||||
{
|
||||
MOZ_ASSERT(!mShuttingDown);
|
||||
if (aTexture->GetFlags() & TextureFlags::DEALLOCATE_CLIENT) {
|
||||
mTxn->AddEdit(OpRemoveTexture(nullptr, aCompositable->GetIPDLActor(),
|
||||
nullptr, aTexture->GetIPDLActor()));
|
||||
|
@ -821,6 +886,7 @@ static void RemoveTextureSync(TextureClient* aTexture, ReentrantMonitor* aBarrie
|
|||
void ImageBridgeChild::RemoveTexture(TextureClient* aTexture)
|
||||
{
|
||||
if (InImageBridgeChildThread()) {
|
||||
MOZ_ASSERT(!mShuttingDown);
|
||||
aTexture->ForceRemove();
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -131,15 +131,6 @@ public:
|
|||
*/
|
||||
static bool StartUpOnThread(base::Thread* aThread);
|
||||
|
||||
/**
|
||||
* Destroys The ImageBridge protcol.
|
||||
*
|
||||
* The actual destruction happens synchronously on the ImageBridgeChild thread
|
||||
* which means that if this function is called from another thread, the current
|
||||
* thread will be paused until the destruction is done.
|
||||
*/
|
||||
static void DestroyBridge();
|
||||
|
||||
/**
|
||||
* Returns true if the singleton has been created.
|
||||
*
|
||||
|
@ -310,6 +301,7 @@ public:
|
|||
|
||||
void SendPendingAsyncMessge();
|
||||
|
||||
void MarkShutDown();
|
||||
protected:
|
||||
ImageBridgeChild();
|
||||
bool DispatchAllocShmemInternal(size_t aSize,
|
||||
|
@ -318,6 +310,7 @@ protected:
|
|||
bool aUnsafe);
|
||||
|
||||
CompositableTransaction* mTxn;
|
||||
bool mShuttingDown;
|
||||
};
|
||||
|
||||
} // layers
|
||||
|
|
|
@ -138,7 +138,7 @@ ImageBridgeParent::Create(Transport* aTransport, ProcessId aOtherProcess)
|
|||
return bridge.get();
|
||||
}
|
||||
|
||||
bool ImageBridgeParent::RecvStop()
|
||||
bool ImageBridgeParent::RecvWillStop()
|
||||
{
|
||||
// If there is any texture still alive we have to force it to deallocate the
|
||||
// device data (GL textures, etc.) now because shortly after SenStop() returns
|
||||
|
@ -153,6 +153,13 @@ bool ImageBridgeParent::RecvStop()
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ImageBridgeParent::RecvStop()
|
||||
{
|
||||
// Nothing to do. This message just serves as synchronization between the
|
||||
// child and parent threads during shutdown.
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint64_t GenImageContainerID() {
|
||||
static uint64_t sNextImageID = 1;
|
||||
|
||||
|
|
|
@ -78,7 +78,10 @@ public:
|
|||
virtual bool
|
||||
RecvChildAsyncMessages(const InfallibleTArray<AsyncChildMessageData>& aMessages) MOZ_OVERRIDE;
|
||||
|
||||
bool RecvStop() MOZ_OVERRIDE;
|
||||
// Shutdown step 1
|
||||
virtual bool RecvWillStop() MOZ_OVERRIDE;
|
||||
// Shutdown step 2
|
||||
virtual bool RecvStop() MOZ_OVERRIDE;
|
||||
|
||||
MessageLoop * GetMessageLoop();
|
||||
|
||||
|
|
|
@ -36,13 +36,16 @@ parent:
|
|||
sync Update(CompositableOperation[] ops) returns (EditReply[] reply);
|
||||
async UpdateNoSwap(CompositableOperation[] ops);
|
||||
|
||||
// First step of the destruction sequence. This puts all the ImageContainerParents
|
||||
// in a state in which they can't send asynchronous messages to their child
|
||||
// counterpart so as to not race with the upcomming __delete__ message.
|
||||
// In the child side, the __delete__ messages are not sent right after Stop,
|
||||
// they are scheduled in the ImageBridgeChild's message queue in order to ensure
|
||||
// that all the messages from the parent side have been received and processed
|
||||
// before sending __delete__.
|
||||
// First step of the destruction sequence. This puts ImageBridge
|
||||
// in a state in which it can't send asynchronous messages
|
||||
// so as to not race with the upcomming Stop message and destruction.
|
||||
// In the child side, the Stop message is not sent right after WillStop,
|
||||
// it is scheduled in the ImageBridgeChild's message queue in order to ensure
|
||||
// that all of the messages from the parent side have been received and processed
|
||||
// before sending Stop, and that after Stop returns, there is no message in
|
||||
// flight on any side and we can safely destroy the channel and threads.
|
||||
sync WillStop();
|
||||
// Second step
|
||||
sync Stop();
|
||||
|
||||
sync PCompositable(TextureInfo aInfo) returns (uint64_t id);
|
||||
|
|
|
@ -500,16 +500,6 @@ gfxPlatform::Shutdown()
|
|||
mozilla::gl::GLContextProviderEGL::Shutdown();
|
||||
#endif
|
||||
|
||||
// This will block this thread untill the ImageBridge protocol is completely
|
||||
// deleted.
|
||||
ImageBridgeChild::ShutDown();
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
SharedBufferManagerChild::ShutDown();
|
||||
#endif
|
||||
CompositorParent::ShutDown();
|
||||
|
||||
AsyncTransactionTracker::Finalize();
|
||||
|
||||
delete gGfxPlatformPrefsLock;
|
||||
|
||||
gfxPrefs::DestroySingleton();
|
||||
|
|
|
@ -14,10 +14,12 @@
|
|||
#include "nsXPCOMPrivate.h"
|
||||
#include "nsXPCOMCIDInternal.h"
|
||||
|
||||
#include "prlink.h"
|
||||
|
||||
#include "mozilla/layers/ImageBridgeChild.h"
|
||||
#include "mozilla/layers/CompositorParent.h"
|
||||
#include "mozilla/layers/AsyncTransactionTracker.h"
|
||||
#include "mozilla/layers/SharedBufferManagerChild.h"
|
||||
|
||||
#include "prlink.h"
|
||||
|
||||
#include "nsCycleCollector.h"
|
||||
#include "nsObserverList.h"
|
||||
|
@ -795,6 +797,13 @@ ShutdownXPCOM(nsIServiceManager* servMgr)
|
|||
}
|
||||
}
|
||||
|
||||
// This must happen after the shutdown of media and widgets, which
|
||||
// are triggered by the NS_XPCOM_SHUTDOWN_OBSERVER_ID notification.
|
||||
layers::ImageBridgeChild::ShutDown();
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
layers::SharedBufferManagerChild::ShutDown();
|
||||
#endif
|
||||
|
||||
NS_ProcessPendingEvents(thread);
|
||||
mozilla::scache::StartupCache::DeleteSingleton();
|
||||
if (observerService)
|
||||
|
@ -802,6 +811,9 @@ ShutdownXPCOM(nsIServiceManager* servMgr)
|
|||
NotifyObservers(nullptr, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID,
|
||||
nullptr);
|
||||
|
||||
layers::CompositorParent::ShutDown();
|
||||
layers::AsyncTransactionTracker::Finalize();
|
||||
|
||||
gXPCOMThreadsShutDown = true;
|
||||
NS_ProcessPendingEvents(thread);
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче