Bug 1261347 - Consolidate the destruction of CompositableClient/TextureClient objects. r=edwin
☠☠ backed out by 0a1722feaf7b ☠ ☠
authorNicolas Silva <nsilva@mozilla.com>
Wed, 13 Apr 2016 15:50:04 +0200
changeset 316804 685e89b31d6b9814d95c3678b7bf8ecafee245a0
parent 316803 3bcaa74cc73a490cc7f2c309b72009dd229a41dc
child 316805 4b36ba6198154815e958ebeb1e08ce631e36bbb6
push id9480
push userjlund@mozilla.com
push dateMon, 25 Apr 2016 17:12:58 +0000
treeherdermozilla-aurora@0d6a91c76a9e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersedwin
bugs1261347
milestone48.0a1
Bug 1261347 - Consolidate the destruction of CompositableClient/TextureClient objects. r=edwin
gfx/layers/IPDLActor.h
gfx/layers/client/CompositableClient.cpp
gfx/layers/client/CompositableClient.h
gfx/layers/client/TextureClient.cpp
gfx/layers/client/TextureClient.h
gfx/layers/ipc/ImageBridgeChild.cpp
gfx/layers/ipc/LayerTransactionChild.cpp
--- a/gfx/layers/IPDLActor.h
+++ b/gfx/layers/IPDLActor.h
@@ -29,123 +29,187 @@ namespace layers {
 /// The actual IPDL protocol must have the following messages:
 ///
 /// child:
 ///   async __delete__();
 /// parent:
 ///   async Destroy();
 ///   sync DestroySynchronously();
 ///
+/// If a sub-class overrides ActorDestroy, it should also call
+/// ChildActor::ActorDestroy from there.
+///
+/// The typical usage of this class looks like the following:
+/// some reference-counted object FooClient holds a reference to a FooChild
+/// actor inheriting from ChildActor<PFooChild>.
+/// Usually, the destruction of FooChild will be triggered by FooClient's
+/// destructor or some other thing that really means that nothing other than
+/// IPDL is holding a pointer to the actor. This is important because we track
+/// this information in mReleased and use it to know when it is safe to delete
+/// the actor. If FooManager::DeallocPFoChild is invoked by IPDL while mReleased
+/// is still false, we don't delete the actor as we usually would. Instead we
+/// set mIPCOpen to false, and ReleaseActor will delete the actor.
 template<typename Protocol>
 class ChildActor : public Protocol
 {
 public:
-  ChildActor() : mDestroyed(false) {}
+  ChildActor()
+  : mReleased(false)
+  , mSentDestroy(false)
+  , mIPCOpen(true)
+  {}
 
-  ~ChildActor() { MOZ_ASSERT(mDestroyed); }
+  ~ChildActor() { MOZ_ASSERT(mReleased); }
 
   /// Check the return of CanSend before sending any message!
-  bool CanSend() const { return !mDestroyed; }
+  bool CanSend() const { return !mSentDestroy && mIPCOpen; }
 
-  /// The normal way to destroy the actor.
-  ///
-  /// This will asynchronously send a Destroy message to the parent actor, whom
-  /// will send the delete message.
-  void Destroy(CompositableForwarder* aFwd = nullptr)
-  {
-    MOZ_ASSERT(!mDestroyed);
-    if (!mDestroyed) {
-      mDestroyed = true;
-      DestroyManagees();
-      if (!aFwd || !aFwd->DestroyInTransaction(this, false)) {
-        this->SendDestroy();
-      }
-    }
-  }
+  /// Return true if this actor is still connected to the IPDL system.
+  bool IPCOpen() const { return mIPCOpen; }
 
-  /// The ugly and slow way to destroy the actor.
-  ///
-  /// This will block until the Parent actor has handled the Destroy message,
-  /// and then start the asynchronous handshake (and destruction will already
-  /// be done on the parent side, when the async part happens).
-  void DestroySynchronously(CompositableForwarder* aFwd = nullptr)
-  {
-    MOZ_PERFORMANCE_WARNING("gfx", "IPDL actor requires synchronous deallocation");
-    MOZ_ASSERT(!mDestroyed);
-    if (!mDestroyed) {
-      DestroyManagees();
-      mDestroyed = true;
-      if (!aFwd || !aFwd->DestroyInTransaction(this, true)) {
-        this->SendDestroySync();
-        this->SendDestroy();
-      }
-    }
-  }
+  /// Return true if the actor was released (nothing holds on to the actor
+  /// except IPDL, and the actor will most likely receive the __delete__
+  /// message soon. Useful to know whether we can delete the actor when
+  /// IPDL shuts down.
+  bool Released() const { return mReleased; }
 
   /// If the transaction that was supposed to destroy the texture fails for
   /// whatever reason, fallback to destroying the actor synchronously.
   static bool DestroyFallback(Protocol* aActor)
   {
     return aActor->SendDestroySync();
   }
 
-  /// Override this if the protocol manages other protocols, and destroy the
-  /// managees from there
-  virtual void DestroyManagees() {}
+  typedef ipc::IProtocolManager<ipc::IProtocol>::ActorDestroyReason Why;
+
+  virtual void ActorDestroy(Why) override
+  {
+    mIPCOpen = false;
+  }
+
+  /// Call this during shutdown only, if we need to destroy this
+  /// actor but the object paired with the actor isn't destroyed yet.
+  void ForceActorShutdown()
+  {
+    if (mIPCOpen && !mSentDestroy) {
+      this->SendDestroy();
+      mSentDestroy = true;
+    }
+  }
+
+  /// The normal way to destroy the actor.
+  ///
+  /// This will asynchronously send a Destroy message to the parent actor, whom
+  /// will send the delete message.
+  /// Nothing other than IPDL should hold a pointer to the actor after this is
+  /// called.
+  void ReleaseActor(CompositableForwarder* aFwd = nullptr)
+  {
+    if (!IPCOpen()) {
+      mReleased = true;
+      delete this;
+      return;
+    }
+
+    Destroy(aFwd, false);
+  }
+
+  /// The ugly and slow way to destroy the actor.
+  ///
+  /// This will block until the Parent actor has handled the Destroy message,
+  /// and then start the asynchronous handshake (and destruction will already
+  /// be done on the parent side, when the async part happens).
+  /// Nothing other than IPDL should hold a pointer to the actor after this is
+  /// called.
+  void ReleaseActorSynchronously(CompositableForwarder* aFwd = nullptr)
+  {
+    if (!IPCOpen()) {
+      mReleased = true;
+      delete this;
+      return;
+    }
+
+    Destroy(aFwd, true);
+  }
+
+protected:
+
+  void Destroy(CompositableForwarder* aFwd = nullptr, bool synchronously = false)
+  {
+    MOZ_ASSERT(mIPCOpen);
+    MOZ_ASSERT(!mReleased);
+    if (mReleased) {
+      return;
+    }
+    mReleased = true;
+
+    if (!aFwd || !aFwd->DestroyInTransaction(this, synchronously)) {
+      if (synchronously) {
+        MOZ_PERFORMANCE_WARNING("gfx", "IPDL actor requires synchronous deallocation");
+        this->SendDestroySync();
+      } else {
+        this->SendDestroy();
+      }
+    }
+    mSentDestroy = true;
+  }
 
 private:
-  bool mDestroyed;
+  bool mReleased;
+  bool mSentDestroy;
+  bool mIPCOpen;
 };
 
+
 /// A base class to facilitate the deallocation of IPDL actors.
 ///
 /// Implements the parent side of the simple deallocation handshake.
 /// Override the Destroy method rather than the ActorDestroy method.
 template<typename Protocol>
 class ParentActor : public Protocol
 {
 public:
-  ParentActor() : mDestroyed(false) {}
+  ParentActor() : mReleased(false) {}
 
-  ~ParentActor() { MOZ_ASSERT(mDestroyed); }
+  ~ParentActor() { MOZ_ASSERT(mReleased); }
 
-  bool CanSend() const { return !mDestroyed; }
+  bool CanSend() const { return !mReleased; }
 
   // Override this rather than ActorDestroy
   virtual void Destroy() {}
 
   virtual bool RecvDestroy() override
   {
-    if (!mDestroyed) {
+    if (!mReleased) {
       Destroy();
-      mDestroyed = true;
+      mReleased = true;
     }
     Unused << Protocol::Send__delete__(this);
     return true;
   }
 
   virtual bool RecvDestroySync() override
   {
-    if (!mDestroyed) {
+    if (!mReleased) {
       Destroy();
-      mDestroyed = true;
+      mReleased = true;
     }
     return true;
   }
 
   typedef ipc::IProtocolManager<ipc::IProtocol>::ActorDestroyReason Why;
 
   virtual void ActorDestroy(Why) override
   {
-    if (!mDestroyed) {
+    if (!mReleased) {
       Destroy();
-      mDestroyed = true;
+      mReleased = true;
     }
   }
 
 private:
-  bool mDestroyed;
+  bool mReleased;
 };
 
 } // namespace
 } // namespace
 
 #endif
--- a/gfx/layers/client/CompositableClient.cpp
+++ b/gfx/layers/client/CompositableClient.cpp
@@ -40,21 +40,24 @@ public:
     MOZ_COUNT_CTOR(CompositableChild);
   }
 
   virtual ~CompositableChild()
   {
     MOZ_COUNT_DTOR(CompositableChild);
   }
 
-  virtual void ActorDestroy(ActorDestroyReason) override {
+  virtual void ActorDestroy(ActorDestroyReason aWhy) override {
     DestroyAsyncTransactionTrackersHolder();
-    if (mCompositableClient) {
-      mCompositableClient->mCompositableChild = nullptr;
-    }
+    ChildActor::ActorDestroy(aWhy);
+  }
+
+  static CompositableChild* Cast(PCompositableChild* aActor)
+  {
+    return static_cast<CompositableChild*>(aActor);
   }
 
   CompositableClient* mCompositableClient;
 
   uint64_t mAsyncID;
 };
 
 void
@@ -71,62 +74,68 @@ RemoveTextureFromCompositableTracker::Re
   } else {
     mTextureClient = nullptr;
   }
 }
 
 /* static */ void
 CompositableClient::TransactionCompleteted(PCompositableChild* aActor, uint64_t aTransactionId)
 {
-  CompositableChild* child = static_cast<CompositableChild*>(aActor);
+  CompositableChild* child = CompositableChild::Cast(aActor);
   child->TransactionCompleteted(aTransactionId);
 }
 
 /* static */ void
 CompositableClient::HoldUntilComplete(PCompositableChild* aActor, AsyncTransactionTracker* aTracker)
 {
-  CompositableChild* child = static_cast<CompositableChild*>(aActor);
+  CompositableChild* child = CompositableChild::Cast(aActor);
   child->HoldUntilComplete(aTracker);
 }
 
 /* static */ uint64_t
 CompositableClient::GetTrackersHolderId(PCompositableChild* aActor)
 {
-  CompositableChild* child = static_cast<CompositableChild*>(aActor);
+  CompositableChild* child = CompositableChild::Cast(aActor);
   return child->GetId();
 }
 
 /* static */ PCompositableChild*
 CompositableClient::CreateIPDLActor()
 {
   return new CompositableChild();
 }
 
 /* static */ bool
-CompositableClient::DestroyIPDLActor(PCompositableChild* actor)
+CompositableClient::DeallocIPDLActor(PCompositableChild* aActor)
 {
-  delete actor;
+  MOZ_ASSERT(aActor);
+
+  auto actor = CompositableChild::Cast(aActor);
+  if (actor->Released()) {
+    delete actor;
+  }
+
   return true;
 }
 
 void
 CompositableClient::InitIPDLActor(PCompositableChild* aActor, uint64_t aAsyncID)
 {
   MOZ_ASSERT(aActor);
-  CompositableChild* child = static_cast<CompositableChild*>(aActor);
+  CompositableChild* child = CompositableChild::Cast(aActor);
   mCompositableChild = child;
   child->mCompositableClient = this;
   child->mAsyncID = aAsyncID;
 }
 
 /* static */ CompositableClient*
 CompositableClient::FromIPDLActor(PCompositableChild* aActor)
 {
   MOZ_ASSERT(aActor);
-  return static_cast<CompositableChild*>(aActor)->mCompositableClient;
+  return CompositableChild::Cast(aActor)->mCompositableClient;
 }
 
 CompositableClient::CompositableClient(CompositableForwarder* aForwarder,
                                        TextureFlags aTextureFlags)
 : mCompositableChild(nullptr)
 , mForwarder(aForwarder)
 , mTextureFlags(aTextureFlags)
 {
@@ -169,36 +178,63 @@ CompositableClient::Connect(ImageContain
 }
 
 bool
 CompositableClient::IsConnected() const
 {
   return mCompositableChild && mCompositableChild->CanSend();
 }
 
+// static
+void
+CompositableClient::ForceIPDLActorShutdown(PCompositableChild* aActor)
+{
+  if (!aActor) {
+    return;
+  }
+
+  auto actor = CompositableChild::Cast(aActor);
+  if (actor->CanSend()) {
+#ifdef DEBUG
+    // Making it an assertion would blow up too often during test runs so
+    // let's keep it a simple printf for now. This is happening during shutdown
+    // so we don't have to worry too much about printf being slow.
+    // More involved reporting like crashstats annotations would be too much noise
+    // at this point.
+    printf("!! A CompositableClient is destroyed late during shutdown !!\n");
+#endif
+
+    // Do not access the CompositableClient from here.
+    // Do not call Destroy() either because that's how we know whether the
+    // CompositableClient's destructor has run.
+    actor->ForceActorShutdown();
+  }
+}
+
 void
 CompositableClient::Destroy()
 {
-  if (!IsConnected()) {
+  if (!mCompositableChild) {
     return;
   }
 
   // Send pending AsyncMessages before deleting CompositableChild since the former
   // might have references to the latter.
   mForwarder->SendPendingAsyncMessges();
 
   mCompositableChild->mCompositableClient = nullptr;
-  mCompositableChild->Destroy(mForwarder);
+  mCompositableChild->ReleaseActor(mForwarder);
   mCompositableChild = nullptr;
 }
 
 bool
 CompositableClient::DestroyFallback(PCompositableChild* aActor)
 {
-  return aActor->SendDestroySync();
+  CompositableChild::Cast(aActor)->ReleaseActorSynchronously();
+  return true;
 }
 
 uint64_t
 CompositableClient::GetAsyncID() const
 {
   if (mCompositableChild) {
     return mCompositableChild->mAsyncID;
   }
--- a/gfx/layers/client/CompositableClient.h
+++ b/gfx/layers/client/CompositableClient.h
@@ -217,17 +217,25 @@ public:
    *
    * CompositableChild is an implementation detail of CompositableClient that is not
    * exposed to the rest of the code base. CreateIPDLActor and DestroyIPDLActor
    * are for use with the managing IPDL protocols only (so that they can
    * implement AllocCompositableChild and DeallocPCompositableChild).
    */
   static PCompositableChild* CreateIPDLActor();
 
-  static bool DestroyIPDLActor(PCompositableChild* actor);
+  static bool DeallocIPDLActor(PCompositableChild* actor);
+
+  /// Ideally we would not need this. When we shut top-level IPDL protocols down,
+  /// the remaining managed protocols need to be destroyed synchronously. We'd
+  /// It'd be better if all ImageClients were alread destroyed by that time.
+  /// Call this during shutdown instead of trying to access the CompositableClient
+  /// since the latter may be about to get destroyed and we don't know for sure
+  /// on which thread.
+  static void ForceIPDLActorShutdown(PCompositableChild* aActor);
 
   void InitIPDLActor(PCompositableChild* aActor, uint64_t aAsyncID = 0);
 
   static void TransactionCompleteted(PCompositableChild* aActor, uint64_t aTransactionId);
 
   static void HoldUntilComplete(PCompositableChild* aActor, AsyncTransactionTracker* aTracker);
 
   static uint64_t GetTrackersHolderId(PCompositableChild* aActor);
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -67,17 +67,17 @@ namespace layers {
 
 using namespace mozilla::ipc;
 using namespace mozilla::gl;
 using namespace mozilla::gfx;
 
 struct TextureDeallocParams
 {
   TextureData* data;
-  RefPtr<TextureChild> actor;
+  TextureChild* actor;
   RefPtr<ClientIPCAllocator> allocator;
   bool clientDeallocation;
   bool syncDeallocation;
   bool workAroundSharedSurfaceOwnershipIssue;
 };
 
 void DeallocateTextureClient(TextureDeallocParams params);
 
@@ -95,99 +95,70 @@ void DeallocateTextureClient(TextureDeal
 class TextureChild final : public ChildActor<PTextureChild>
 {
   ~TextureChild()
   {
     // We should have deallocated mTextureData in ActorDestroy
     MOZ_ASSERT(!mTextureData);
   }
 public:
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TextureChild)
 
   TextureChild()
   : mForwarder(nullptr)
-  , mMonitor("TextureChild")
   , mTextureClient(nullptr)
   , mTextureData(nullptr)
-  , mDestroyed(false)
+  , mTextureClientDestroyed(false)
   , mMainThreadOnly(false)
-  , mIPCOpen(false)
   , mOwnsTextureData(false)
+  , mSyncDeallocation(false)
   {}
 
   bool Recv__delete__() override { return true; }
 
   bool RecvCompositorRecycle() override
   {
     RECYCLE_LOG("[CLIENT] Receive recycle %p (%p)\n", mTextureClient, mWaitForRecycle.get());
     mWaitForRecycle = nullptr;
     return true;
   }
 
   void WaitForCompositorRecycle()
   {
     {
-      MonitorAutoLock mon(mMonitor);
-      mWaitForRecycle = mDestroyed ? nullptr : mTextureClient;
+      mWaitForRecycle = mTextureClientDestroyed ? nullptr : mTextureClient;
     }
     RECYCLE_LOG("[CLIENT] Wait for recycle %p\n", mWaitForRecycle.get());
     MOZ_ASSERT(CanSend());
     SendClientRecycle();
   }
 
   void CancelWaitForCompositorRecycle()
   {
     RECYCLE_LOG("[CLIENT] Cancelling wait for recycle %p\n", mWaitForRecycle.get());
     {
-      MonitorAutoLock mon(mMonitor);
       mWaitForRecycle = nullptr;
     }
   }
 
   CompositableForwarder* GetForwarder() { return mForwarder; }
 
   ClientIPCAllocator* GetAllocator() { return mForwarder; }
 
   void ActorDestroy(ActorDestroyReason why) override;
 
-  bool IPCOpen() const { return mIPCOpen; }
-
-private:
-
-  // AddIPDLReference and ReleaseIPDLReference are only to be called by CreateIPDLActor
-  // and DestroyIPDLActor, respectively. We intentionally make them private to prevent misuse.
-  // The purpose of these methods is to be aware of when the IPC system around this
-  // actor goes down: mIPCOpen is then set to false.
-  void AddIPDLReference() {
-    MOZ_ASSERT(mIPCOpen == false);
-    mIPCOpen = true;
-    AddRef();
-  }
-  void ReleaseIPDLReference() {
-    MOZ_ASSERT(mIPCOpen == true);
-    mIPCOpen = false;
-    Release();
-  }
-
-  void SetTextureClient(TextureClient* aTextureClient) {
-    MonitorAutoLock mon(mMonitor);
-    mTextureClient = aTextureClient;
-  }
-
   RefPtr<CompositableForwarder> mForwarder;
+  RefPtr<ClientIPCAllocator> mAllocator;
   RefPtr<TextureClient> mWaitForRecycle;
 
-  // Monitor protecting mTextureClient.
-  Monitor mMonitor;
   TextureClient* mTextureClient;
   TextureData* mTextureData;
-  Atomic<bool> mDestroyed;
+  Atomic<bool> mTextureClientDestroyed;
   bool mMainThreadOnly;
-  bool mIPCOpen;
   bool mOwnsTextureData;
+  bool mSyncDeallocation;
 
   friend class TextureClient;
   friend void DeallocateTextureClient(TextureDeallocParams params);
 };
 
 
 static void DestroyTextureData(TextureData* aTextureData, ClientIPCAllocator* aAllocator,
                                bool aDeallocate, bool aMainThreadOnly)
@@ -217,27 +188,88 @@ TextureChild::ActorDestroy(ActorDestroyR
 {
   PROFILER_LABEL_FUNC(js::ProfileEntry::Category::GRAPHICS);
   mWaitForRecycle = nullptr;
 
   if (mTextureData) {
     DestroyTextureData(mTextureData, GetAllocator(), mOwnsTextureData, mMainThreadOnly);
     mTextureData = nullptr;
   }
+
+  ChildActor::ActorDestroy(why);
 }
 
 void DeallocateTextureClientSyncProxy(TextureDeallocParams params,
                                         ReentrantMonitor* aBarrier, bool* aDone)
 {
   DeallocateTextureClient(params);
   ReentrantMonitorAutoEnter autoMon(*aBarrier);
   *aDone = true;
   aBarrier->NotifyAll();
 }
 
+// This must not be called off the IPDL thread.
+static
+void DestroyTextureActorAndSharedData(TextureChild* aActor)
+{
+  MOZ_ASSERT(aActor);
+  if (!aActor->CanSend()) {
+    return;
+  }
+
+  RefPtr<CompositableForwarder> forwarder = aActor->mForwarder;
+
+  if (aActor->mSyncDeallocation) {
+    MOZ_PERFORMANCE_WARNING("gfx",
+      "TextureClient/Host pair requires synchronous deallocation");
+
+    RefPtr<ClientIPCAllocator> allocator = aActor->GetAllocator();
+    TextureData* data = aActor->mTextureData;
+    bool mainThreadOnly = aActor->mMainThreadOnly;
+    bool clientDeallocation = aActor->mOwnsTextureData;
+
+    // Null out mTextureData so that ActorDestory doesn't call
+    // DestroyTextureData a second time.
+    aActor->mTextureData = nullptr;
+
+    // aActor may get deleted in ReleaseActorSynchronously.
+    aActor->ReleaseActorSynchronously(forwarder);
+
+    if (data) {
+      DestroyTextureData(data, allocator, clientDeallocation, mainThreadOnly);
+    }
+
+  } else {
+    // aActor may get deleted in ReleaseActor.
+    aActor->ReleaseActor(forwarder);
+    // DestroyTextureData will be called by TextureChild::ActorDestroy
+  }
+}
+
+void TextureClient::ForceIPDLActorShutdown(PTextureChild* aActor)
+{
+  if (!aActor) {
+    return;
+  }
+  auto actor = static_cast<TextureChild*>(aActor);
+
+#ifdef DEBUG
+  // Making it an assertion would blow up too often during test runs so
+  // let's keep it a simple printf for now. This is happening during shutdown
+  // so we don't have to worry too much about printf being slow.
+  // More involved reporting like crashstats annotations would be too much noise
+  // at this point.
+  if (actor->CanSend()) {
+    printf("!! A TextureClient is destroyed late during shutdown !!\n");
+  }
+#endif
+
+  DestroyTextureActorAndSharedData(actor);
+}
+
 /// The logic for synchronizing a TextureClient's deallocation goes here.
 ///
 /// This funciton takes care of dispatching work to the right thread using
 /// a synchronous proxy if needed, and handles client/host deallocation.
 void
 DeallocateTextureClient(TextureDeallocParams params)
 {
   if (!params.actor && !params.data) {
@@ -248,17 +280,16 @@ DeallocateTextureClient(TextureDeallocPa
   TextureChild* actor = params.actor;
   MessageLoop* ipdlMsgLoop = nullptr;
 
   if (params.allocator) {
     ipdlMsgLoop = params.allocator->AsClientAllocator()->GetMessageLoop();
     if (!ipdlMsgLoop) {
       // An allocator with no message loop means we are too late in the shutdown
       // sequence.
-      gfxCriticalError() << "Texture deallocated too late during shutdown";
       return;
     }
   }
 
   // First make sure that the work is happening on the IPDL thread.
   if (ipdlMsgLoop && MessageLoop::current() != ipdlMsgLoop) {
     if (params.syncDeallocation) {
       bool done = false;
@@ -299,45 +330,27 @@ DeallocateTextureClient(TextureDeallocPa
     // our data. This is specific to the gralloc SharedSurface.
     bool shouldDeallocate = !params.workAroundSharedSurfaceOwnershipIssue;
     DestroyTextureData(params.data, params.allocator,
                        shouldDeallocate,
                        false);  // main-thread deallocation
     return;
   }
 
-  if (!actor->IPCOpen()) {
-    // The actor is already deallocated which probably means there was a shutdown
-    // race causing this function to be called concurrently which is bad!
-    gfxCriticalError() << "Racy texture deallocation";
-    return;
-  }
-
-  if (params.syncDeallocation) {
-    MOZ_PERFORMANCE_WARNING("gfx",
-      "TextureClient/Host pair requires synchronous deallocation");
-    actor->DestroySynchronously(actor->GetForwarder());
-    DestroyTextureData(params.data, params.allocator, params.clientDeallocation,
-                       actor->mMainThreadOnly);
-  } else {
-    actor->mTextureData = params.data;
-    actor->mOwnsTextureData = params.clientDeallocation;
-    actor->Destroy(actor->GetForwarder());
-    // DestroyTextureData will be called by TextureChild::ActorDestroy
-  }
+  DestroyTextureActorAndSharedData(actor);
 }
 
 void TextureClient::Destroy(bool aForceSync)
 {
   MOZ_ASSERT(!IsLocked());
 
-  RefPtr<TextureChild> actor = mActor;
+  TextureChild* actor = mActor;
   mActor = nullptr;
 
-  if (actor && !actor->mDestroyed.compareExchange(false, true)) {
+  if (actor && !actor->mTextureClientDestroyed.compareExchange(false, true)) {
     actor = nullptr;
   }
 
   TextureData* data = mData;
   if (!mWorkaroundAnnoyingSharedSurfaceLifetimeIssues) {
     mData = nullptr;
   }
 
@@ -578,80 +591,84 @@ TextureClient::WaitForBufferOwnership(bo
   }
 #endif
 }
 
 // static
 PTextureChild*
 TextureClient::CreateIPDLActor()
 {
-  TextureChild* c = new TextureChild();
-  c->AddIPDLReference();
-  return c;
+  return new TextureChild();
 }
 
 // static
 bool
-TextureClient::DestroyIPDLActor(PTextureChild* actor)
+TextureClient::DeallocIPDLActor(PTextureChild* aActor)
 {
-  static_cast<TextureChild*>(actor)->ReleaseIPDLReference();
+  auto actor = static_cast<TextureChild*>(aActor);
+  if (actor->Released()) {
+    delete actor;
+  }
+
   return true;
 }
 
 // static
 TextureClient*
 TextureClient::AsTextureClient(PTextureChild* actor)
 {
   if (!actor) {
     return nullptr;
   }
   TextureChild* tc = static_cast<TextureChild*>(actor);
-  if (tc->mDestroyed) {
+  if (tc->mTextureClientDestroyed) {
+    // This is mostly a band aid. Nothing prevents the texture to be destroyed
+    // on another thread just after this check is performed.
     return nullptr;
   }
 
   return tc->mTextureClient;
 }
 
 bool
 TextureClient::IsSharedWithCompositor() const {
-  return mActor && mActor->IPCOpen();
+  return mActor && mActor->CanSend();
 }
 
 void
 TextureClient::AddFlags(TextureFlags aFlags)
 {
   MOZ_ASSERT(!IsSharedWithCompositor() ||
              ((GetFlags() & TextureFlags::RECYCLE) && !IsAddedToCompositableClient()));
   mFlags |= aFlags;
-  if (IsValid() && mActor && !mActor->mDestroyed && mActor->IPCOpen()) {
+  if (IsValid() && mActor && mActor->CanSend()) {
     mActor->SendRecycleTexture(mFlags);
   }
 }
 
 void
 TextureClient::RemoveFlags(TextureFlags aFlags)
 {
   MOZ_ASSERT(!IsSharedWithCompositor() ||
              ((GetFlags() & TextureFlags::RECYCLE) && !IsAddedToCompositableClient()));
   mFlags &= ~aFlags;
-  if (IsValid() && mActor && !mActor->mDestroyed && mActor->IPCOpen()) {
+  if (IsValid() && mActor && mActor->CanSend()) {
     mActor->SendRecycleTexture(mFlags);
   }
 }
 
 void
 TextureClient::RecycleTexture(TextureFlags aFlags)
 {
   MOZ_ASSERT(GetFlags() & TextureFlags::RECYCLE);
 
   mAddedToCompositableClient = false;
   if (mFlags != aFlags) {
     mFlags = aFlags;
-    if (IsValid() && mActor && !mActor->mDestroyed && mActor->IPCOpen()) {
+    if (IsValid() && mActor && mActor->CanSend()) {
       mActor->SendRecycleTexture(mFlags);
     }
   }
 }
 
 void
 TextureClient::WaitForCompositorRecycle()
 {
@@ -693,32 +710,39 @@ TextureClient::SetRecycleAllocator(IText
     ClearRecycleCallback();
   }
 }
 
 bool
 TextureClient::InitIPDLActor(CompositableForwarder* aForwarder)
 {
   MOZ_ASSERT(aForwarder && aForwarder->GetMessageLoop() == mAllocator->AsClientAllocator()->GetMessageLoop());
-  if (mActor && !mActor->mDestroyed && mActor->GetForwarder() == aForwarder) {
+  if (mActor && !mActor->mTextureClientDestroyed && mActor->GetForwarder() == aForwarder) {
     return true;
   }
-  MOZ_ASSERT(!mActor || mActor->mDestroyed, "Cannot use a texture on several IPC channels.");
+  MOZ_ASSERT(!mActor || mActor->mTextureClientDestroyed, "Cannot use a texture on several IPC channels.");
 
   SurfaceDescriptor desc;
   if (!ToSurfaceDescriptor(desc)) {
     return false;
   }
 
   mActor = static_cast<TextureChild*>(aForwarder->CreateTexture(desc, aForwarder->GetCompositorBackendType(), GetFlags()));
   MOZ_ASSERT(mActor);
+  mActor->mTextureClient = this;
   mActor->mForwarder = aForwarder;
-  mActor->mTextureClient = this;
+  mActor->mAllocator = mAllocator;
   mActor->mMainThreadOnly = !!(mFlags & TextureFlags::DEALLOCATE_MAIN_THREAD);
-  return mActor->IPCOpen();
+  mActor->mOwnsTextureData = !!(mFlags & TextureFlags::DEALLOCATE_CLIENT);
+  mActor->mSyncDeallocation = !!(mFlags & TextureFlags::DEALLOCATE_CLIENT);
+  if (!mWorkaroundAnnoyingSharedSurfaceLifetimeIssues) {
+    mActor->mTextureData = mData;
+  }
+
+  return mActor->CanSend();
 }
 
 PTextureChild*
 TextureClient::GetIPDLActor()
 {
   return mActor;
 }
 
--- a/gfx/layers/client/TextureClient.h
+++ b/gfx/layers/client/TextureClient.h
@@ -423,17 +423,18 @@ public:
    * Allocate and deallocate a TextureChild actor.
    *
    * TextureChild is an implementation detail of TextureClient that is not
    * exposed to the rest of the code base. CreateIPDLActor and DestroyIPDLActor
    * are for use with the managing IPDL protocols only (so that they can
    * implement AllocPextureChild and DeallocPTextureChild).
    */
   static PTextureChild* CreateIPDLActor();
-  static bool DestroyIPDLActor(PTextureChild* actor);
+  static bool DeallocIPDLActor(PTextureChild* actor);
+  static void ForceIPDLActorShutdown(PTextureChild* actor);
   // call this if the transaction that was supposed to destroy the actor failed.
   static bool DestroyFallback(PTextureChild* actor);
 
   /**
    * Get the TextureClient corresponding to the actor passed in parameter.
    */
   static TextureClient* AsTextureClient(PTextureChild* actor);
 
@@ -615,17 +616,17 @@ protected:
    * Calling ToSurfaceDescriptor again after it has already returned true,
    * or never constructing a TextureHost with aDescriptor may result in a memory
    * leak (see TextureClientD3D9 for example).
    */
   bool ToSurfaceDescriptor(SurfaceDescriptor& aDescriptor);
 
 
   RefPtr<ClientIPCAllocator> mAllocator;
-  RefPtr<TextureChild> mActor;
+  TextureChild* mActor;
   RefPtr<ITextureClientRecycleAllocator> mRecycleAllocator;
   RefPtr<AsyncTransactionWaiter> mRemoveFromCompositableWaiter;
 
   TextureData* mData;
   RefPtr<gfx::DrawTarget> mBorrowedDrawTarget;
 
   TextureFlags mFlags;
   FenceHandle mReleaseFenceHandle;
--- a/gfx/layers/ipc/ImageBridgeChild.cpp
+++ b/gfx/layers/ipc/ImageBridgeChild.cpp
@@ -262,28 +262,22 @@ static void ImageBridgeShutdownStep1(Ree
 
   MediaSystemResourceManager::Shutdown();
 
   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) {
-      auto compositable = CompositableClient::FromIPDLActor(compositables[i]);
-      if (compositable) {
-        compositable->Destroy();
-      }
+      CompositableClient::ForceIPDLActorShutdown(compositables[i]);
     }
     InfallibleTArray<PTextureChild*> textures;
     sImageBridgeChildSingleton->ManagedPTextureChild(textures);
     for (int i = textures.Length() - 1; i >= 0; --i) {
-      RefPtr<TextureClient> client = TextureClient::AsTextureClient(textures[i]);
-      if (client) {
-        client->Destroy();
-      }
+      TextureClient::ForceIPDLActorShutdown(textures[i]);
     }
     sImageBridgeChildSingleton->FallbackDestroyActors();
 
     sImageBridgeChildSingleton->SendWillClose();
     sImageBridgeChildSingleton->MarkShutDown();
     // From now on, no message can be sent through the image bridge from the
     // client side except the final Stop message.
   }
@@ -392,17 +386,17 @@ ImageBridgeChild::AllocPCompositableChil
 {
   MOZ_ASSERT(!mShuttingDown);
   return CompositableClient::CreateIPDLActor();
 }
 
 bool
 ImageBridgeChild::DeallocPCompositableChild(PCompositableChild* aActor)
 {
-  return CompositableClient::DestroyIPDLActor(aActor);
+  return CompositableClient::DeallocIPDLActor(aActor);
 }
 
 
 Thread* ImageBridgeChild::GetThread() const
 {
   return sImageBridgeChildThread;
 }
 
@@ -468,17 +462,16 @@ void ImageBridgeChild::DispatchReleaseIm
 
   if (!IsCreated()) {
     if (aClient) {
       // 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();
     }
     delete aChild;
     return;
   }
 
   sImageBridgeChildSingleton->GetMessageLoop()->PostTask(
     FROM_HERE,
@@ -499,17 +492,16 @@ void ImageBridgeChild::DispatchReleaseCa
   }
 
   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(&ReleaseCanvasClientNow, aClient));
 }
@@ -528,17 +520,16 @@ void ImageBridgeChild::DispatchReleaseTe
   }
 
   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);
     RELEASE_MANUALLY(aClient);
     return;
   }
 
   sImageBridgeChildSingleton->GetMessageLoop()->PostTask(
     FROM_HERE,
     NewRunnableFunction(&ReleaseTextureClientNow, aClient));
 }
@@ -1096,17 +1087,17 @@ ImageBridgeChild::AllocPTextureChild(con
 {
   MOZ_ASSERT(!mShuttingDown);
   return TextureClient::CreateIPDLActor();
 }
 
 bool
 ImageBridgeChild::DeallocPTextureChild(PTextureChild* actor)
 {
-  return TextureClient::DestroyIPDLActor(actor);
+  return TextureClient::DeallocIPDLActor(actor);
 }
 
 PMediaSystemResourceManagerChild*
 ImageBridgeChild::AllocPMediaSystemResourceManagerChild()
 {
   MOZ_ASSERT(!mShuttingDown);
   return new mozilla::media::MediaSystemResourceManagerChild();
 }
--- a/gfx/layers/ipc/LayerTransactionChild.cpp
+++ b/gfx/layers/ipc/LayerTransactionChild.cpp
@@ -32,26 +32,24 @@ LayerTransactionChild::Destroy()
   // under Send__delete__() call, this function is called from
   // ShadowLayerForwarder's destructor.
   // When it happens, IPCOpen() is still true.
   // See bug 1004191.
   mDestroyed = true;
   MOZ_ASSERT(0 == ManagedPLayerChild().Count(),
              "layers should have been cleaned up by now");
 
+  const ManagedContainer<PCompositableChild>& compositables = ManagedPCompositableChild();
+  for (auto iter = compositables.ConstIter(); !iter.Done(); iter.Next()) {
+    CompositableClient::ForceIPDLActorShutdown(iter.Get()->GetKey());
+  }
+
   const ManagedContainer<PTextureChild>& textures = ManagedPTextureChild();
   for (auto iter = textures.ConstIter(); !iter.Done(); iter.Next()) {
-    TextureClient* texture = TextureClient::AsTextureClient(iter.Get()->GetKey());
-
-    if (texture) {
-      // TODO: cf bug 1242448.
-      //gfxDevCrash(gfx::LogReason::TextureAliveAfterShutdown)
-      //  << "A texture is held alive after shutdown (PCompositorBridge)";
-      texture->Destroy();
-    }
+    TextureClient::ForceIPDLActorShutdown(iter.Get()->GetKey());
   }
 
   SendShutdown();
 }
 
 
 PLayerChild*
 LayerTransactionChild::AllocPLayerChild()
@@ -73,17 +71,17 @@ LayerTransactionChild::AllocPCompositabl
 {
   MOZ_ASSERT(!mDestroyed);
   return CompositableClient::CreateIPDLActor();
 }
 
 bool
 LayerTransactionChild::DeallocPCompositableChild(PCompositableChild* actor)
 {
-  return CompositableClient::DestroyIPDLActor(actor);
+  return CompositableClient::DeallocIPDLActor(actor);
 }
 
 bool
 LayerTransactionChild::RecvParentAsyncMessages(InfallibleTArray<AsyncParentMessageData>&& aMessages)
 {
   for (AsyncParentMessageArray::index_type i = 0; i < aMessages.Length(); ++i) {
     const AsyncParentMessageData& message = aMessages[i];
 
@@ -137,13 +135,13 @@ LayerTransactionChild::AllocPTextureChil
 {
   MOZ_ASSERT(!mDestroyed);
   return TextureClient::CreateIPDLActor();
 }
 
 bool
 LayerTransactionChild::DeallocPTextureChild(PTextureChild* actor)
 {
-  return TextureClient::DestroyIPDLActor(actor);
+  return TextureClient::DeallocIPDLActor(actor);
 }
 
 } // namespace layers
 } // namespace mozilla