Bug 1449982 - Conceptually split the mUpdaterQueue into separate queues per layers id. r=botond
authorKartikaya Gupta <kgupta@mozilla.com>
Tue, 10 Apr 2018 12:30:01 -0400
changeset 412621 3542146e997310c11ab000341cce6360623fce30
parent 412620 1a6398d3f9b34038484ea85e6dc9a6e0381f9f23
child 412622 9d0fc1d12d9a29d989df38bf74747490a86b114f
push id33813
push userccoroiu@mozilla.com
push dateTue, 10 Apr 2018 21:54:55 +0000
treeherdermozilla-central@d42671c2e69d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbotond
bugs1449982
milestone61.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 1449982 - Conceptually split the mUpdaterQueue into separate queues per layers id. r=botond This allows us to treat tasks from different layers id as independent, thereby removing the unnecessary latency increase as described in bug 1449982 comment 33. Note that we could probably implement this by actually maintaining separate queues for each layers id, but that involves more overhead since we would need to have a map from layers id to task queue, and removing entries from that map becomes tricky with respect to locking and timing. MozReview-Commit-ID: 7jtYqNDwJqP
gfx/layers/apz/public/APZUpdater.h
gfx/layers/apz/src/APZCTreeManager.cpp
gfx/layers/apz/src/APZUpdater.cpp
gfx/layers/ipc/APZCTreeManagerParent.cpp
gfx/layers/ipc/CompositorBridgeParent.cpp
gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
--- a/gfx/layers/apz/public/APZUpdater.h
+++ b/gfx/layers/apz/public/APZUpdater.h
@@ -55,17 +55,17 @@ public:
    * which thread it is.
    */
   static void SetUpdaterThread(const wr::WrWindowId& aWindowId);
   static void PrepareForSceneSwap(const wr::WrWindowId& aWindowId);
   static void CompleteSceneSwap(const wr::WrWindowId& aWindowId,
                                 wr::WrPipelineInfo* aInfo);
   static void ProcessPendingTasks(const wr::WrWindowId& aWindowId);
 
-  void ClearTree();
+  void ClearTree(LayersId aRootLayersId);
   void UpdateFocusState(LayersId aRootLayerTreeId,
                         LayersId aOriginatingLayersId,
                         const FocusTarget& aFocusTarget);
   void UpdateHitTestingTree(LayersId aRootLayerTreeId,
                             Layer* aRoot,
                             bool aIsFirstPaint,
                             LayersId aOriginatingLayersId,
                             uint32_t aPaintSequenceNumber);
@@ -104,41 +104,47 @@ public:
    * This does nothing if thread assertions are disabled.
    */
   void AssertOnUpdaterThread() const;
 
   /**
    * Runs the given task on the APZ "updater thread" for this APZUpdater. If
    * this function is called from the updater thread itself then the task is
    * run immediately without getting queued.
+   * The layers id argument should be the id of the layer tree that is
+   * requesting this task to be run. Conceptually each layer tree has a separate
+   * task queue, so that if one layer tree is blocked waiting for a scene build
+   * then tasks for the other layer trees can still be processed.
    */
-  void RunOnUpdaterThread(already_AddRefed<Runnable> aTask);
+  void RunOnUpdaterThread(LayersId aLayersId, already_AddRefed<Runnable> aTask);
 
   /**
    * Returns true if currently on the APZUpdater's "updater thread".
    */
   bool IsUpdaterThread() const;
 
   /**
    * Dispatches the given task to the APZ "controller thread", but does it *from*
    * the updater thread. That is, if the thread on which this function is called
    * is not the updater thread, the task is first dispatched to the updater thread.
    * When the updater thread runs it (or if this is called directly on the updater
    * thread), that is when the task gets dispatched to the controller thread.
    * The controller thread then actually runs the task.
+   * The layers id argument should be the id of the layer tree that is
+   * requesting this task to be run; in most cases this will probably just be
+   * the root layers id of the compositor.
    */
-  void RunOnControllerThread(already_AddRefed<Runnable> aTask);
+  void RunOnControllerThread(LayersId aLayersId, already_AddRefed<Runnable> aTask);
 
 protected:
   virtual ~APZUpdater();
 
   bool UsingWebRenderUpdaterThread() const;
   static already_AddRefed<APZUpdater> GetUpdater(const wr::WrWindowId& aWindowId);
 
-  bool IsQueueBlocked() const;
   void ProcessQueue();
 
 private:
   RefPtr<APZCTreeManager> mApz;
 
   // Map from layers id to WebRenderScrollData. This can only be touched on
   // the updater thread.
   std::unordered_map<LayersId,
@@ -153,32 +159,29 @@ private:
     // The epoch for the most recent scene built and swapped in on the WR side.
     Maybe<wr::Epoch> mBuilt;
     // True if and only if the layers id is the root layers id for the compositor
     bool mIsRoot;
 
     EpochState();
 
     // Whether or not the state for this layers id is such that it blocks
-    // processing of queued tasks. This happens if the root layers id or any
-    // "visible" layers id has scroll data for an epoch newer than what has
-    // been built. A "visible" layers id is one that is attached to the full
+    // processing of tasks for the layer tree. This happens if the root layers
+    // id or a "visible" layers id has scroll data for an epoch newer than what
+    // has been built. A "visible" layers id is one that is attached to the full
     // layer tree (i.e. there is a chain of reflayer items from the root layer
     // tree to the relevant layer subtree. This is not always the case; for
     // instance a content process may send the compositor layers for a document
     // before the chrome has attached the remote iframe to the root document.
     // Since WR only builds pipelines for "visible" layers ids, |mBuilt| being
     // populated means that the layers id is "visible".
-    bool IsBlockingQueue() const;
+    bool IsBlocked() const;
   };
 
-  // Map from layers id to epoch state. If any of the epoch states returns true
-  // for IsBlockingQueue(), that means we cannot yet process content-side data
-  // still in the task queue, because otherwise we would apply it before the
-  // scene swap for that scene has occurred.
+  // Map from layers id to epoch state.
   // This data structure can only be touched on the updater thread.
   std::unordered_map<LayersId,
                      EpochState,
                      LayersId::HashFn> mEpochData;
 
   // Used to manage the mapping from a WR window id to APZUpdater. These are only
   // used if WebRender is enabled. Both sWindowIdMap and mWindowId should only
   // be used while holding the sWindowIdLock.
@@ -196,20 +199,31 @@ private:
   // without locking should be fine.
   Maybe<PlatformThreadId> mUpdaterThreadId;
 #ifdef DEBUG
   // This flag is used to ensure that we don't ever try to do updater-thread
   // stuff before the updater thread has been properly initialized.
   mutable bool mUpdaterThreadQueried;
 #endif
 
+  // Helper struct that pairs each queued runnable with the layers id that it
+  // is associated with. This allows us to easily implement the conceptual
+  // separation of mUpdaterQueue into independent queues per layers id.
+  struct QueuedTask {
+    LayersId mLayersId;
+    RefPtr<Runnable> mRunnable;
+  };
+
   // Lock used to protect mUpdaterQueue
   Mutex mQueueLock;
-  // Holds a FIFO queue of tasks to be run on the updater thread,
+  // Holds a queue of tasks to be run on the updater thread,
   // when the updater thread is a WebRender thread, since it won't have a
-  // message loop we can dispatch to.
-  std::deque<RefPtr<Runnable>> mUpdaterQueue;
+  // message loop we can dispatch to. Note that although this is a single queue
+  // it is conceptually separated into multiple ones, one per layers id. Tasks
+  // for a given layers id will always run in FIFO order, but there is no
+  // guaranteed ordering for tasks with different layers ids.
+  std::deque<QueuedTask> mUpdaterQueue;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // mozilla_layers_APZUpdater_h
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -1971,16 +1971,17 @@ APZCTreeManager::UpdateZoomConstraints(c
 {
   if (!GetUpdater()->IsUpdaterThread()) {
     // This can happen if we're in the UI process and got a call directly from
     // nsBaseWidget (as opposed to over PAPZCTreeManager). We want this function
     // to run on the updater thread, so bounce it over.
     MOZ_ASSERT(XRE_IsParentProcess());
 
     GetUpdater()->RunOnUpdaterThread(
+        aGuid.mLayersId,
         NewRunnableMethod<ScrollableLayerGuid, Maybe<ZoomConstraints>>(
             "APZCTreeManager::UpdateZoomConstraints",
             this,
             &APZCTreeManager::UpdateZoomConstraints,
             aGuid,
             aConstraints));
     return;
   }
--- a/gfx/layers/apz/src/APZUpdater.cpp
+++ b/gfx/layers/apz/src/APZUpdater.cpp
@@ -130,21 +130,21 @@ APZUpdater::CompleteSceneSwap(const wr::
 APZUpdater::ProcessPendingTasks(const wr::WrWindowId& aWindowId)
 {
   if (RefPtr<APZUpdater> updater = GetUpdater(aWindowId)) {
     updater->ProcessQueue();
   }
 }
 
 void
-APZUpdater::ClearTree()
+APZUpdater::ClearTree(LayersId aRootLayersId)
 {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   RefPtr<APZUpdater> self = this;
-  RunOnUpdaterThread(NS_NewRunnableFunction(
+  RunOnUpdaterThread(aRootLayersId, NS_NewRunnableFunction(
     "APZUpdater::ClearTree",
     [=]() {
       self->mApz->ClearTree();
 
       // Once ClearTree is called on the APZCTreeManager, we are in a shutdown
       // phase. After this point it's ok if WebRender cannot get a hold of the
       // updater via the window id, and it's a good point to remove the mapping
       // and avoid leaving a dangling pointer to this object.
@@ -157,17 +157,17 @@ APZUpdater::ClearTree()
 }
 
 void
 APZUpdater::UpdateFocusState(LayersId aRootLayerTreeId,
                              LayersId aOriginatingLayersId,
                              const FocusTarget& aFocusTarget)
 {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
-  RunOnUpdaterThread(NewRunnableMethod<LayersId, LayersId, FocusTarget>(
+  RunOnUpdaterThread(aOriginatingLayersId, NewRunnableMethod<LayersId, LayersId, FocusTarget>(
       "APZUpdater::UpdateFocusState",
       mApz,
       &APZCTreeManager::UpdateFocusState,
       aRootLayerTreeId,
       aOriginatingLayersId,
       aFocusTarget));
 }
 
@@ -192,26 +192,26 @@ APZUpdater::UpdateScrollDataAndTreeState
 {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   RefPtr<APZUpdater> self = this;
   // Insert an epoch requirement update into the queue, so that
   // tasks inserted into the queue after this point only get executed
   // once the epoch requirement is satisfied. In particular, the
   // UpdateHitTestingTree call below needs to wait until the epoch requirement
   // is satisfied, which is why it is a separate task in the queue.
-  RunOnUpdaterThread(NS_NewRunnableFunction(
+  RunOnUpdaterThread(aOriginatingLayersId, NS_NewRunnableFunction(
     "APZUpdater::UpdateEpochRequirement",
     [=]() {
       if (aRootLayerTreeId == aOriginatingLayersId) {
         self->mEpochData[aOriginatingLayersId].mIsRoot = true;
       }
       self->mEpochData[aOriginatingLayersId].mRequired = aEpoch;
     }
   ));
-  RunOnUpdaterThread(NS_NewRunnableFunction(
+  RunOnUpdaterThread(aOriginatingLayersId, NS_NewRunnableFunction(
     "APZUpdater::UpdateHitTestingTree",
     [=,aScrollData=Move(aScrollData)]() {
       self->mApz->UpdateFocusState(aRootLayerTreeId,
           aOriginatingLayersId, aScrollData.GetFocusTarget());
 
       self->mScrollData[aOriginatingLayersId] = aScrollData;
       auto root = self->mScrollData.find(aRootLayerTreeId);
       if (root == self->mScrollData.end()) {
@@ -225,30 +225,30 @@ APZUpdater::UpdateScrollDataAndTreeState
   ));
 }
 
 void
 APZUpdater::NotifyLayerTreeAdopted(LayersId aLayersId,
                                    const RefPtr<APZUpdater>& aOldUpdater)
 {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
-  RunOnUpdaterThread(NewRunnableMethod<LayersId, RefPtr<APZCTreeManager>>(
+  RunOnUpdaterThread(aLayersId, NewRunnableMethod<LayersId, RefPtr<APZCTreeManager>>(
       "APZUpdater::NotifyLayerTreeAdopted",
       mApz,
       &APZCTreeManager::NotifyLayerTreeAdopted,
       aLayersId,
       aOldUpdater ? aOldUpdater->mApz : nullptr));
 }
 
 void
 APZUpdater::NotifyLayerTreeRemoved(LayersId aLayersId)
 {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   RefPtr<APZUpdater> self = this;
-  RunOnUpdaterThread(NS_NewRunnableFunction(
+  RunOnUpdaterThread(aLayersId, NS_NewRunnableFunction(
     "APZUpdater::NotifyLayerTreeRemoved",
     [=]() {
       self->mEpochData.erase(aLayersId);
       self->mScrollData.erase(aLayersId);
       self->mApz->NotifyLayerTreeRemoved(aLayersId);
     }
   ));
 }
@@ -257,17 +257,17 @@ bool
 APZUpdater::GetAPZTestData(LayersId aLayersId,
                            APZTestData* aOutData)
 {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
 
   RefPtr<APZCTreeManager> apz = mApz;
   bool ret = false;
   SynchronousTask waiter("APZUpdater::GetAPZTestData");
-  RunOnUpdaterThread(NS_NewRunnableFunction(
+  RunOnUpdaterThread(aLayersId, NS_NewRunnableFunction(
     "APZUpdater::GetAPZTestData",
     [&]() {
       AutoCompleteTask notifier(&waiter);
       ret = apz->GetAPZTestData(aLayersId, aOutData);
     }
   ));
 
   // Wait until the task posted above has run and populated aOutData and ret
@@ -278,17 +278,17 @@ APZUpdater::GetAPZTestData(LayersId aLay
 
 void
 APZUpdater::SetTestAsyncScrollOffset(LayersId aLayersId,
                                      const FrameMetrics::ViewID& aScrollId,
                                      const CSSPoint& aOffset)
 {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   RefPtr<APZCTreeManager> apz = mApz;
-  RunOnUpdaterThread(NS_NewRunnableFunction(
+  RunOnUpdaterThread(aLayersId, NS_NewRunnableFunction(
     "APZUpdater::SetTestAsyncScrollOffset",
     [=]() {
       RefPtr<AsyncPanZoomController> apzc = apz->GetTargetAPZC(aLayersId, aScrollId);
       if (apzc) {
         apzc->SetTestAsyncScrollOffset(aOffset);
       } else {
         NS_WARNING("Unable to find APZC in SetTestAsyncScrollOffset");
       }
@@ -298,17 +298,17 @@ APZUpdater::SetTestAsyncScrollOffset(Lay
 
 void
 APZUpdater::SetTestAsyncZoom(LayersId aLayersId,
                              const FrameMetrics::ViewID& aScrollId,
                              const LayerToParentLayerScale& aZoom)
 {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   RefPtr<APZCTreeManager> apz = mApz;
-  RunOnUpdaterThread(NS_NewRunnableFunction(
+  RunOnUpdaterThread(aLayersId, NS_NewRunnableFunction(
     "APZUpdater::SetTestAsyncZoom",
     [=]() {
       RefPtr<AsyncPanZoomController> apzc = apz->GetTargetAPZC(aLayersId, aScrollId);
       if (apzc) {
         apzc->SetTestAsyncZoom(aZoom);
       } else {
         NS_WARNING("Unable to find APZC in SetTestAsyncZoom");
       }
@@ -328,34 +328,34 @@ void
 APZUpdater::AssertOnUpdaterThread() const
 {
   if (APZThreadUtils::GetThreadAssertionsEnabled()) {
     MOZ_ASSERT(IsUpdaterThread());
   }
 }
 
 void
-APZUpdater::RunOnUpdaterThread(already_AddRefed<Runnable> aTask)
+APZUpdater::RunOnUpdaterThread(LayersId aLayersId, already_AddRefed<Runnable> aTask)
 {
   RefPtr<Runnable> task = aTask;
 
   if (IsUpdaterThread()) {
     task->Run();
     return;
   }
 
   if (UsingWebRenderUpdaterThread()) {
     // If the updater thread is a WebRender thread, and we're not on it
     // right now, save the task in the queue. We will run tasks from the queue
     // during the callback from the updater thread, which we trigger by the
     // call to WakeSceneBuilder.
 
     { // scope lock
       MutexAutoLock lock(mQueueLock);
-      mUpdaterQueue.push_back(task);
+      mUpdaterQueue.push_back(QueuedTask { aLayersId, task });
     }
     RefPtr<wr::WebRenderAPI> api = mApz->GetWebRenderAPI();
     if (api) {
       api->WakeSceneBuilder();
     } else {
       // Not sure if this can happen, but it might be possible. If it does,
       // the task is in the queue, but if we didn't get a WebRenderAPI it
       // might never run, or it might run later if we manage to get a
@@ -379,21 +379,21 @@ APZUpdater::IsUpdaterThread() const
 {
   if (UsingWebRenderUpdaterThread()) {
     return PlatformThread::CurrentId() == *mUpdaterThreadId;
   }
   return CompositorThreadHolder::IsInCompositorThread();
 }
 
 void
-APZUpdater::RunOnControllerThread(already_AddRefed<Runnable> aTask)
+APZUpdater::RunOnControllerThread(LayersId aLayersId, already_AddRefed<Runnable> aTask)
 {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
 
-  RunOnUpdaterThread(NewRunnableFunction(
+  RunOnUpdaterThread(aLayersId, NewRunnableFunction(
       "APZUpdater::RunOnControllerThread",
       &APZThreadUtils::RunOnControllerThread,
       Move(aTask)));
 }
 
 bool
 APZUpdater::UsingWebRenderUpdaterThread() const
 {
@@ -421,65 +421,69 @@ APZUpdater::GetUpdater(const wr::WrWindo
   StaticMutexAutoLock lock(sWindowIdLock);
   auto it = sWindowIdMap.find(wr::AsUint64(aWindowId));
   if (it != sWindowIdMap.end()) {
     updater = it->second;
   }
   return updater.forget();
 }
 
-bool
-APZUpdater::IsQueueBlocked() const
-{
-  AssertOnUpdaterThread();
-
-  for (const auto& i : mEpochData) {
-    if (i.second.IsBlockingQueue()) {
-      return true;
-    }
-  }
-  return false;
-}
-
 void
 APZUpdater::ProcessQueue()
 {
   { // scope lock to check for emptiness
     MutexAutoLock lock(mQueueLock);
     if (mUpdaterQueue.empty()) {
       return;
     }
   }
 
-  // Note that running a task might update mEpochData, so we can't
-  // cache the result of IsQueueBlocked().
-  while (!IsQueueBlocked()) {
-    RefPtr<Runnable> task;
+  std::deque<QueuedTask> blockedTasks;
+  while (true) {
+    QueuedTask task;
 
     { // scope lock to extract a task
       MutexAutoLock lock(mQueueLock);
       if (mUpdaterQueue.empty()) {
+        // If we're done processing mUpdaterQueue, swap the tasks that are
+        // still blocked back in and finish
+        std::swap(mUpdaterQueue, blockedTasks);
         break;
       }
       task = mUpdaterQueue.front();
       mUpdaterQueue.pop_front();
     }
 
-    task->Run();
+    // We check the task to see if it is blocked. Note that while this
+    // ProcessQueue function is executing, a particular layers is cannot go
+    // from blocked to unblocked, because only CompleteSceneSwap can unblock
+    // a layers id, and that also runs on the updater thread. If somehow
+    // a layers id gets unblocked while we're processing the queue, then it
+    // might result in tasks getting executed out of order.
+
+    auto it = mEpochData.find(task.mLayersId);
+    if (it != mEpochData.end() && it->second.IsBlocked()) {
+      // If this task is blocked, put it into the blockedTasks queue that
+      // we will replace mUpdaterQueue with
+      blockedTasks.push_back(task);
+    } else {
+      // Run and discard the task
+      task.mRunnable->Run();
+    }
   }
 }
 
 APZUpdater::EpochState::EpochState()
   : mRequired{0}
   , mIsRoot(false)
 {
 }
 
 bool
-APZUpdater::EpochState::IsBlockingQueue() const
+APZUpdater::EpochState::IsBlocked() const
 {
   // The root is a special case because we basically assume it is "visible"
   // even before it is built for the first time. This is because building the
   // scene automatically makes it visible, and we need to make sure the APZ
   // scroll data gets applied atomically with that happening.
   //
   // Layer subtrees on the other hand do not automatically become visible upon
   // being built, because there must be a another layer tree update to change
--- a/gfx/layers/ipc/APZCTreeManagerParent.cpp
+++ b/gfx/layers/ipc/APZCTreeManagerParent.cpp
@@ -38,17 +38,17 @@ APZCTreeManagerParent::ChildAdopted(RefP
   MOZ_ASSERT(aAPZUpdater->HasTreeManager(aAPZCTreeManager));
   mTreeManager = Move(aAPZCTreeManager);
   mUpdater = Move(aAPZUpdater);
 }
 
 mozilla::ipc::IPCResult
 APZCTreeManagerParent::RecvSetKeyboardMap(const KeyboardMap& aKeyboardMap)
 {
-  mUpdater->RunOnControllerThread(NewRunnableMethod<KeyboardMap>(
+  mUpdater->RunOnControllerThread(mLayersId, NewRunnableMethod<KeyboardMap>(
     "layers::IAPZCTreeManager::SetKeyboardMap",
     mTreeManager,
     &IAPZCTreeManager::SetKeyboardMap,
     aKeyboardMap));
 
   return IPC_OK();
 }
 
@@ -60,30 +60,31 @@ APZCTreeManagerParent::RecvZoomToRect(
 {
   if (aGuid.mLayersId != mLayersId) {
     // Guard against bad data from hijacked child processes
     NS_ERROR("Unexpected layers id in RecvZoomToRect; dropping message...");
     return IPC_FAIL_NO_REASON(this);
   }
 
   mUpdater->RunOnControllerThread(
+    mLayersId,
     NewRunnableMethod<ScrollableLayerGuid, CSSRect, uint32_t>(
       "layers::IAPZCTreeManager::ZoomToRect",
       mTreeManager,
       &IAPZCTreeManager::ZoomToRect,
       aGuid, aRect, aFlags));
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 APZCTreeManagerParent::RecvContentReceivedInputBlock(
     const uint64_t& aInputBlockId,
     const bool& aPreventDefault)
 {
-  mUpdater->RunOnControllerThread(NewRunnableMethod<uint64_t, bool>(
+  mUpdater->RunOnControllerThread(mLayersId, NewRunnableMethod<uint64_t, bool>(
     "layers::IAPZCTreeManager::ContentReceivedInputBlock",
     mTreeManager,
     &IAPZCTreeManager::ContentReceivedInputBlock,
     aInputBlockId,
     aPreventDefault));
 
   return IPC_OK();
 }
@@ -96,16 +97,17 @@ APZCTreeManagerParent::RecvSetTargetAPZC
   for (size_t i = 0; i < aTargets.Length(); i++) {
     if (aTargets[i].mLayersId != mLayersId) {
       // Guard against bad data from hijacked child processes
       NS_ERROR("Unexpected layers id in RecvSetTargetAPZC; dropping message...");
       return IPC_FAIL_NO_REASON(this);
     }
   }
   mUpdater->RunOnControllerThread(
+    mLayersId,
     NewRunnableMethod<uint64_t,
                       StoreCopyPassByRRef<nsTArray<ScrollableLayerGuid>>>(
       "layers::IAPZCTreeManager::SetTargetAPZC",
       mTreeManager,
       &IAPZCTreeManager::SetTargetAPZC,
       aInputBlockId,
       aTargets));
 
@@ -125,30 +127,31 @@ APZCTreeManagerParent::RecvUpdateZoomCon
 
   mTreeManager->UpdateZoomConstraints(aGuid, aConstraints);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 APZCTreeManagerParent::RecvSetDPI(const float& aDpiValue)
 {
-  mUpdater->RunOnControllerThread(NewRunnableMethod<float>(
+  mUpdater->RunOnControllerThread(mLayersId, NewRunnableMethod<float>(
     "layers::IAPZCTreeManager::SetDPI",
     mTreeManager,
     &IAPZCTreeManager::SetDPI,
     aDpiValue));
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 APZCTreeManagerParent::RecvSetAllowedTouchBehavior(
     const uint64_t& aInputBlockId,
     nsTArray<TouchBehaviorFlags>&& aValues)
 {
   mUpdater->RunOnControllerThread(
+    mLayersId,
     NewRunnableMethod<uint64_t,
                       StoreCopyPassByRRef<nsTArray<TouchBehaviorFlags>>>(
       "layers::IAPZCTreeManager::SetAllowedTouchBehavior",
       mTreeManager,
       &IAPZCTreeManager::SetAllowedTouchBehavior,
       aInputBlockId,
       Move(aValues)));
 
@@ -162,16 +165,17 @@ APZCTreeManagerParent::RecvStartScrollba
 {
   if (aGuid.mLayersId != mLayersId) {
     // Guard against bad data from hijacked child processes
     NS_ERROR("Unexpected layers id in RecvStartScrollbarDrag; dropping message...");
     return IPC_FAIL_NO_REASON(this);
   }
 
   mUpdater->RunOnControllerThread(
+    mLayersId,
     NewRunnableMethod<ScrollableLayerGuid, AsyncDragMetrics>(
       "layers::IAPZCTreeManager::StartScrollbarDrag",
       mTreeManager,
       &IAPZCTreeManager::StartScrollbarDrag,
       aGuid,
       aDragMetrics));
 
   return IPC_OK();
@@ -185,44 +189,47 @@ APZCTreeManagerParent::RecvStartAutoscro
   // Unlike RecvStartScrollbarDrag(), this message comes from the parent
   // process (via nsBaseWidget::mAPZC) rather than from the child process
   // (via TabChild::mApzcTreeManager), so there is no need to check the
   // layers id against mLayersId (and in any case, it wouldn't match, because
   // mLayersId stores the parent process's layers id, while nsBaseWidget is
   // sending the child process's layers id).
 
   mUpdater->RunOnControllerThread(
+      mLayersId,
       NewRunnableMethod<ScrollableLayerGuid, ScreenPoint>(
         "layers::IAPZCTreeManager::StartAutoscroll",
         mTreeManager,
         &IAPZCTreeManager::StartAutoscroll,
         aGuid, aAnchorLocation));
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 APZCTreeManagerParent::RecvStopAutoscroll(const ScrollableLayerGuid& aGuid)
 {
   // See RecvStartAutoscroll() for why we don't check the layers id.
 
   mUpdater->RunOnControllerThread(
+      mLayersId,
       NewRunnableMethod<ScrollableLayerGuid>(
         "layers::IAPZCTreeManager::StopAutoscroll",
         mTreeManager,
         &IAPZCTreeManager::StopAutoscroll,
         aGuid));
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 APZCTreeManagerParent::RecvSetLongTapEnabled(const bool& aLongTapEnabled)
 {
   mUpdater->RunOnControllerThread(
+      mLayersId,
       NewRunnableMethod<bool>(
         "layers::IAPZCTreeManager::SetLongTapEnabled",
         mTreeManager,
         &IAPZCTreeManager::SetLongTapEnabled,
         aLongTapEnabled));
 
   return IPC_OK();
 }
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -635,17 +635,17 @@ CompositorBridgeParent::ActorDestroy(Act
   RemoveCompositor(mCompositorBridgeID);
 
   mCompositionManager = nullptr;
 
   MOZ_ASSERT((mApzSampler != nullptr) == (mApzcTreeManager != nullptr));
   MOZ_ASSERT((mApzUpdater != nullptr) == (mApzcTreeManager != nullptr));
   if (mApzUpdater) {
     mApzSampler = nullptr;
-    mApzUpdater->ClearTree();
+    mApzUpdater->ClearTree(mRootLayerTreeID);
     mApzUpdater = nullptr;
     mApzcTreeManager = nullptr;
   }
 
   { // scope lock
     MonitorAutoLock lock(*sIndirectLayerTreesLock);
     sIndirectLayerTrees.erase(mRootLayerTreeID);
   }
--- a/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
@@ -128,19 +128,20 @@ CrossProcessCompositorBridgeParent::Allo
   CompositorBridgeParent::LayerTreeState& state = sIndirectLayerTrees[aLayersId];
 
   // If the widget has shutdown its compositor, we may not have had a chance yet
   // to unmap our layers id, and we could get here without a parent compositor.
   // In this case return an empty APZCTM.
   if (!state.mParent) {
     // Note: we immediately call ClearTree since otherwise the APZCTM will
     // retain a reference to itself, through the checkerboard observer.
-    RefPtr<APZCTreeManager> temp = new APZCTreeManager(LayersId{0});
+    LayersId dummyId{0};
+    RefPtr<APZCTreeManager> temp = new APZCTreeManager(dummyId);
     RefPtr<APZUpdater> tempUpdater = new APZUpdater(temp);
-    tempUpdater->ClearTree();
+    tempUpdater->ClearTree(dummyId);
     return new APZCTreeManagerParent(aLayersId, temp, tempUpdater);
   }
 
   state.mParent->AllocateAPZCTreeManagerParent(lock, aLayersId, state);
   return state.mApzcTreeManagerParent;
 }
 bool
 CrossProcessCompositorBridgeParent::DeallocPAPZCTreeManagerParent(PAPZCTreeManagerParent* aActor)