Bug 1340117 - Batch ReadLock intializer into a separate IDPL message to avoid hitting the file descriptor limit. r=dvander
authorMatt Woodrow <mwoodrow@mozilla.com>
Mon, 27 Feb 2017 16:12:12 +1300
changeset 346041 e7e495f38cf3
parent 346040 c3437d0e66b7
child 346042 4087fb7a5fb5
push id31452
push usercbook@mozilla.com
push date2017-03-06 09:54 +0000
treeherdermozilla-central@966464a68a2c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdvander
bugs1340117
milestone54.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1340117 - Batch ReadLock intializer into a separate IDPL message to avoid hitting the file descriptor limit. r=dvander
gfx/ipc/GfxMessageUtils.h
gfx/layers/LayersTypes.h
gfx/layers/client/TextureClient.cpp
gfx/layers/client/TextureClient.h
gfx/layers/composite/TextureHost.cpp
gfx/layers/composite/TextureHost.h
gfx/layers/ipc/CompositableForwarder.h
gfx/layers/ipc/CompositableTransactionParent.cpp
gfx/layers/ipc/CompositableTransactionParent.h
gfx/layers/ipc/ISurfaceAllocator.cpp
gfx/layers/ipc/ImageBridgeChild.cpp
gfx/layers/ipc/ImageBridgeParent.cpp
gfx/layers/ipc/ImageBridgeParent.h
gfx/layers/ipc/LayerTransactionParent.cpp
gfx/layers/ipc/LayerTransactionParent.h
gfx/layers/ipc/LayersMessages.ipdlh
gfx/layers/ipc/PImageBridge.ipdl
gfx/layers/ipc/PLayerTransaction.ipdl
gfx/layers/ipc/PWebRenderBridge.ipdl
gfx/layers/ipc/ShadowLayers.cpp
gfx/layers/wr/WebRenderBridgeChild.cpp
gfx/layers/wr/WebRenderBridgeChild.h
gfx/layers/wr/WebRenderBridgeParent.cpp
gfx/layers/wr/WebRenderBridgeParent.h
--- a/gfx/ipc/GfxMessageUtils.h
+++ b/gfx/ipc/GfxMessageUtils.h
@@ -723,16 +723,29 @@ struct ParamTraits<mozilla::layers::Comp
   static void Write(Message* msg, const paramType& param) {
     WriteParam(msg, param.mHandle);
   }
   static bool Read(const Message* msg, PickleIterator* iter, paramType* result) {
     return ReadParam(msg, iter, &result->mHandle);
   }
 };
 
+template<>
+struct ParamTraits<mozilla::layers::ReadLockHandle>
+{
+  typedef mozilla::layers::ReadLockHandle paramType;
+
+  static void Write(Message* msg, const paramType& param) {
+    WriteParam(msg, param.mHandle);
+  }
+  static bool Read(const Message* msg, PickleIterator* iter, paramType* result) {
+    return ReadParam(msg, iter, &result->mHandle);
+  }
+};
+
 // Helper class for reading bitfields.
 // If T has bitfields members, derive ParamTraits<T> from BitfieldHelper<T>.
 template <typename ParamType>
 struct BitfieldHelper
 {
   // We need this helper because we can't get the address of a bitfield to
   // pass directly to ReadParam. So instead we read it into a temporary bool
   // and set the bitfield using a setter function
--- a/gfx/layers/LayersTypes.h
+++ b/gfx/layers/LayersTypes.h
@@ -269,16 +269,42 @@ public:
   }
   uint64_t Value() const {
     return mHandle;
   }
 private:
   uint64_t mHandle;
 };
 
+class ReadLockHandle
+{
+  friend struct IPC::ParamTraits<mozilla::layers::ReadLockHandle>;
+public:
+  ReadLockHandle() : mHandle(0)
+  {}
+  ReadLockHandle(const ReadLockHandle& aOther) : mHandle(aOther.mHandle)
+  {}
+  explicit ReadLockHandle(uint64_t aHandle) : mHandle(aHandle)
+  {}
+  bool IsValid() const {
+    return mHandle != 0;
+  }
+  explicit operator bool() const {
+    return IsValid();
+  }
+  bool operator ==(const ReadLockHandle& aOther) const {
+    return mHandle == aOther.mHandle;
+  }
+  uint64_t Value() const {
+    return mHandle;
+  }
+private:
+  uint64_t mHandle;
+};
+
 enum class ScrollDirection : uint32_t {
   NONE,
   VERTICAL,
   HORIZONTAL
 };
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -577,27 +577,29 @@ TextureClient::Unlock()
 void
 TextureClient::EnableReadLock()
 {
   if (!mReadLock) {
     mReadLock = NonBlockingTextureReadLock::Create(mAllocator);
   }
 }
 
-void
+bool
 TextureClient::SerializeReadLock(ReadLockDescriptor& aDescriptor)
 {
   if (mReadLock && mUpdated) {
     // Take a read lock on behalf of the TextureHost. The latter will unlock
     // after the shared data is available again for drawing.
     mReadLock->ReadLock();
     mReadLock->Serialize(aDescriptor, GetAllocator()->GetParentPid());
     mUpdated = false;
+    return true;
   } else {
     aDescriptor = null_t();
+    return false;
   }
 }
 
 TextureClient::~TextureClient()
 {
   mReadLock = nullptr;
   Destroy();
 }
--- a/gfx/layers/client/TextureClient.h
+++ b/gfx/layers/client/TextureClient.h
@@ -646,17 +646,17 @@ public:
 
   TextureReadLock* GetReadLock() { return mReadLock; }
 
   bool IsReadLocked() const;
 
   bool TryReadLock();
   void ReadUnlock();
 
-  void SerializeReadLock(ReadLockDescriptor& aDescriptor);
+  bool SerializeReadLock(ReadLockDescriptor& aDescriptor);
 
 private:
   static void TextureClientRecycleCallback(TextureClient* aClient, void* aClosure);
  
   // Internal helpers for creating texture clients using the actual forwarder instead
   // of KnowsCompositor. TextureClientPool uses these to let it cache texture clients
   // per-process instead of per ShadowLayerForwarder, but everyone else should
   // use the public functions instead.
--- a/gfx/layers/composite/TextureHost.cpp
+++ b/gfx/layers/composite/TextureHost.cpp
@@ -555,16 +555,28 @@ TextureHost::DeserializeReadLock(const R
 
   // If mReadLock is not null it means we haven't unlocked it yet and the content
   // side should not have been able to write into this texture and send a new lock!
   MOZ_ASSERT(!mReadLock);
   mReadLock = lock.forget();
 }
 
 void
+TextureHost::SetReadLock(TextureReadLock* aReadLock)
+{
+  if (!aReadLock) {
+    return;
+  }
+  // If mReadLock is not null it means we haven't unlocked it yet and the content
+  // side should not have been able to write into this texture and send a new lock!
+  MOZ_ASSERT(!mReadLock);
+  mReadLock = aReadLock;
+}
+
+void
 TextureHost::ReadUnlock()
 {
   if (mReadLock) {
     mReadLock->ReadUnlock();
     mReadLock = nullptr;
   }
 }
 
--- a/gfx/layers/composite/TextureHost.h
+++ b/gfx/layers/composite/TextureHost.h
@@ -573,16 +573,17 @@ public:
   }
 
   int NumCompositableRefs() const { return mCompositableCount; }
 
   void SetLastFwdTransactionId(uint64_t aTransactionId);
 
   void DeserializeReadLock(const ReadLockDescriptor& aDesc,
                            ISurfaceAllocator* aAllocator);
+  void SetReadLock(TextureReadLock* aReadLock);
 
   TextureReadLock* GetReadLock() { return mReadLock; }
 
   virtual Compositor* GetCompositor() = 0;
 
   virtual BufferTextureHost* AsBufferTextureHost() { return nullptr; }
 
 protected:
--- a/gfx/layers/ipc/CompositableForwarder.h
+++ b/gfx/layers/ipc/CompositableForwarder.h
@@ -109,16 +109,18 @@ public:
   virtual uint64_t GetFwdTransactionId() = 0;
 
   virtual bool InForwarderThread() = 0;
 
   void AssertInForwarderThread() {
     MOZ_ASSERT(InForwarderThread());
   }
 
+  static uint32_t GetMaxFileDescriptorsPerMessage();
+
 protected:
   nsTArray<RefPtr<TextureClient> > mTexturesToRemove;
   nsTArray<RefPtr<CompositableClient>> mCompositableClientsToRemove;
 };
 
 } // namespace layers
 } // namespace mozilla
 
--- a/gfx/layers/ipc/CompositableTransactionParent.cpp
+++ b/gfx/layers/ipc/CompositableTransactionParent.cpp
@@ -155,17 +155,17 @@ CompositableParentManager::ReceiveCompos
         CompositableHost::TimedTexture* t = textures.AppendElement();
         t->mTexture =
             TextureHost::AsTextureHost(timedTexture.textureParent());
         MOZ_ASSERT(t->mTexture);
         t->mTimeStamp = timedTexture.timeStamp();
         t->mPictureRect = timedTexture.picture();
         t->mFrameID = timedTexture.frameID();
         t->mProducerID = timedTexture.producerID();
-        t->mTexture->DeserializeReadLock(timedTexture.sharedLock(), this);
+        t->mTexture->SetReadLock(FindReadLock(timedTexture.sharedLock()));
       }
       if (textures.Length() > 0) {
         compositable->UseTextureHost(textures);
 
         for (auto& timedTexture : op.textures()) {
           RefPtr<TextureHost> texture = TextureHost::AsTextureHost(timedTexture.textureParent());
           if (texture) {
             texture->SetLastFwdTransactionId(mFwdTransactionId);
@@ -180,18 +180,18 @@ CompositableParentManager::ReceiveCompos
         ScheduleComposition(compositable);
       }
       break;
     }
     case CompositableOperationDetail::TOpUseComponentAlphaTextures: {
       const OpUseComponentAlphaTextures& op = aEdit.detail().get_OpUseComponentAlphaTextures();
       RefPtr<TextureHost> texOnBlack = TextureHost::AsTextureHost(op.textureOnBlackParent());
       RefPtr<TextureHost> texOnWhite = TextureHost::AsTextureHost(op.textureOnWhiteParent());
-      texOnBlack->DeserializeReadLock(op.sharedLockBlack(), this);
-      texOnWhite->DeserializeReadLock(op.sharedLockWhite(), this);
+      texOnBlack->SetReadLock(FindReadLock(op.sharedLockBlack()));
+      texOnWhite->SetReadLock(FindReadLock(op.sharedLockWhite()));
 
       MOZ_ASSERT(texOnBlack && texOnWhite);
       compositable->UseComponentAlphaTextures(texOnBlack, texOnWhite);
 
       if (texOnBlack) {
         texOnBlack->SetLastFwdTransactionId(mFwdTransactionId);
         // Make sure that each texture was handled by the compositable
         // because the recycling logic depends on it.
@@ -278,11 +278,34 @@ CompositableParentManager::ReleaseCompos
   }
 
   RefPtr<CompositableHost> host = iter->second;
   mCompositables.erase(iter);
 
   host->Detach(nullptr, CompositableHost::FORCE_DETACH);
 }
 
+bool
+CompositableParentManager::AddReadLocks(ReadLockArray&& aReadLocks)
+{
+  for (ReadLockInit& r : aReadLocks) {
+    if (mReadLocks.find(r.handle().Value()) != mReadLocks.end()) {
+      NS_ERROR("Duplicate read lock handle!");
+      return false;
+    }
+    mReadLocks[r.handle().Value()] = TextureReadLock::Deserialize(r.sharedLock(), this);
+  }
+  return true;
+}
+
+TextureReadLock*
+CompositableParentManager::FindReadLock(const ReadLockHandle& aHandle)
+{
+  auto iter = mReadLocks.find(aHandle.Value());
+  if (iter == mReadLocks.end()) {
+    return nullptr;
+  }
+  return iter->second.get();
+}
+
 } // namespace layers
 } // namespace mozilla
 
--- a/gfx/layers/ipc/CompositableTransactionParent.h
+++ b/gfx/layers/ipc/CompositableTransactionParent.h
@@ -7,28 +7,31 @@
 
 #ifndef MOZILLA_LAYERS_COMPOSITABLETRANSACTIONPARENT_H
 #define MOZILLA_LAYERS_COMPOSITABLETRANSACTIONPARENT_H
 
 #include <vector>                       // for vector
 #include "mozilla/Attributes.h"         // for override
 #include "mozilla/layers/ISurfaceAllocator.h"  // for ISurfaceAllocator
 #include "mozilla/layers/LayersMessages.h"  // for EditReply, etc
+#include "mozilla/layers/TextureClient.h"
 #include "CompositableHost.h"
 
 namespace mozilla {
 namespace layers {
 
 // Since PCompositble has two potential manager protocols, we can't just call
 // the Manager() method usually generated when there's one manager protocol,
 // so both manager protocols implement this and we keep a reference to them
 // through this interface.
 class CompositableParentManager : public HostIPCAllocator
 {
 public:
+  typedef InfallibleTArray<ReadLockInit> ReadLockArray;
+
   CompositableParentManager() {}
 
   void DestroyActor(const OpDestroy& aOp);
 
   void UpdateFwdTransactionId(uint64_t aTransactionId)
   {
     MOZ_ASSERT(mFwdTransactionId < aTransactionId);
     mFwdTransactionId = aTransactionId;
@@ -36,28 +39,47 @@ public:
 
   uint64_t GetFwdTransactionId() { return mFwdTransactionId; }
 
   RefPtr<CompositableHost> AddCompositable(
     const CompositableHandle& aHandle,
     const TextureInfo& aInfo);
   RefPtr<CompositableHost> FindCompositable(const CompositableHandle& aHandle);
 
+  bool AddReadLocks(ReadLockArray&& aReadLocks);
+  TextureReadLock* FindReadLock(const ReadLockHandle& aLockHandle);
+
 protected:
   /**
    * Handle the IPDL messages that affect PCompositable actors.
    */
   bool ReceiveCompositableUpdate(const CompositableOperation& aEdit);
 
   void ReleaseCompositable(const CompositableHandle& aHandle);
 
   uint64_t mFwdTransactionId = 0;
 
   /**
    * Mapping form IDs to CompositableHosts.
    */
   std::map<uint64_t, RefPtr<CompositableHost>> mCompositables;
+  std::map<uint64_t, RefPtr<TextureReadLock>> mReadLocks;
+
+};
+
+struct AutoClearReadLocks {
+  explicit AutoClearReadLocks(std::map<uint64_t, RefPtr<TextureReadLock>>& aReadLocks)
+    : mReadLocks(aReadLocks)
+
+  {}
+
+  ~AutoClearReadLocks()
+  {
+    mReadLocks.clear();
+  }
+
+  std::map<uint64_t, RefPtr<TextureReadLock>>& mReadLocks;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif
--- a/gfx/layers/ipc/ISurfaceAllocator.cpp
+++ b/gfx/layers/ipc/ISurfaceAllocator.cpp
@@ -6,24 +6,36 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ISurfaceAllocator.h"
 
 #include "gfxPrefs.h"
 #include "mozilla/layers/ImageBridgeParent.h" // for ImageBridgeParent
 #include "mozilla/layers/TextureHost.h"       // for TextureHost
 #include "mozilla/layers/TextureForwarder.h"
+#include "mozilla/layers/CompositableForwarder.h"
 
 namespace mozilla {
 namespace layers {
 
 NS_IMPL_ISUPPORTS(GfxMemoryImageReporter, nsIMemoryReporter)
 
 mozilla::Atomic<ptrdiff_t> GfxMemoryImageReporter::sAmount(0);
 
+/* static */ uint32_t
+CompositableForwarder::GetMaxFileDescriptorsPerMessage() {
+#if defined(OS_POSIX)
+  static const uint32_t kMaxFileDescriptors = FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE;
+#else
+  // default number that works everywhere else
+  static const uint32_t kMaxFileDescriptors = 250;
+#endif
+  return kMaxFileDescriptors;
+}
+
 mozilla::ipc::SharedMemory::SharedMemoryType OptimalShmemType()
 {
   return ipc::SharedMemory::SharedMemoryType::TYPE_BASIC;
 }
 
 void
 HostIPCAllocator::SendPendingAsyncMessages()
 {
--- a/gfx/layers/ipc/ImageBridgeChild.cpp
+++ b/gfx/layers/ipc/ImageBridgeChild.cpp
@@ -53,53 +53,71 @@ namespace layers {
 using base::Thread;
 using base::ProcessId;
 using namespace mozilla::ipc;
 using namespace mozilla::gfx;
 using namespace mozilla::media;
 
 typedef std::vector<CompositableOperation> OpVector;
 typedef nsTArray<OpDestroy> OpDestroyVector;
+typedef nsTArray<ReadLockInit> ReadLockVector;
 
 struct CompositableTransaction
 {
   CompositableTransaction()
-  : mFinished(true)
+  : mReadLockSequenceNumber(0)
+  , mFinished(true)
   {}
   ~CompositableTransaction()
   {
     End();
   }
   bool Finished() const
   {
     return mFinished;
   }
   void Begin()
   {
     MOZ_ASSERT(mFinished);
     mFinished = false;
+    mReadLockSequenceNumber = 0;
+    mReadLocks.AppendElement();
   }
   void End()
   {
     mFinished = true;
     mOperations.clear();
     mDestroyedActors.Clear();
+    mReadLocks.Clear();
   }
   bool IsEmpty() const
   {
     return mOperations.empty() && mDestroyedActors.IsEmpty();
   }
   void AddNoSwapEdit(const CompositableOperation& op)
   {
     MOZ_ASSERT(!Finished(), "forgot BeginTransaction?");
     mOperations.push_back(op);
   }
 
+  ReadLockHandle AddReadLock(const ReadLockDescriptor& aReadLock)
+  {
+    ReadLockHandle handle(++mReadLockSequenceNumber);
+    if (mReadLocks.LastElement().Length() >= CompositableForwarder::GetMaxFileDescriptorsPerMessage()) {
+      mReadLocks.AppendElement();
+    }
+    mReadLocks.LastElement().AppendElement(ReadLockInit(aReadLock, handle));
+    return handle;
+  }
+
   OpVector mOperations;
   OpDestroyVector mDestroyedActors;
+  nsTArray<ReadLockVector> mReadLocks;
+  uint64_t mReadLockSequenceNumber;
+
   bool mFinished;
 };
 
 struct AutoEndTransaction {
   explicit AutoEndTransaction(CompositableTransaction* aTxn) : mTxn(aTxn) {}
   ~AutoEndTransaction() { mTxn->End(); }
   CompositableTransaction* mTxn;
 };
@@ -118,61 +136,39 @@ ImageBridgeChild::UseTextures(Compositab
     MOZ_ASSERT(t.mTextureClient);
     MOZ_ASSERT(t.mTextureClient->GetIPDLActor());
 
     if (!t.mTextureClient->IsSharedWithCompositor()) {
       return;
     }
 
     ReadLockDescriptor readLock;
-    t.mTextureClient->SerializeReadLock(readLock);
+    ReadLockHandle readLockHandle;
+    if (t.mTextureClient->SerializeReadLock(readLock)) {
+      readLockHandle = mTxn->AddReadLock(readLock);
+    }
 
     textures.AppendElement(TimedTexture(nullptr, t.mTextureClient->GetIPDLActor(),
-                                        readLock,
+                                        readLockHandle,
                                         t.mTimeStamp, t.mPictureRect,
                                         t.mFrameID, t.mProducerID));
 
     // Wait end of usage on host side if TextureFlags::RECYCLE is set
     HoldUntilCompositableRefReleasedIfNecessary(t.mTextureClient);
   }
   mTxn->AddNoSwapEdit(CompositableOperation(aCompositable->GetIPCHandle(),
                                             OpUseTexture(textures)));
 }
 
 void
 ImageBridgeChild::UseComponentAlphaTextures(CompositableClient* aCompositable,
                                             TextureClient* aTextureOnBlack,
                                             TextureClient* aTextureOnWhite)
 {
-  MOZ_ASSERT(aCompositable);
-  MOZ_ASSERT(aTextureOnWhite);
-  MOZ_ASSERT(aTextureOnBlack);
-  MOZ_ASSERT(aCompositable->IsConnected());
-  MOZ_ASSERT(aTextureOnWhite->GetIPDLActor());
-  MOZ_ASSERT(aTextureOnBlack->GetIPDLActor());
-  MOZ_ASSERT(aTextureOnBlack->GetSize() == aTextureOnWhite->GetSize());
-
-  ReadLockDescriptor readLockW;
-  ReadLockDescriptor readLockB;
-  aTextureOnBlack->SerializeReadLock(readLockB);
-  aTextureOnWhite->SerializeReadLock(readLockW);
-
-  HoldUntilCompositableRefReleasedIfNecessary(aTextureOnBlack);
-  HoldUntilCompositableRefReleasedIfNecessary(aTextureOnWhite);
-
-  mTxn->AddNoSwapEdit(
-    CompositableOperation(
-      aCompositable->GetIPCHandle(),
-      OpUseComponentAlphaTextures(
-        nullptr, aTextureOnBlack->GetIPDLActor(),
-        nullptr, aTextureOnWhite->GetIPDLActor(),
-        readLockB, readLockW
-      )
-    )
-  );
+  MOZ_CRASH("should not be called");
 }
 
 void
 ImageBridgeChild::HoldUntilCompositableRefReleasedIfNecessary(TextureClient* aClient)
 {
   // Wait ReleaseCompositableRef only when TextureFlags::RECYCLE is set on ImageBridge.
   if (!aClient ||
       !(aClient->GetFlags() & TextureFlags::RECYCLE)) {
@@ -538,16 +534,25 @@ ImageBridgeChild::EndTransaction()
   if (!mTxn->mOperations.empty()) {
     cset.AppendElements(&mTxn->mOperations.front(), mTxn->mOperations.size());
   }
 
   if (!IsSameProcess()) {
     ShadowLayerForwarder::PlatformSyncBeforeUpdate();
   }
 
+  for (ReadLockVector& locks : mTxn->mReadLocks) {
+    if (locks.Length()) {
+      if (!SendInitReadLocks(locks)) {
+        NS_WARNING("[LayersForwarder] WARNING: sending read locks failed!");
+        return;
+      }
+    }
+  }
+
   if (!SendUpdate(cset, mTxn->mDestroyedActors, GetFwdTransactionId())) {
     NS_WARNING("could not send async texture transaction");
     return;
   }
 }
 
 void
 ImageBridgeChild::SendImageBridgeThreadId()
--- a/gfx/layers/ipc/ImageBridgeParent.cpp
+++ b/gfx/layers/ipc/ImageBridgeParent.cpp
@@ -148,23 +148,33 @@ public:
     }
   }
 private:
   ImageBridgeParent* mImageBridge;
   InfallibleTArray<OpDestroy>* mToDestroy;
 };
 
 mozilla::ipc::IPCResult
+ImageBridgeParent::RecvInitReadLocks(ReadLockArray&& aReadLocks)
+{
+  if (!AddReadLocks(Move(aReadLocks))) {
+    return IPC_FAIL_NO_REASON(this);
+  }
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
 ImageBridgeParent::RecvUpdate(EditArray&& aEdits, OpDestroyArray&& aToDestroy,
                               const uint64_t& aFwdTransactionId)
 {
   // This ensures that destroy operations are always processed. It is not safe
   // to early-return from RecvUpdate without doing so.
   AutoImageBridgeParentAsyncMessageSender autoAsyncMessageSender(this, &aToDestroy);
   UpdateFwdTransactionId(aFwdTransactionId);
+  AutoClearReadLocks clearLocks(mReadLocks);
 
   for (EditArray::index_type i = 0; i < aEdits.Length(); ++i) {
     if (!ReceiveCompositableUpdate(aEdits[i])) {
       return IPC_FAIL_NO_REASON(this);
     }
   }
 
   if (!IsSameProcess()) {
--- a/gfx/layers/ipc/ImageBridgeParent.h
+++ b/gfx/layers/ipc/ImageBridgeParent.h
@@ -65,16 +65,17 @@ public:
 
   virtual base::ProcessId GetChildProcessId() override
   {
     return OtherPid();
   }
 
   // PImageBridge
   virtual mozilla::ipc::IPCResult RecvImageBridgeThreadId(const PlatformThreadId& aThreadId) override;
+  virtual mozilla::ipc::IPCResult RecvInitReadLocks(ReadLockArray&& aReadLocks) override;
   virtual mozilla::ipc::IPCResult RecvUpdate(EditArray&& aEdits, OpDestroyArray&& aToDestroy,
                                           const uint64_t& aFwdTransactionId) override;
 
   virtual PTextureParent* AllocPTextureParent(const SurfaceDescriptor& aSharedData,
                                               const LayersBackend& aLayersBackend,
                                               const TextureFlags& aFlags,
                                               const uint64_t& aSerial) override;
   virtual bool DeallocPTextureParent(PTextureParent* actor) override;
--- a/gfx/layers/ipc/LayerTransactionParent.cpp
+++ b/gfx/layers/ipc/LayerTransactionParent.cpp
@@ -128,29 +128,39 @@ mozilla::ipc::IPCResult
 LayerTransactionParent::RecvPaintTime(const uint64_t& aTransactionId,
                                       const TimeDuration& aPaintTime)
 {
   mCompositorBridge->UpdatePaintTime(this, aPaintTime);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
+LayerTransactionParent::RecvInitReadLocks(ReadLockArray&& aReadLocks)
+{
+  if (!AddReadLocks(Move(aReadLocks))) {
+    return IPC_FAIL_NO_REASON(this);
+  }
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
 LayerTransactionParent::RecvUpdate(const TransactionInfo& aInfo)
 {
   GeckoProfilerTracingRAII tracer("Paint", "LayerTransaction");
   PROFILER_LABEL("LayerTransactionParent", "RecvUpdate",
     js::ProfileEntry::Category::GRAPHICS);
 
 #ifdef COMPOSITOR_PERFORMANCE_WARNING
   TimeStamp updateStart = TimeStamp::Now();
 #endif
 
   MOZ_LAYERS_LOG(("[ParentSide] received txn with %" PRIuSIZE " edits", aInfo.cset().Length()));
 
   UpdateFwdTransactionId(aInfo.fwdTransactionId());
+  AutoClearReadLocks clearLocks(mReadLocks);
 
   if (mDestroyed || !layer_manager() || layer_manager()->IsDestroyed()) {
     for (const auto& op : aInfo.toDestroy()) {
       DestroyActor(op);
     }
     return IPC_OK();
   }
 
--- a/gfx/layers/ipc/LayerTransactionParent.h
+++ b/gfx/layers/ipc/LayerTransactionParent.h
@@ -38,16 +38,17 @@ class CompositorBridgeParentBase;
 class LayerTransactionParent final : public PLayerTransactionParent,
                                      public CompositableParentManager,
                                      public ShmemAllocator
 {
   typedef mozilla::layout::RenderFrameParent RenderFrameParent;
   typedef InfallibleTArray<Edit> EditArray;
   typedef InfallibleTArray<OpDestroy> OpDestroyArray;
   typedef InfallibleTArray<PluginWindowData> PluginsArray;
+  typedef InfallibleTArray<ReadLockInit> ReadLockArray;
 
 public:
   LayerTransactionParent(HostLayerManager* aManager,
                          CompositorBridgeParentBase* aBridge,
                          uint64_t aId);
 
 protected:
   ~LayerTransactionParent();
@@ -106,16 +107,17 @@ public:
   }
 
 protected:
   virtual mozilla::ipc::IPCResult RecvShutdown() override;
 
   virtual mozilla::ipc::IPCResult RecvPaintTime(const uint64_t& aTransactionId,
                                                 const TimeDuration& aPaintTime) override;
 
+  virtual mozilla::ipc::IPCResult RecvInitReadLocks(ReadLockArray&& aReadLocks) override;
   virtual mozilla::ipc::IPCResult RecvUpdate(const TransactionInfo& aInfo) override;
 
   virtual mozilla::ipc::IPCResult RecvSetLayerObserverEpoch(const uint64_t& aLayerObserverEpoch) override;
   virtual mozilla::ipc::IPCResult RecvNewCompositable(const CompositableHandle& aHandle,
                                                       const TextureInfo& aInfo) override;
   virtual mozilla::ipc::IPCResult RecvReleaseLayer(const LayerHandle& aHandle) override;
   virtual mozilla::ipc::IPCResult RecvReleaseCompositable(const CompositableHandle& aHandle) override;
 
--- a/gfx/layers/ipc/LayersMessages.ipdlh
+++ b/gfx/layers/ipc/LayersMessages.ipdlh
@@ -45,16 +45,17 @@ using mozilla::layers::FrameMetrics::Vie
 using mozilla::layers::LayersBackend from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::MaybeLayerClip from "FrameMetrics.h";
 using mozilla::gfx::Glyph from "Layers.h";
 using mozilla::layers::BorderColors from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::BorderCorners from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::BorderWidths from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::LayerHandle from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::CompositableHandle from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::ReadLockHandle from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::SimpleLayerAttributes from "mozilla/layers/LayerAttributes.h";
 using mozilla::CrossProcessSemaphoreHandle from "mozilla/ipc/CrossProcessSemaphore.h";
 
 namespace mozilla {
 namespace layers {
 
 struct TargetConfig {
   IntRect naturalBounds;
@@ -343,16 +344,21 @@ struct CrossProcessSemaphoreDescriptor {
 
 union ReadLockDescriptor {
   ShmemSection;
   CrossProcessSemaphoreDescriptor;
   uintptr_t;
   null_t;
 };
 
+struct ReadLockInit {
+  ReadLockDescriptor sharedLock;
+  ReadLockHandle handle;
+};
+
 union MaybeTexture {
   PTexture;
   null_t;
 };
 
 struct TexturedTileDescriptor {
   PTexture texture;
   MaybeTexture textureOnWhite;
@@ -398,17 +404,17 @@ struct OpPaintTextureRegion {
  * Tells the CompositableHost to remove the corresponding TextureHost
  */
 struct OpRemoveTexture {
   PTexture texture;
 };
 
 struct TimedTexture {
   PTexture texture;
-  ReadLockDescriptor sharedLock;
+  ReadLockHandle sharedLock;
   TimeStamp timeStamp;
   IntRect picture;
   uint32_t frameID;
   uint32_t producerID;
 };
 
 /**
  * Tells the compositor-side which textures to use (for example, as front buffer
@@ -422,18 +428,18 @@ struct TimedTexture {
  */
 struct OpUseTexture {
   TimedTexture[] textures;
 };
 
 struct OpUseComponentAlphaTextures {
   PTexture textureOnBlack;
   PTexture textureOnWhite;
-  ReadLockDescriptor sharedLockBlack;
-  ReadLockDescriptor sharedLockWhite;
+  ReadLockHandle sharedLockBlack;
+  ReadLockHandle sharedLockWhite;
 };
 
 union MaybeRegion {
   nsIntRegion;
   null_t;
 };
 
 struct OpNotifyNotUsed {
--- a/gfx/layers/ipc/PImageBridge.ipdl
+++ b/gfx/layers/ipc/PImageBridge.ipdl
@@ -33,16 +33,21 @@ sync protocol PImageBridge
 child:
   async ParentAsyncMessages(AsyncParentMessageData[] aMessages);
 
   async DidComposite(ImageCompositeNotification[] aNotifications);
 
 parent:
   async ImageBridgeThreadId(PlatformThreadId aTreahdId);
 
+  // Creates a set of mappings between TextureReadLocks and an associated
+  // ReadLockHandle that can be used in Update, and persist until the
+  // next Update call.
+  async InitReadLocks(ReadLockInit[] locks);
+
   async Update(CompositableOperation[] ops, OpDestroy[] toDestroy, uint64_t fwdTransactionId);
 
   // 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 channel getting closed.
   // In the child side, the Closing the channel does not happen right after WillClose,
   // 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
--- a/gfx/layers/ipc/PLayerTransaction.ipdl
+++ b/gfx/layers/ipc/PLayerTransaction.ipdl
@@ -45,16 +45,21 @@ union MaybeTransform {
  * tab, in which case the PLayerTransactionChild exists in the content process.
  * In either case, the PLayerTransactionParent exists in the GPU process (if
  * there is one) or the UI process otherwise.
  */
 sync protocol PLayerTransaction {
   manager PCompositorBridge;
 
 parent:
+  // Creates a set of mappings between TextureReadLocks and an associated
+  // ReadLockHandle that can be used in Update, and persist until the
+  // next Update call.
+  async InitReadLocks(ReadLockInit[] locks);
+
   // The isFirstPaint flag can be used to indicate that this is the first update
   // for a particular document.
   async Update(TransactionInfo txn);
 
   async PaintTime(uint64_t id, TimeDuration paintTime);
 
   async SetLayerObserverEpoch(uint64_t layerObserverEpoch);
 
--- a/gfx/layers/ipc/PWebRenderBridge.ipdl
+++ b/gfx/layers/ipc/PWebRenderBridge.ipdl
@@ -27,16 +27,21 @@ namespace layers {
 sync protocol PWebRenderBridge
 {
   manager PCompositorBridge;
 
 parent:
   async NewCompositable(CompositableHandle handle, TextureInfo info);
   async ReleaseCompositable(CompositableHandle compositable);
 
+  // Creates a set of mappings between TextureReadLocks and an associated
+  // ReadLockHandle that can be used in Update, and persist until the
+  // next Update call.
+  async InitReadLocks(ReadLockInit[] locks);
+
   sync Create(IntSize aSize);
   sync AddImage(ImageKey aImageKey, IntSize aSize, uint32_t aStride,
                 SurfaceFormat aFormat, ByteBuffer aBytes);
   sync UpdateImage(ImageKey aImageKey, IntSize aSize,
                    SurfaceFormat aFormat, ByteBuffer aBytes);
   sync DeleteImage(ImageKey aImageKey);
   async AddRawFont(FontKey aFontKey, ByteBuffer aBytes, uint32_t aFontIndex);
   async DPBegin(IntSize aSize);
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -49,22 +49,24 @@ using namespace mozilla::gl;
 using namespace mozilla::ipc;
 
 class ClientTiledLayerBuffer;
 
 typedef nsTArray<SurfaceDescriptor> BufferArray;
 typedef nsTArray<Edit> EditVector;
 typedef nsTHashtable<nsPtrHashKey<ShadowableLayer>> ShadowableLayerSet;
 typedef nsTArray<OpDestroy> OpDestroyVector;
+typedef nsTArray<ReadLockInit> ReadLockVector;
 
 class Transaction
 {
 public:
   Transaction()
-    : mTargetRotation(ROTATION_0)
+    : mReadLockSequenceNumber(0)
+    , mTargetRotation(ROTATION_0)
     , mOpen(false)
     , mRotationChanged(false)
   {}
 
   void Begin(const gfx::IntRect& aTargetBounds, ScreenRotation aRotation,
              dom::ScreenOrientationInternal aOrientation)
   {
     mOpen = true;
@@ -73,16 +75,18 @@ public:
       // the first time this is called, mRotationChanged will be false if
       // aRotation is 0, but we should be OK because for the first transaction
       // we should only compose if it is non-empty. See the caller(s) of
       // RotationChanged.
       mRotationChanged = true;
     }
     mTargetRotation = aRotation;
     mTargetOrientation = aOrientation;
+    mReadLockSequenceNumber = 0;
+    mReadLocks.AppendElement();
   }
   void AddEdit(const Edit& aEdit)
   {
     MOZ_ASSERT(!Finished(), "forgot BeginTransaction?");
     mCset.AppendElement(aEdit);
   }
   void AddEdit(const CompositableOperation& aEdit)
   {
@@ -99,23 +103,33 @@ public:
     MOZ_ASSERT(!Finished(), "forgot BeginTransaction?");
     mMutants.PutEntry(aLayer);
   }
   void AddSimpleMutant(ShadowableLayer* aLayer)
   {
     MOZ_ASSERT(!Finished(), "forgot BeginTransaction?");
     mSimpleMutants.PutEntry(aLayer);
   }
+  ReadLockHandle AddReadLock(const ReadLockDescriptor& aReadLock)
+  {
+    ReadLockHandle handle(++mReadLockSequenceNumber);
+    if (mReadLocks.LastElement().Length() >= CompositableForwarder::GetMaxFileDescriptorsPerMessage()) {
+      mReadLocks.AppendElement();
+    }
+    mReadLocks.LastElement().AppendElement(ReadLockInit(aReadLock, handle));
+    return handle;
+  }
   void End()
   {
     mCset.Clear();
     mPaints.Clear();
     mMutants.Clear();
     mSimpleMutants.Clear();
     mDestroyedActors.Clear();
+    mReadLocks.Clear();
     mOpen = false;
     mRotationChanged = false;
   }
 
   bool Empty() const {
     return mCset.IsEmpty() &&
            mPaints.IsEmpty() &&
            mMutants.IsEmpty() &&
@@ -129,16 +143,18 @@ public:
 
   bool Opened() const { return mOpen; }
 
   EditVector mCset;
   nsTArray<CompositableOperation> mPaints;
   OpDestroyVector mDestroyedActors;
   ShadowableLayerSet mMutants;
   ShadowableLayerSet mSimpleMutants;
+  nsTArray<ReadLockVector> mReadLocks;
+  uint64_t mReadLockSequenceNumber;
   gfx::IntRect mTargetBounds;
   ScreenRotation mTargetRotation;
   dom::ScreenOrientationInternal mTargetOrientation;
 
 private:
   bool mOpen;
   bool mRotationChanged;
 
@@ -415,19 +431,22 @@ ShadowLayerForwarder::UseTextures(Compos
 
   AutoTArray<TimedTexture,4> textures;
 
   for (auto& t : aTextures) {
     MOZ_ASSERT(t.mTextureClient);
     MOZ_ASSERT(t.mTextureClient->GetIPDLActor());
     MOZ_RELEASE_ASSERT(t.mTextureClient->GetIPDLActor()->GetIPCChannel() == mShadowManager->GetIPCChannel());
     ReadLockDescriptor readLock;
-    t.mTextureClient->SerializeReadLock(readLock);
+    ReadLockHandle readLockHandle;
+    if (t.mTextureClient->SerializeReadLock(readLock)) {
+      readLockHandle = mTxn->AddReadLock(readLock);
+    }
     textures.AppendElement(TimedTexture(nullptr, t.mTextureClient->GetIPDLActor(),
-                                        readLock,
+                                        readLockHandle,
                                         t.mTimeStamp, t.mPictureRect,
                                         t.mFrameID, t.mProducerID));
     mClientLayerManager->GetCompositorBridgeChild()->HoldUntilCompositableRefReleasedIfNecessary(t.mTextureClient);
   }
   mTxn->AddEdit(CompositableOperation(aCompositable->GetIPCHandle(),
                                       OpUseTexture(textures)));
 }
 
@@ -446,31 +465,37 @@ ShadowLayerForwarder::UseComponentAlphaT
   MOZ_ASSERT(aTextureOnBlack);
   MOZ_ASSERT(aCompositable->GetIPCHandle());
   MOZ_ASSERT(aTextureOnBlack->GetIPDLActor());
   MOZ_ASSERT(aTextureOnWhite->GetIPDLActor());
   MOZ_ASSERT(aTextureOnBlack->GetSize() == aTextureOnWhite->GetSize());
   MOZ_RELEASE_ASSERT(aTextureOnWhite->GetIPDLActor()->GetIPCChannel() == mShadowManager->GetIPCChannel());
   MOZ_RELEASE_ASSERT(aTextureOnBlack->GetIPDLActor()->GetIPCChannel() == mShadowManager->GetIPCChannel());
 
+  ReadLockDescriptor readLockB;
+  ReadLockHandle readLockHandleB;
   ReadLockDescriptor readLockW;
-  ReadLockDescriptor readLockB;
-  aTextureOnBlack->SerializeReadLock(readLockB);
-  aTextureOnWhite->SerializeReadLock(readLockW);
+  ReadLockHandle readLockHandleW;
+  if (aTextureOnBlack->SerializeReadLock(readLockB)) {
+    readLockHandleB = mTxn->AddReadLock(readLockB);
+  }
+  if (aTextureOnWhite->SerializeReadLock(readLockW)) {
+    readLockHandleW = mTxn->AddReadLock(readLockW);
+  }
 
   mClientLayerManager->GetCompositorBridgeChild()->HoldUntilCompositableRefReleasedIfNecessary(aTextureOnBlack);
   mClientLayerManager->GetCompositorBridgeChild()->HoldUntilCompositableRefReleasedIfNecessary(aTextureOnWhite);
 
   mTxn->AddEdit(
     CompositableOperation(
       aCompositable->GetIPCHandle(),
       OpUseComponentAlphaTextures(
         nullptr, aTextureOnBlack->GetIPDLActor(),
         nullptr, aTextureOnWhite->GetIPDLActor(),
-        readLockB, readLockW)
+        readLockHandleB, readLockHandleW)
       )
     );
 }
 
 static bool
 AddOpDestroy(Transaction* aTxn, const OpDestroy& op)
 {
   if (!aTxn->Opened()) {
@@ -693,16 +718,25 @@ ShadowLayerForwarder::EndTransaction(con
                             aRegionToClear);
   info.targetConfig() = targetConfig;
 
   if (!GetTextureForwarder()->IsSameProcess()) {
     MOZ_LAYERS_LOG(("[LayersForwarder] syncing before send..."));
     PlatformSyncBeforeUpdate();
   }
 
+  for (ReadLockVector& locks : mTxn->mReadLocks) {
+    if (locks.Length()) {
+      if (!mShadowManager->SendInitReadLocks(locks)) {
+        MOZ_LAYERS_LOG(("[LayersForwarder] WARNING: sending read locks failed!"));
+        return false;
+      }
+    }
+  }
+
   MOZ_LAYERS_LOG(("[LayersForwarder] sending transaction..."));
   RenderTraceScope rendertrace3("Forward Transaction", "000093");
   if (!mShadowManager->SendUpdate(info)) {
     MOZ_LAYERS_LOG(("[LayersForwarder] WARNING: sending transaction failed!"));
     return false;
   }
 
   *aSent = true;
--- a/gfx/layers/wr/WebRenderBridgeChild.cpp
+++ b/gfx/layers/wr/WebRenderBridgeChild.cpp
@@ -12,17 +12,18 @@
 #include "mozilla/layers/ImageDataSerializer.h"
 #include "mozilla/layers/PTextureChild.h"
 #include "mozilla/webrender/WebRenderAPI.h"
 
 namespace mozilla {
 namespace layers {
 
 WebRenderBridgeChild::WebRenderBridgeChild(const wr::PipelineId& aPipelineId)
-  : mIsInTransaction(false)
+  : mReadLockSequenceNumber(0)
+  , mIsInTransaction(false)
   , mIdNamespace(0)
   , mResourceId(0)
   , mPipelineId(aPipelineId)
   , mIPCOpen(false)
   , mDestroyed(false)
 {
 }
 
@@ -77,16 +78,18 @@ bool
 WebRenderBridgeChild::DPBegin(const gfx::IntSize& aSize)
 {
   MOZ_ASSERT(!mDestroyed);
   MOZ_ASSERT(!mIsInTransaction);
 
   UpdateFwdTransactionId();
   this->SendDPBegin(aSize);
   mIsInTransaction = true;
+  mReadLockSequenceNumber = 0;
+  mReadLocks.AppendElement();
   return true;
 }
 
 wr::BuiltDisplayList
 WebRenderBridgeChild::ProcessWebrenderCommands(const gfx::IntSize &aSize,
                                                InfallibleTArray<WebRenderCommand>& aCommands)
 {
   wr::DisplayListBuilder builder(mPipelineId);
@@ -192,31 +195,41 @@ WebRenderBridgeChild::ProcessWebrenderCo
 }
 
 void
 WebRenderBridgeChild::DPEnd(const gfx::IntSize& aSize, bool aIsSync, uint64_t aTransactionId)
 {
   MOZ_ASSERT(!mDestroyed);
   MOZ_ASSERT(mIsInTransaction);
 
+  for (nsTArray<ReadLockInit>& locks : mReadLocks) {
+    if (locks.Length()) {
+      if (!SendInitReadLocks(locks)) {
+        NS_WARNING("WARNING: sending read locks failed!");
+        return;
+      }
+    }
+  }
+
   wr::BuiltDisplayList dl = ProcessWebrenderCommands(aSize, mCommands);
   ByteBuffer dlData(Move(dl.dl));
   ByteBuffer auxData(Move(dl.aux));
 
   if (aIsSync) {
     this->SendDPSyncEnd(aSize, mParentCommands, mDestroyedActors, GetFwdTransactionId(), aTransactionId,
                         dlData, dl.dl_desc, auxData, dl.aux_desc);
   } else {
     this->SendDPEnd(aSize, mParentCommands, mDestroyedActors, GetFwdTransactionId(), aTransactionId,
                     dlData, dl.dl_desc, auxData, dl.aux_desc);
   }
 
   mCommands.Clear();
   mParentCommands.Clear();
   mDestroyedActors.Clear();
+  mReadLocks.Clear();
   mIsInTransaction = false;
 }
 
 uint64_t
 WebRenderBridgeChild::GetNextExternalImageId()
 {
   static uint32_t sNextID = 1;
   ++sNextID;
@@ -371,19 +384,26 @@ WebRenderBridgeChild::UseTextures(Compos
 
   AutoTArray<TimedTexture,4> textures;
 
   for (auto& t : aTextures) {
     MOZ_ASSERT(t.mTextureClient);
     MOZ_ASSERT(t.mTextureClient->GetIPDLActor());
     MOZ_RELEASE_ASSERT(t.mTextureClient->GetIPDLActor()->GetIPCChannel() == GetIPCChannel());
     ReadLockDescriptor readLock;
-    t.mTextureClient->SerializeReadLock(readLock);
+    ReadLockHandle readLockHandle;
+    if (t.mTextureClient->SerializeReadLock(readLock)) {
+      readLockHandle = ReadLockHandle(++mReadLockSequenceNumber);
+      if (mReadLocks.LastElement().Length() >= GetMaxFileDescriptorsPerMessage()) {
+        mReadLocks.AppendElement();
+      }
+      mReadLocks.LastElement().AppendElement(ReadLockInit(readLock, readLockHandle));
+    }
     textures.AppendElement(TimedTexture(nullptr, t.mTextureClient->GetIPDLActor(),
-                                        readLock,
+                                        readLockHandle,
                                         t.mTimeStamp, t.mPictureRect,
                                         t.mFrameID, t.mProducerID));
     GetCompositorBridgeChild()->HoldUntilCompositableRefReleasedIfNecessary(t.mTextureClient);
   }
   AddWebRenderParentCommand(CompositableOperation(aCompositable->GetIPCHandle(),
                                             OpUseTexture(textures)));
 }
 
--- a/gfx/layers/wr/WebRenderBridgeChild.h
+++ b/gfx/layers/wr/WebRenderBridgeChild.h
@@ -109,16 +109,18 @@ private:
   }
 
   bool AddOpDestroy(const OpDestroy& aOp);
 
   nsTArray<WebRenderCommand> mCommands;
   nsTArray<WebRenderParentCommand> mParentCommands;
   nsTArray<OpDestroy> mDestroyedActors;
   nsDataHashtable<nsUint64HashKey, CompositableClient*> mCompositables;
+  nsTArray<nsTArray<ReadLockInit>> mReadLocks;
+  uint64_t mReadLockSequenceNumber;
   bool mIsInTransaction;
   uint32_t mIdNamespace;
   uint32_t mResourceId;
   wr::PipelineId mPipelineId;
 
   bool mIPCOpen;
   bool mDestroyed;
 };
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -221,16 +221,17 @@ WebRenderBridgeParent::HandleDPEnd(const
                                  const uint64_t& aFwdTransactionId,
                                  const uint64_t& aTransactionId,
                                  const ByteBuffer& dl,
                                  const WrBuiltDisplayListDescriptor& dlDesc,
                                  const ByteBuffer& aux,
                                  const WrAuxiliaryListsDescriptor& auxDesc)
 {
   UpdateFwdTransactionId(aFwdTransactionId);
+  AutoClearReadLocks clearLocks(mReadLocks);
 
   if (mDestroyed) {
     for (const auto& op : aToDestroy) {
       DestroyActor(op);
     }
     return;
   }
   // This ensures that destroy operations are always processed. It is not safe
@@ -648,16 +649,25 @@ WebRenderBridgeParent::RecvNewCompositab
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvReleaseCompositable(const CompositableHandle& aHandle)
 {
   ReleaseCompositable(aHandle);
   return IPC_OK();
 }
 
+mozilla::ipc::IPCResult
+WebRenderBridgeParent::RecvInitReadLocks(ReadLockArray&& aReadLocks)
+{
+  if (!AddReadLocks(Move(aReadLocks))) {
+    return IPC_FAIL_NO_REASON(this);
+  }
+  return IPC_OK();
+}
+
 void
 WebRenderBridgeParent::SetWebRenderProfilerEnabled(bool aEnabled)
 {
   if (mWidget) {
     // Only set the flag to "root" WebRenderBridgeParent.
     mApi->SetProfilerEnabled(aEnabled);
   }
 }
--- a/gfx/layers/wr/WebRenderBridgeParent.h
+++ b/gfx/layers/wr/WebRenderBridgeParent.h
@@ -54,16 +54,18 @@ public:
   wr::WebRenderAPI* GetWebRenderAPI() { return mApi; }
   WebRenderCompositableHolder* CompositableHolder() { return mCompositableHolder; }
   CompositorVsyncScheduler* CompositorScheduler() { return mCompositorScheduler.get(); }
 
   mozilla::ipc::IPCResult RecvNewCompositable(const CompositableHandle& aHandle,
                                               const TextureInfo& aInfo) override;
   mozilla::ipc::IPCResult RecvReleaseCompositable(const CompositableHandle& aHandle) override;
 
+  mozilla::ipc::IPCResult RecvInitReadLocks(ReadLockArray&& aReadLocks) override;
+
   mozilla::ipc::IPCResult RecvCreate(const gfx::IntSize& aSize) override;
   mozilla::ipc::IPCResult RecvShutdown() override;
   mozilla::ipc::IPCResult RecvAddImage(const wr::ImageKey& aImageKey,
                                        const gfx::IntSize& aSize,
                                        const uint32_t& aStride,
                                        const gfx::SurfaceFormat& aFormat,
                                        const ByteBuffer& aBuffer) override;
   mozilla::ipc::IPCResult RecvAddRawFont(const wr::FontKey& aFontKey,