Bug 924622 - Make sure gfx's ipc shutdown happens before shutting down xpcom threads. r=bsmedberg, sotaro
authorNicolas Silva <nical@mozilla.com>
Mon, 28 Apr 2014 16:03:12 +0200
changeset 180947 8008f2e4865e043bf8e0f13b9810d76bcb174de8
parent 180946 2fac94005f866f86901203dace773c262f325cc8
child 180948 ac7f491b04603937a7f725201b6eee627203b947
push id272
push userpvanderbeken@mozilla.com
push dateMon, 05 May 2014 16:31:18 +0000
reviewersbsmedberg, sotaro
bugs924622
milestone31.0a1
Bug 924622 - Make sure gfx's ipc shutdown happens before shutting down xpcom threads. r=bsmedberg, sotaro
gfx/layers/client/CompositableClient.cpp
gfx/layers/ipc/ImageBridgeChild.cpp
gfx/layers/ipc/ImageBridgeChild.h
gfx/thebes/gfxPlatform.cpp
xpcom/build/nsXPComInit.cpp
--- a/gfx/layers/client/CompositableClient.cpp
+++ b/gfx/layers/client/CompositableClient.cpp
@@ -174,10 +174,11 @@ CompositableClient::AddTextureClient(Tex
   return aClient->InitIPDLActor(mForwarder);
 }
 
 void
 CompositableClient::OnTransaction()
 {
 }
 
+
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/ipc/ImageBridgeChild.cpp
+++ b/gfx/layers/ipc/ImageBridgeChild.cpp
@@ -168,23 +168,43 @@ static Thread *sImageBridgeChildThread =
 // dispatched function
 static void StopImageBridgeSync(ReentrantMonitor *aBarrier, bool *aDone)
 {
   ReentrantMonitorAutoEnter autoMon(*aBarrier);
 
   NS_ABORT_IF_FALSE(InImageBridgeChildThread(),
                     "Should be in ImageBridgeChild thread.");
   if (sImageBridgeChildSingleton) {
-
+    // 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->SendStop();
+    sImageBridgeChildSingleton->MarkShutDown();
+    // From now on, no message can be sent through the image bridge from the
+    // client side.
   }
   *aDone = true;
   aBarrier->NotifyAll();
 }
 
+void
+ImageBridgeChild::MarkShutDown()
+{
+  MOZ_ASSERT(!mShuttingDown);
+  mShuttingDown = true;
+}
+
 // dispatched function
 static void DeleteImageBridgeSync(ReentrantMonitor *aBarrier, bool *aDone)
 {
   ReentrantMonitorAutoEnter autoMon(*aBarrier);
 
   NS_ABORT_IF_FALSE(InImageBridgeChildThread(),
                     "Should be in ImageBridgeChild thread.");
   sImageBridgeChildSingleton = nullptr;
@@ -246,38 +266,41 @@ static void AllocGrallocBufferSync(const
 static void ConnectImageBridge(ImageBridgeChild * child, ImageBridgeParent * parent)
 {
   MessageLoop *parentMsgLoop = parent->GetMessageLoop();
   ipc::MessageChannel *parentChannel = parent->GetIPCChannel();
   child->Open(parentChannel, parentMsgLoop, mozilla::ipc::ChildSide);
 }
 
 ImageBridgeChild::ImageBridgeChild()
+  : mShuttingDown(false)
 {
   mTxn = new CompositableTransaction();
 }
 ImageBridgeChild::~ImageBridgeChild()
 {
   delete mTxn;
 }
 
 void
 ImageBridgeChild::Connect(CompositableClient* aCompositable)
 {
   MOZ_ASSERT(aCompositable);
+  MOZ_ASSERT(!mShuttingDown);
   uint64_t id = 0;
   PCompositableChild* child =
     SendPCompositableConstructor(aCompositable->GetTextureInfo(), &id);
   MOZ_ASSERT(child);
   aCompositable->InitIPDLActor(child, id);
 }
 
 PCompositableChild*
 ImageBridgeChild::AllocPCompositableChild(const TextureInfo& aInfo, uint64_t* aID)
 {
+  MOZ_ASSERT(!mShuttingDown);
   return CompositableClient::CreateIPDLActor();
 }
 
 bool
 ImageBridgeChild::DeallocPCompositableChild(PCompositableChild* aActor)
 {
   return CompositableClient::DestroyIPDLActor(aActor);
 }
@@ -331,30 +354,52 @@ static void ReleaseImageClientNow(ImageC
 {
   MOZ_ASSERT(InImageBridgeChildThread());
   aClient->Release();
 }
 
 // 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));
 }
 
 static void ReleaseTextureClientNow(TextureClient* aClient)
 {
   MOZ_ASSERT(InImageBridgeChildThread());
   aClient->Release();
 }
 
 // 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));
 }
 
 static void UpdateImageClientNow(ImageClient* aClient, ImageContainer* aContainer)
 {
   MOZ_ASSERT(aClient);
@@ -364,16 +409,20 @@ static void UpdateImageClientNow(ImageCl
   aClient->OnTransaction();
   sImageBridgeChildSingleton->EndTransaction();
 }
 
 //static
 void ImageBridgeChild::DispatchImageClientUpdate(ImageClient* aClient,
                                                  ImageContainer* aContainer)
 {
+  if (!IsCreated()) {
+    return;
+  }
+
   if (InImageBridgeChildThread()) {
     UpdateImageClientNow(aClient, aContainer);
     return;
   }
   sImageBridgeChildSingleton->GetMessageLoop()->PostTask(
     FROM_HERE,
     NewRunnableFunction<
       void (*)(ImageClient*, ImageContainer*),
@@ -388,16 +437,20 @@ static void FlushAllImagesSync(ImageClie
   ReentrantMonitorAutoEnter autoMon(*aBarrier);
   *aDone = true;
   aBarrier->NotifyAll();
 }
 
 //static
 void ImageBridgeChild::FlushAllImages(ImageClient* aClient, ImageContainer* aContainer, bool aExceptFront)
 {
+  if (!IsCreated()) {
+    return;
+  }
+
   if (InImageBridgeChildThread()) {
     FlushAllImagesNow(aClient, aContainer, aExceptFront);
     return;
   }
 
   ReentrantMonitor barrier("CreateImageClient Lock");
   ReentrantMonitorAutoEnter autoMon(barrier);
   bool done = false;
@@ -412,28 +465,30 @@ void ImageBridgeChild::FlushAllImages(Im
     barrier.Wait();
   }
 }
 
 //static
 void ImageBridgeChild::FlushAllImagesNow(ImageClient* aClient, ImageContainer* aContainer, bool aExceptFront)
 {
   MOZ_ASSERT(aClient);
+  MOZ_ASSERT(!sImageBridgeChildSingleton->mShuttingDown);
   sImageBridgeChildSingleton->BeginTransaction();
   if (aContainer && !aExceptFront) {
     aContainer->ClearCurrentImage();
   }
   aClient->FlushAllImages(aExceptFront);
   aClient->OnTransaction();
   sImageBridgeChildSingleton->EndTransaction();
 }
 
 void
 ImageBridgeChild::BeginTransaction()
 {
+  MOZ_ASSERT(!mShuttingDown);
   MOZ_ASSERT(mTxn->Finished(), "uncommitted txn?");
   mTxn->Begin();
 }
 
 class MOZ_STACK_CLASS AutoRemoveTextures
 {
 public:
   AutoRemoveTextures(ImageBridgeChild* aImageBridge)
@@ -445,16 +500,17 @@ public:
   }
 private:
   ImageBridgeChild* mImageBridge;
 };
 
 void
 ImageBridgeChild::EndTransaction()
 {
+  MOZ_ASSERT(!mShuttingDown);
   MOZ_ASSERT(!mTxn->Finished(), "forgot BeginTransaction?");
 
   AutoEndTransaction _(mTxn);
   AutoRemoveTextures autoRemoveTextures(this);
 
   if (mTxn->IsEmpty()) {
     return;
   }
@@ -540,16 +596,17 @@ ImageBridgeChild::StartUpInChildProcess(
 
   return sImageBridgeChildSingleton;
 }
 
 void ImageBridgeChild::ShutDown()
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on the main Thread!");
   if (ImageBridgeChild::IsCreated()) {
+    MOZ_ASSERT(!sImageBridgeChildSingleton->mShuttingDown);
     ImageBridgeChild::DestroyBridge();
     delete sImageBridgeChildThread;
     sImageBridgeChildThread = nullptr;
   }
 }
 
 bool ImageBridgeChild::StartUpOnThread(Thread* aThread)
 {
@@ -566,25 +623,24 @@ bool ImageBridgeChild::StartUpOnThread(T
     return true;
   } else {
     return false;
   }
 }
 
 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.
 
-  if (!IsCreated()) {
-    return;
-  }
-
   ReentrantMonitor barrier("ImageBridgeDestroyTask lock");
   ReentrantMonitorAutoEnter autoMon(barrier);
 
   bool done = false;
   sImageBridgeChildSingleton->GetMessageLoop()->PostTask(FROM_HERE,
                   NewRunnableFunction(&StopImageBridgeSync, &barrier, &done));
   while (!done) {
     barrier.Wait();
@@ -596,17 +652,18 @@ void ImageBridgeChild::DestroyBridge()
   while (!done) {
     barrier.Wait();
   }
 
 }
 
 bool InImageBridgeChildThread()
 {
-  return sImageBridgeChildThread->thread_id() == PlatformThread::CurrentId();
+  return ImageBridgeChild::IsCreated() &&
+    sImageBridgeChildThread->thread_id() == PlatformThread::CurrentId();
 }
 
 MessageLoop * ImageBridgeChild::GetMessageLoop() const
 {
   return sImageBridgeChildThread->message_loop();
 }
 
 void ImageBridgeChild::ConnectAsync(ImageBridgeParent* aParent)
@@ -642,66 +699,71 @@ ImageBridgeChild::CreateImageClient(Comp
     barrier.Wait();
   }
   return result.forget();
 }
 
 TemporaryRef<ImageClient>
 ImageBridgeChild::CreateImageClientNow(CompositableType aType)
 {
+  MOZ_ASSERT(!sImageBridgeChildSingleton->mShuttingDown);
   RefPtr<ImageClient> client
     = ImageClient::CreateImageClient(aType, this, 0);
   MOZ_ASSERT(client, "failed to create ImageClient");
   if (client) {
     client->Connect();
   }
   return client.forget();
 }
 
 PGrallocBufferChild*
 ImageBridgeChild::AllocPGrallocBufferChild(const IntSize&, const uint32_t&, const uint32_t&,
                                            MaybeMagicGrallocBufferHandle*)
 {
+  MOZ_ASSERT(!mShuttingDown);
 #ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC
   return GrallocBufferActor::Create();
 #else
   NS_RUNTIMEABORT("No gralloc buffers for you");
   return nullptr;
 #endif
 }
 
 bool
 ImageBridgeChild::DeallocPGrallocBufferChild(PGrallocBufferChild* actor)
 {
+  MOZ_ASSERT(!mShuttingDown);
 #ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC
   delete actor;
   return true;
 #else
   NS_RUNTIMEABORT("Um, how did we get here?");
   return false;
 #endif
 }
 
 bool
 ImageBridgeChild::AllocUnsafeShmem(size_t aSize,
                                    ipc::SharedMemory::SharedMemoryType aType,
                                    ipc::Shmem* aShmem)
 {
+  MOZ_ASSERT(!mShuttingDown);
   if (InImageBridgeChildThread()) {
     return PImageBridgeChild::AllocUnsafeShmem(aSize, aType, aShmem);
   } else {
     return DispatchAllocShmemInternal(aSize, aType, aShmem, true); // true: unsafe
   }
 }
 
 bool
 ImageBridgeChild::AllocShmem(size_t aSize,
                              ipc::SharedMemory::SharedMemoryType aType,
                              ipc::Shmem* aShmem)
 {
+  MOZ_ASSERT(!mShuttingDown);
   if (InImageBridgeChildThread()) {
     return PImageBridgeChild::AllocShmem(aSize, aType, aShmem);
   } else {
     return DispatchAllocShmemInternal(aSize, aType, aShmem, false); // false: unsafe
   }
 }
 
 // NewRunnableFunction accepts a limited number of parameters so we need a
@@ -831,16 +893,17 @@ ImageBridgeChild::AllocGrallocBuffer(con
 }
 
 void
 ImageBridgeChild::AllocGrallocBufferNow(const gfx::IntSize& aSize,
                                         uint32_t aFormat, uint32_t aUsage,
                                         MaybeMagicGrallocBufferHandle* aHandle,
                                         PGrallocBufferChild** aChild)
 {
+  MOZ_ASSERT(!mShuttingDown);
 #ifdef MOZ_WIDGET_GONK
   *aChild = SendPGrallocBufferConstructor(aSize,
                                           aFormat,
                                           aUsage,
                                           aHandle);
 #else
   NS_RUNTIMEABORT("not implemented");
   aChild = nullptr;
@@ -893,36 +956,39 @@ ImageBridgeChild::DeallocGrallocBuffer(P
     }
   }
 }
 
 PTextureChild*
 ImageBridgeChild::AllocPTextureChild(const SurfaceDescriptor&,
                                      const TextureFlags&)
 {
+  MOZ_ASSERT(!mShuttingDown);
   return TextureClient::CreateIPDLActor();
 }
 
 bool
 ImageBridgeChild::DeallocPTextureChild(PTextureChild* actor)
 {
   return TextureClient::DestroyIPDLActor(actor);
 }
 
 PTextureChild*
 ImageBridgeChild::CreateTexture(const SurfaceDescriptor& aSharedData,
                                 TextureFlags aFlags)
 {
+  MOZ_ASSERT(!mShuttingDown);
   return SendPTextureConstructor(aSharedData, aFlags);
 }
 
 void
 ImageBridgeChild::RemoveTextureFromCompositable(CompositableClient* aCompositable,
                                                 TextureClient* aTexture)
 {
+  MOZ_ASSERT(!mShuttingDown);
   if (aTexture->GetFlags() & TEXTURE_DEALLOCATE_CLIENT) {
     mTxn->AddEdit(OpRemoveTexture(nullptr, aCompositable->GetIPDLActor(),
                                   nullptr, aTexture->GetIPDLActor()));
   } else {
     mTxn->AddNoSwapEdit(OpRemoveTexture(nullptr, aCompositable->GetIPDLActor(),
                                         nullptr, aTexture->GetIPDLActor()));
   }
   // Hold texture until transaction complete.
@@ -936,16 +1002,17 @@ static void RemoveTextureSync(TextureCli
   ReentrantMonitorAutoEnter autoMon(*aBarrier);
   *aDone = true;
   aBarrier->NotifyAll();
 }
 
 void ImageBridgeChild::RemoveTexture(TextureClient* aTexture)
 {
   if (InImageBridgeChildThread()) {
+    MOZ_ASSERT(!mShuttingDown);
     aTexture->ForceRemove();
     return;
   }
 
   ReentrantMonitor barrier("RemoveTexture Lock");
   ReentrantMonitorAutoEnter autoMon(barrier);
   bool done = false;
 
--- a/gfx/layers/ipc/ImageBridgeChild.h
+++ b/gfx/layers/ipc/ImageBridgeChild.h
@@ -309,29 +309,32 @@ public:
                                        TextureFlags aFlags) MOZ_OVERRIDE;
 
   virtual bool IsSameProcess() const MOZ_OVERRIDE;
 
   void AllocGrallocBufferNow(const gfx::IntSize& aSize,
                              uint32_t aFormat, uint32_t aUsage,
                              MaybeMagicGrallocBufferHandle* aHandle,
                              PGrallocBufferChild** aChild);
+  void MarkShutDown();
 protected:
   ImageBridgeChild();
   bool DispatchAllocShmemInternal(size_t aSize,
                                   SharedMemory::SharedMemoryType aType,
                                   Shmem* aShmem,
                                   bool aUnsafe);
 
   CompositableTransaction* mTxn;
 
   // ISurfaceAllocator
   virtual PGrallocBufferChild* AllocGrallocBuffer(const gfx::IntSize& aSize,
                                                   uint32_t aFormat, uint32_t aUsage,
                                                   MaybeMagicGrallocBufferHandle* aHandle) MOZ_OVERRIDE;
 
   virtual void DeallocGrallocBuffer(PGrallocBufferChild* aChild) MOZ_OVERRIDE;
+
+  bool mShuttingDown;
 };
 
 } // layers
 } // mozilla
 
 #endif
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -488,22 +488,16 @@ gfxPlatform::Shutdown()
     // 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.
     mozilla::gl::GLContextProviderEGL::Shutdown();
 #endif
 
-    // This will block this thread untill the ImageBridge protocol is completely
-    // deleted.
-    ImageBridgeChild::ShutDown();
-
-    CompositorParent::ShutDown();
-
     delete gGfxPlatformPrefsLock;
 
     gfxPrefs::DestroySingleton();
     gfxFont::DestroySingletons();
 
     delete gPlatform;
     gPlatform = nullptr;
 }
--- a/xpcom/build/nsXPComInit.cpp
+++ b/xpcom/build/nsXPComInit.cpp
@@ -11,19 +11,16 @@
 #include "mozilla/XPCOM.h"
 #include "nsXULAppAPI.h"
 
 #include "nsXPCOMPrivate.h"
 #include "nsXPCOMCIDInternal.h"
 
 #include "prlink.h"
 
-#include "mozilla/layers/ImageBridgeChild.h"
-#include "mozilla/layers/CompositorParent.h"
-
 #include "nsCycleCollector.h"
 #include "nsObserverList.h"
 #include "nsObserverService.h"
 #include "nsProperties.h"
 #include "nsPersistentProperties.h"
 #include "nsScriptableInputStream.h"
 #include "nsBinaryStream.h"
 #include "nsStorageStream.h"
@@ -124,16 +121,19 @@ extern nsresult nsStringInputStreamConst
 #include "base/command_line.h"
 #include "base/message_loop.h"
 
 #include "mozilla/ipc/BrowserProcessSubThread.h"
 #include "mozilla/AvailableMemoryTracker.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/SystemMemoryReporter.h"
 
+#include "mozilla/layers/ImageBridgeChild.h"
+#include "mozilla/layers/CompositorParent.h"
+
 #ifdef MOZ_VISUAL_EVENT_TRACER
 #include "mozilla/VisualEventTracer.h"
 #endif
 
 #include "ogg/ogg.h"
 #if defined(MOZ_VPX) && !defined(MOZ_VPX_NO_MEM_REPORTING)
 #include "vpx_mem/vpx_mem.h"
 #endif
@@ -784,16 +784,21 @@ ShutdownXPCOM(nsIServiceManager* servMgr
             if (NS_SUCCEEDED(rv))
             {
                 (void) observerService->
                     NotifyObservers(mgr, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
                                     nullptr);
             }
         }
 
+        // This must happen after the shutdown of media and widgets, which
+        // are triggered by the NS_XPCOM_SHUTDOWN_OBSERVER_ID notification.
+        mozilla::layers::ImageBridgeChild::ShutDown();
+        mozilla::layers::CompositorParent::ShutDown();
+
         NS_ProcessPendingEvents(thread);
         mozilla::scache::StartupCache::DeleteSingleton();
         if (observerService)
             (void) observerService->
                 NotifyObservers(nullptr, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID,
                                 nullptr);
 
         gXPCOMThreadsShutDown = true;