Bug 1447299 - Move the sampler thread util functions from APZThreadUtils to APZSampler. r=botond
authorKartikaya Gupta <kgupta@mozilla.com>
Wed, 28 Mar 2018 14:56:41 -0400
changeset 410510 e76ec9b09021bb1e40a0fcb1841e7821b04a1034
parent 410509 4b4f127d99ca6b6af90b0816efd724b830667051
child 410511 2c7c644cdc1c05d181f59453528ae8316bfc6030
push id33730
push userdluca@mozilla.com
push dateThu, 29 Mar 2018 09:49:41 +0000
treeherdermozilla-central@f9755fa986d2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbotond
bugs1447299
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 1447299 - Move the sampler thread util functions from APZThreadUtils to APZSampler. r=botond Note that this also makes the utility functions instance methods, because each APZSampler might have a different sampler thread instance. MozReview-Commit-ID: 9dY8ZzVX6lR
gfx/layers/apz/public/APZSampler.h
gfx/layers/apz/src/APZCTreeManager.cpp
gfx/layers/apz/src/APZCTreeManager.h
gfx/layers/apz/src/APZSampler.cpp
gfx/layers/apz/src/AsyncPanZoomController.cpp
gfx/layers/apz/src/AsyncPanZoomController.h
gfx/layers/apz/src/FocusState.cpp
gfx/layers/apz/src/HitTestingTreeNode.cpp
gfx/layers/apz/test/gtest/APZCBasicTester.h
gfx/layers/apz/test/gtest/APZCTreeManagerTester.h
gfx/layers/apz/util/APZThreadUtils.cpp
gfx/layers/apz/util/APZThreadUtils.h
--- a/gfx/layers/apz/public/APZSampler.h
+++ b/gfx/layers/apz/public/APZSampler.h
@@ -99,16 +99,35 @@ public:
   ParentLayerPoint GetCurrentAsyncScrollOffset(const LayerMetricsWrapper& aLayer);
   AsyncTransform GetCurrentAsyncTransform(const LayerMetricsWrapper& aLayer);
   AsyncTransformComponentMatrix GetOverscrollTransform(const LayerMetricsWrapper& aLayer);
   AsyncTransformComponentMatrix GetCurrentAsyncTransformWithOverscroll(const LayerMetricsWrapper& aLayer);
 
   void MarkAsyncTransformAppliedToContent(const LayerMetricsWrapper& aLayer);
   bool HasUnusedAsyncTransform(const LayerMetricsWrapper& aLayer);
 
+  /**
+   * This can be used to assert that the current thread is the
+   * sampler thread (which samples the async transform).
+   * This does nothing if thread assertions are disabled.
+   */
+  void AssertOnSamplerThread();
+
+  /**
+   * Runs the given task on the APZ "sampler thread" for this APZSampler. If
+   * this function is called from the sampler thread itself then the task is
+   * run immediately without getting queued.
+   */
+  void RunOnSamplerThread(already_AddRefed<Runnable> aTask);
+
+  /**
+   * Returns true if currently on the APZSampler's "sampler thread".
+   */
+  bool IsSamplerThread();
+
 protected:
   virtual ~APZSampler();
 
 private:
   RefPtr<APZCTreeManager> mApz;
 };
 
 } // namespace layers
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -255,17 +255,17 @@ APZCTreeManager::SetSampler(APZSampler* 
   MOZ_ASSERT((mSampler == nullptr) != (aSampler == nullptr));
   mSampler = aSampler;
 }
 
 void
 APZCTreeManager::NotifyLayerTreeAdopted(LayersId aLayersId,
                                         const RefPtr<APZCTreeManager>& aOldApzcTreeManager)
 {
-  APZThreadUtils::AssertOnSamplerThread();
+  AssertOnSamplerThread();
 
   if (aOldApzcTreeManager) {
     aOldApzcTreeManager->mFocusState.RemoveFocusTarget(aLayersId);
     // While we could move the focus target information from the old APZC tree
     // manager into this one, it's safer to not do that, as we'll probably have
     // that information repopulated soon anyway (on the next layers update).
   }
 
@@ -282,17 +282,17 @@ APZCTreeManager::NotifyLayerTreeAdopted(
     MutexAutoLock lock(mTestDataLock);
     mTestData[aLayersId] = Move(adoptedData);
   }
 }
 
 void
 APZCTreeManager::NotifyLayerTreeRemoved(LayersId aLayersId)
 {
-  APZThreadUtils::AssertOnSamplerThread();
+  AssertOnSamplerThread();
 
   mFocusState.RemoveFocusTarget(aLayersId);
 
   { // scope lock
     MutexAutoLock lock(mTestDataLock);
     mTestData.erase(aLayersId);
   }
 }
@@ -477,17 +477,17 @@ APZCTreeManager::UpdateHitTestingTreeImp
 #endif
 }
 
 void
 APZCTreeManager::UpdateFocusState(LayersId aRootLayerTreeId,
                                   LayersId aOriginatingLayersId,
                                   const FocusTarget& aFocusTarget)
 {
-  APZThreadUtils::AssertOnSamplerThread();
+  AssertOnSamplerThread();
 
   if (!gfxPrefs::APZKeyboardEnabled()) {
     return;
   }
 
   mFocusState.Update(aRootLayerTreeId,
                      aOriginatingLayersId,
                      aFocusTarget);
@@ -495,43 +495,43 @@ APZCTreeManager::UpdateFocusState(Layers
 
 void
 APZCTreeManager::UpdateHitTestingTree(LayersId aRootLayerTreeId,
                                       Layer* aRoot,
                                       bool aIsFirstPaint,
                                       LayersId aOriginatingLayersId,
                                       uint32_t aPaintSequenceNumber)
 {
-  APZThreadUtils::AssertOnSamplerThread();
+  AssertOnSamplerThread();
 
   LayerMetricsWrapper root(aRoot);
   UpdateHitTestingTreeImpl(aRootLayerTreeId, root, aIsFirstPaint,
                            aOriginatingLayersId, aPaintSequenceNumber);
 }
 
 void
 APZCTreeManager::UpdateHitTestingTree(LayersId aRootLayerTreeId,
                                       const WebRenderScrollData& aScrollData,
                                       bool aIsFirstPaint,
                                       LayersId aOriginatingLayersId,
                                       uint32_t aPaintSequenceNumber)
 {
-  APZThreadUtils::AssertOnSamplerThread();
+  AssertOnSamplerThread();
 
   WebRenderScrollDataWrapper wrapper(&aScrollData);
   UpdateHitTestingTreeImpl(aRootLayerTreeId, wrapper, aIsFirstPaint,
                            aOriginatingLayersId, aPaintSequenceNumber);
 }
 
 bool
 APZCTreeManager::PushStateToWR(wr::TransactionBuilder& aTxn,
                                const TimeStamp& aSampleTime,
                                nsTArray<wr::WrTransformProperty>& aTransformArray)
 {
-  APZThreadUtils::AssertOnSamplerThread();
+  AssertOnSamplerThread();
 
   RecursiveMutexAutoLock lock(mTreeLock);
 
   // During the first pass through the tree, we build a cache of guid->HTTN so
   // that we can find the relevant APZC instances quickly in subsequent passes,
   // such as the one below to generate scrollbar transforms. Without this, perf
   // could end up being O(n^2) instead of O(n log n) because we'd have to search
   // the tree to find the corresponding APZC every time we hit a thumb node.
@@ -1955,33 +1955,33 @@ APZCTreeManager::SetTargetAPZC(uint64_t 
   RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aTarget);
   mInputQueue->SetConfirmedTargetApzc(aInputBlockId, apzc);
 }
 
 void
 APZCTreeManager::UpdateZoomConstraints(const ScrollableLayerGuid& aGuid,
                                        const Maybe<ZoomConstraints>& aConstraints)
 {
-  if (!APZThreadUtils::IsSamplerThread()) {
+  if (!GetSampler()->IsSamplerThread()) {
     // 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 sampler thread, so bounce it over.
     MOZ_ASSERT(XRE_IsParentProcess());
 
-    APZThreadUtils::RunOnSamplerThread(
+    GetSampler()->RunOnSamplerThread(
         NewRunnableMethod<ScrollableLayerGuid, Maybe<ZoomConstraints>>(
             "APZCTreeManager::UpdateZoomConstraints",
             this,
             &APZCTreeManager::UpdateZoomConstraints,
             aGuid,
             aConstraints));
     return;
   }
 
-  APZThreadUtils::AssertOnSamplerThread();
+  AssertOnSamplerThread();
 
   RecursiveMutexAutoLock lock(mTreeLock);
   RefPtr<HitTestingTreeNode> node = GetTargetNode(aGuid, nullptr);
   MOZ_ASSERT(!node || node->GetApzc()); // any node returned must have an APZC
 
   // Propagate the zoom constraints down to the subtree, stopping at APZCs
   // which have their own zoom constraints or are in a different layers id.
   if (aConstraints) {
@@ -2066,17 +2066,17 @@ APZCTreeManager::AdjustScrollForSurfaceS
   if (apzc) {
     apzc->AdjustScrollForSurfaceShift(aShift);
   }
 }
 
 void
 APZCTreeManager::ClearTree()
 {
-  APZThreadUtils::AssertOnSamplerThread();
+  AssertOnSamplerThread();
 
 #if defined(MOZ_WIDGET_ANDROID)
   mToolbarAnimator->ClearTreeManager();
 #endif
 
   // Ensure that no references to APZCs are alive in any lingering input
   // blocks. This breaks cycles from InputBlockState::mTargetApzc back to
   // the InputQueue.
@@ -3022,17 +3022,17 @@ APZCTreeManager::GetContentController(La
     });
   return controller.forget();
 }
 
 bool
 APZCTreeManager::GetAPZTestData(LayersId aLayersId,
                                 APZTestData* aOutData)
 {
-  APZThreadUtils::AssertOnSamplerThread();
+  AssertOnSamplerThread();
   MutexAutoLock lock(mTestDataLock);
   auto it = mTestData.find(aLayersId);
   if (it == mTestData.end()) {
     return false;
   }
   *aOutData = *(it->second);
   return true;
 }
@@ -3194,16 +3194,31 @@ APZCTreeManager::ComputeTransformForScro
       *aOutClipTransform = asyncCompensation;
     }
   }
   transform = transform * compensation;
 
   return transform;
 }
 
+APZSampler*
+APZCTreeManager::GetSampler() const
+{
+  // We should always have a sampler here, since in practice the sampler
+  // is destroyed at the same time that this APZCTreeMAnager instance is.
+  MOZ_ASSERT(mSampler);
+  return mSampler;
+}
+
+void
+APZCTreeManager::AssertOnSamplerThread()
+{
+  GetSampler()->AssertOnSamplerThread();
+}
+
 void
 APZCTreeManager::SetDPI(float aDpiValue)
 {
   APZThreadUtils::AssertOnControllerThread();
   mDPI = aDpiValue;
 }
 
 float
--- a/gfx/layers/apz/src/APZCTreeManager.h
+++ b/gfx/layers/apz/src/APZCTreeManager.h
@@ -521,20 +521,25 @@ public:
       const LayerToParentLayerMatrix4x4& aCurrentTransform,
       const gfx::Matrix4x4& aScrollableContentTransform,
       AsyncPanZoomController* aApzc,
       const FrameMetrics& aMetrics,
       const ScrollThumbData& aThumbData,
       bool aScrollbarIsDescendant,
       AsyncTransformComponentMatrix* aOutClipTransform);
 
+  // Assert that the current thread is the sampler thread for this APZCTM.
+  void AssertOnSamplerThread();
+
 protected:
   // Protected destructor, to discourage deletion outside of Release():
   virtual ~APZCTreeManager();
 
+  APZSampler* GetSampler() const;
+
   // Protected hooks for gtests subclass
   virtual AsyncPanZoomController* NewAPZCInstance(LayersId aLayersId,
                                                   GeckoContentController* aController);
 public:
   // Public hooks for gtests subclass
   virtual TimeStamp GetFrameTime();
 
 public:
--- a/gfx/layers/apz/src/APZSampler.cpp
+++ b/gfx/layers/apz/src/APZSampler.cpp
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/layers/APZSampler.h"
 
 #include "APZCTreeManager.h"
 #include "AsyncPanZoomController.h"
+#include "mozilla/layers/APZThreadUtils.h"
 #include "mozilla/layers/CompositorThread.h"
 #include "mozilla/layers/LayerMetricsWrapper.h"
 #include "TreeTraversal.h"
 
 namespace mozilla {
 namespace layers {
 
 APZSampler::APZSampler(const RefPtr<APZCTreeManager>& aApz)
@@ -208,10 +209,43 @@ bool
 APZSampler::HasUnusedAsyncTransform(const LayerMetricsWrapper& aLayer)
 {
   AsyncPanZoomController* apzc = aLayer.GetApzc();
   return apzc
       && !apzc->GetAsyncTransformAppliedToContent()
       && !AsyncTransformComponentMatrix(apzc->GetCurrentAsyncTransform(AsyncPanZoomController::eForCompositing)).IsIdentity();
 }
 
+void
+APZSampler::AssertOnSamplerThread()
+{
+  if (APZThreadUtils::GetThreadAssertionsEnabled()) {
+    MOZ_ASSERT(IsSamplerThread());
+  }
+}
+
+void
+APZSampler::RunOnSamplerThread(already_AddRefed<Runnable> aTask)
+{
+  RefPtr<Runnable> task = aTask;
+
+  MessageLoop* loop = CompositorThreadHolder::Loop();
+  if (!loop) {
+    // Could happen during startup
+    NS_WARNING("Dropping task posted to sampler thread");
+    return;
+  }
+
+  if (IsSamplerThread()) {
+    task->Run();
+  } else {
+    loop->PostTask(task.forget());
+  }
+}
+
+bool
+APZSampler::IsSamplerThread()
+{
+  return CompositorThreadHolder::IsInCompositorThread();
+}
+
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -843,17 +843,17 @@ AsyncPanZoomController::GetGestureEventL
 const RefPtr<InputQueue>&
 AsyncPanZoomController::GetInputQueue() const {
   return mInputQueue;
 }
 
 void
 AsyncPanZoomController::Destroy()
 {
-  APZThreadUtils::AssertOnSamplerThread();
+  AssertOnSamplerThread();
 
   CancelAnimation(CancelAnimationFlags::ScrollSnap);
 
   { // scope the lock
     MonitorAutoLock lock(mRefPtrMonitor);
     mGeckoContentController = nullptr;
     mGestureEventListener = nullptr;
   }
@@ -3476,17 +3476,17 @@ AsyncPanZoomController::RequestContentRe
   controller->RequestContentRepaint(aFrameMetrics);
   mExpectedGeckoMetrics = aFrameMetrics;
   mLastPaintRequestMetrics = aFrameMetrics;
 }
 
 bool AsyncPanZoomController::UpdateAnimation(const TimeStamp& aSampleTime,
                                              nsTArray<RefPtr<Runnable>>* aOutDeferredTasks)
 {
-  APZThreadUtils::AssertOnSamplerThread();
+  AssertOnSamplerThread();
 
   // This function may get called multiple with the same sample time, because
   // there may be multiple layers with this APZC, and each layer invokes this
   // function during composition. However we only want to do one animation step
   // per composition so we need to deduplicate these calls first.
   if (mLastSampleTime == aSampleTime) {
     return false;
   }
@@ -3535,17 +3535,17 @@ AsyncPanZoomController::GetOverscrollTra
   // The overscroll effect is a simple translation by the overscroll offset.
   ParentLayerPoint overscrollOffset(-mX.GetOverscroll(), -mY.GetOverscroll());
   return AsyncTransformComponentMatrix()
       .PostTranslate(overscrollOffset.x, overscrollOffset.y, 0);
 }
 
 bool AsyncPanZoomController::AdvanceAnimations(const TimeStamp& aSampleTime)
 {
-  APZThreadUtils::AssertOnSamplerThread();
+  AssertOnSamplerThread();
 
   // Don't send any state-change notifications until the end of the function,
   // because we may go through some intermediate states while we finish
   // animations and start new ones.
   StateChangeNotificationBlocker blocker(this);
 
   // The eventual return value of this function. The compositor needs to know
   // whether or not to advance by a frame as soon as it can. For example, if a
@@ -3814,17 +3814,17 @@ bool AsyncPanZoomController::IsCurrently
     this, Stringify(painted).c_str(), Stringify(visible).c_str());
   return true;
 }
 
 void AsyncPanZoomController::NotifyLayersUpdated(const ScrollMetadata& aScrollMetadata,
                                                  bool aIsFirstPaint,
                                                  bool aThisLayerTreeUpdated)
 {
-  APZThreadUtils::AssertOnSamplerThread();
+  AssertOnSamplerThread();
 
   RecursiveMutexAutoLock lock(mRecursiveMutex);
   bool isDefault = mScrollMetadata.IsDefault();
 
   const FrameMetrics& aLayerMetrics = aScrollMetadata.GetMetrics();
 
   if ((aScrollMetadata == mLastContentPaintMetadata) && !isDefault) {
     // No new information here, skip it.
@@ -4078,16 +4078,24 @@ const FrameMetrics& AsyncPanZoomControll
   return mFrameMetrics;
 }
 
 const ScrollMetadata& AsyncPanZoomController::GetScrollMetadata() const {
   mRecursiveMutex.AssertCurrentThreadIn();
   return mScrollMetadata;
 }
 
+void
+AsyncPanZoomController::AssertOnSamplerThread() const
+{
+  if (APZCTreeManager* treeManagerLocal = GetApzcTreeManager()) {
+    treeManagerLocal->AssertOnSamplerThread();
+  }
+}
+
 APZCTreeManager* AsyncPanZoomController::GetApzcTreeManager() const {
   mRecursiveMutex.AssertNotCurrentThreadIn();
   return mTreeManager;
 }
 
 void AsyncPanZoomController::ZoomToRect(CSSRect aRect, const uint32_t aFlags) {
   if (!aRect.IsFinite()) {
     NS_WARNING("ZoomToRect got called with a non-finite rect; ignoring...");
@@ -4432,17 +4440,17 @@ void AsyncPanZoomController::UpdateShare
     mSharedLock->Lock();
     *frame = mFrameMetrics;
     mSharedLock->Unlock();
   }
 }
 
 void AsyncPanZoomController::ShareCompositorFrameMetrics()
 {
-  APZThreadUtils::AssertOnSamplerThread();
+  AssertOnSamplerThread();
 
   // Only create the shared memory buffer if it hasn't already been created,
   // we are using progressive tile painting, and we have a
   // controller to pass the shared memory back to the content process/thread.
   if (!mSharedFrameMetricsBuffer && mMetricsSharingController && gfxPrefs::ProgressivePaint()) {
 
     // Create shared memory and initialize it with the current FrameMetrics value
     mSharedFrameMetricsBuffer = new ipc::SharedMemoryBasic;
--- a/gfx/layers/apz/src/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -748,16 +748,18 @@ protected:
 
   /**
    * Gets the pointer to the apzc tree manager. All the access to tree manager
    * should be made via this method and not via private variable since this method
    * ensures that no lock is set.
    */
   APZCTreeManager* GetApzcTreeManager() const;
 
+  void AssertOnSamplerThread() const;
+
   /**
    * Convert ScreenPoint relative to the screen to LayoutDevicePoint relative
    * to the parent document. This excludes the transient compositor transform.
    * NOTE: This must be converted to LayoutDevicePoint relative to the child
    * document before sending over IPC to a child process.
    */
   bool ConvertToGecko(const ScreenIntPoint& aPoint, LayoutDevicePoint* aOut);
 
--- a/gfx/layers/apz/src/FocusState.cpp
+++ b/gfx/layers/apz/src/FocusState.cpp
@@ -65,17 +65,19 @@ FocusState::ReceiveFocusChangingEvent()
          mLastAPZProcessedEvent);
 }
 
 void
 FocusState::Update(LayersId aRootLayerTreeId,
                    LayersId aOriginatingLayersId,
                    const FocusTarget& aState)
 {
-  APZThreadUtils::AssertOnSamplerThread();
+  // This runs on the sampler thread, it's not worth passing around extra raw
+  // pointers just to assert it.
+
   MutexAutoLock lock(mMutex);
 
   FS_LOG("Update with rlt=%" PRIu64 ", olt=%" PRIu64 ", ft=(%s, %" PRIu64 ")\n",
          aRootLayerTreeId,
          aOriginatingLayersId,
          aState.Type(),
          aState.mSequenceNumber);
   mReceivedUpdate = true;
@@ -177,17 +179,18 @@ FocusState::Update(LayersId aRootLayerTr
       return;
     }
   }
 }
 
 void
 FocusState::RemoveFocusTarget(LayersId aLayersId)
 {
-  APZThreadUtils::AssertOnSamplerThread();
+  // This runs on the sampler thread, it's not worth passing around extra raw
+  // pointers just to assert it.
   MutexAutoLock lock(mMutex);
 
   mFocusTree.erase(aLayersId);
 }
 
 Maybe<ScrollableLayerGuid>
 FocusState::GetHorizontalTarget() const
 {
--- a/gfx/layers/apz/src/HitTestingTreeNode.cpp
+++ b/gfx/layers/apz/src/HitTestingTreeNode.cpp
@@ -5,17 +5,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "HitTestingTreeNode.h"
 
 #include "AsyncPanZoomController.h"                     // for AsyncPanZoomController
 #include "gfxPrefs.h"
 #include "LayersLogging.h"                              // for Stringify
 #include "mozilla/gfx/Point.h"                          // for Point4D
-#include "mozilla/layers/APZThreadUtils.h"              // for AssertOnSamplerThread
 #include "mozilla/layers/APZUtils.h"                    // for CompleteAsyncTransform
 #include "mozilla/layers/AsyncCompositionManager.h"     // for ViewTransform::operator Matrix4x4()
 #include "mozilla/layers/AsyncDragMetrics.h"            // for AsyncDragMetrics
 #include "nsPrintfCString.h"                            // for nsPrintfCString
 #include "UnitTransforms.h"                             // for ViewAs
 
 namespace mozilla {
 namespace layers {
@@ -53,17 +52,18 @@ HitTestingTreeNode::RecycleWith(AsyncPan
   // fields.
 }
 
 HitTestingTreeNode::~HitTestingTreeNode() = default;
 
 void
 HitTestingTreeNode::Destroy()
 {
-  APZThreadUtils::AssertOnSamplerThread();
+  // This runs on the sampler thread, it's not worth passing around extra raw
+  // pointers just to assert it.
 
   mPrevSibling = nullptr;
   mLastChild = nullptr;
   mParent = nullptr;
 
   if (mApzc) {
     if (mIsPrimaryApzcHolder) {
       mApzc->Destroy();
--- a/gfx/layers/apz/test/gtest/APZCBasicTester.h
+++ b/gfx/layers/apz/test/gtest/APZCBasicTester.h
@@ -8,32 +8,34 @@
 #define mozilla_layers_APZCBasicTester_h
 
 /**
  * Defines a test fixture used for testing a single APZC.
  */
 
 #include "APZTestCommon.h"
 #include "gfxPrefs.h"
+#include "mozilla/layers/APZSampler.h"
 
 class APZCBasicTester : public APZCTesterBase {
 public:
   explicit APZCBasicTester(AsyncPanZoomController::GestureBehavior aGestureBehavior = AsyncPanZoomController::DEFAULT_GESTURES)
     : mGestureBehavior(aGestureBehavior)
   {
   }
 
 protected:
   virtual void SetUp()
   {
     gfxPrefs::GetSingleton();
     APZThreadUtils::SetThreadAssertionsEnabled(false);
     APZThreadUtils::SetControllerThread(MessageLoop::current());
 
     tm = new TestAPZCTreeManager(mcc);
+    sampler = new APZSampler(tm);
     apzc = new TestAsyncPanZoomController(LayersId{0}, mcc, tm, mGestureBehavior);
     apzc->SetFrameMetrics(TestFrameMetrics());
     apzc->GetScrollMetadata().SetIsLayersIdRoot(true);
   }
 
   /**
    * Get the APZC's scroll range in CSS pixels.
    */
@@ -110,12 +112,13 @@ protected:
     EXPECT_TRUE(recoveredFromOverscroll);
     apzc->AssertStateIsReset();
   }
 
   void TestOverscroll();
 
   AsyncPanZoomController::GestureBehavior mGestureBehavior;
   RefPtr<TestAPZCTreeManager> tm;
+  RefPtr<APZSampler> sampler;
   RefPtr<TestAsyncPanZoomController> apzc;
 };
 
 #endif // mozilla_layers_APZCBasicTester_h
--- a/gfx/layers/apz/test/gtest/APZCTreeManagerTester.h
+++ b/gfx/layers/apz/test/gtest/APZCTreeManagerTester.h
@@ -10,26 +10,28 @@
 /**
  * Defines a test fixture used for testing multiple APZCs interacting in
  * an APZCTreeManager.
  */
 
 #include "APZTestCommon.h"
 #include "gfxPlatform.h"
 #include "gfxPrefs.h"
+#include "mozilla/layers/APZSampler.h"
 
 class APZCTreeManagerTester : public APZCTesterBase {
 protected:
   virtual void SetUp() {
     gfxPrefs::GetSingleton();
     gfxPlatform::GetPlatform();
     APZThreadUtils::SetThreadAssertionsEnabled(false);
     APZThreadUtils::SetControllerThread(MessageLoop::current());
 
     manager = new TestAPZCTreeManager(mcc);
+    sampler = new APZSampler(manager);
   }
 
   virtual void TearDown() {
     while (mcc->RunThroughDelayedTasks());
     manager->ClearTree();
     manager->ClearContentController();
   }
 
@@ -49,16 +51,17 @@ protected:
     }
   }
 
   nsTArray<RefPtr<Layer> > layers;
   RefPtr<LayerManager> lm;
   RefPtr<Layer> root;
 
   RefPtr<TestAPZCTreeManager> manager;
+  RefPtr<APZSampler> sampler;
 
 protected:
   static ScrollMetadata BuildScrollMetadata(FrameMetrics::ViewID aScrollId,
                                             const CSSRect& aScrollableRect,
                                             const ParentLayerRect& aCompositionBounds)
   {
     ScrollMetadata metadata;
     FrameMetrics& metrics = metadata.GetMetrics();
--- a/gfx/layers/apz/util/APZThreadUtils.cpp
+++ b/gfx/layers/apz/util/APZThreadUtils.cpp
@@ -1,18 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/layers/APZThreadUtils.h"
 
-#include "mozilla/layers/CompositorThread.h"
-
 namespace mozilla {
 namespace layers {
 
 static bool sThreadAssertionsEnabled = true;
 static MessageLoop* sControllerThread;
 
 /*static*/ void
 APZThreadUtils::SetThreadAssertionsEnabled(bool aEnabled) {
@@ -61,45 +59,12 @@ APZThreadUtils::RunOnControllerThread(al
 }
 
 /*static*/ bool
 APZThreadUtils::IsControllerThread()
 {
   return sControllerThread == MessageLoop::current();
 }
 
-/*static*/ void
-APZThreadUtils::AssertOnSamplerThread()
-{
-  if (GetThreadAssertionsEnabled()) {
-    MOZ_ASSERT(IsSamplerThread());
-  }
-}
-
-/*static*/ void
-APZThreadUtils::RunOnSamplerThread(already_AddRefed<Runnable> aTask)
-{
-  RefPtr<Runnable> task = aTask;
-
-  MessageLoop* loop = CompositorThreadHolder::Loop();
-  if (!loop) {
-    // Could happen during startup
-    NS_WARNING("Dropping task posted to sampler thread");
-    return;
-  }
-
-  if (IsSamplerThread()) {
-    task->Run();
-  } else {
-    loop->PostTask(task.forget());
-  }
-}
-
-/*static*/ bool
-APZThreadUtils::IsSamplerThread()
-{
-  return CompositorThreadHolder::IsInCompositorThread();
-}
-
 NS_IMPL_ISUPPORTS(GenericNamedTimerCallbackBase, nsITimerCallback, nsINamed)
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/apz/util/APZThreadUtils.h
+++ b/gfx/layers/apz/util/APZThreadUtils.h
@@ -46,35 +46,16 @@ public:
    * run immediately without getting queued.
    */
   static void RunOnControllerThread(already_AddRefed<Runnable> aTask);
 
   /**
    * Returns true if currently on APZ "controller thread".
    */
   static bool IsControllerThread();
-
-  /**
-   * This can be used to assert that the current thread is the
-   * sampler thread (which samples the async transform).
-   * This does nothing if thread assertions are disabled.
-   */
-  static void AssertOnSamplerThread();
-
-  /**
-   * Runs the given task on the APZ "sampler thread" for this platform. If
-   * this function is called from the sampler thread itself then the task is
-   * run immediately without getting queued.
-   */
-  static void RunOnSamplerThread(already_AddRefed<Runnable> aTask);
-
-  /**
-   * Returns true if currently on the APZ "sampler thread".
-   */
-  static bool IsSamplerThread();
 };
 
 // A base class for GenericNamedTimerCallback<Function>.
 // This is necessary because NS_IMPL_ISUPPORTS doesn't work for a class
 // template.
 class GenericNamedTimerCallbackBase : public nsITimerCallback,
                                       public nsINamed
 {