Bug 1449982 - Implement the WR updater thread registration. r?botond draft
authorKartikaya Gupta <kgupta@mozilla.com>
Tue, 10 Apr 2018 12:29:55 -0400
changeset 779787 f79351207dd428b3e680d43e7a82c948cd5cf960
parent 779786 a14e72dc7259e27ecfb0b6d7ea98c831fe195888
child 779788 31efc2c2cb434895a782a3853ba333da96a67754
push id105878
push userkgupta@mozilla.com
push dateTue, 10 Apr 2018 16:32:25 +0000
reviewersbotond
bugs1449982
milestone61.0a1
Bug 1449982 - Implement the WR updater thread registration. r?botond This lets the APZUpdater know which thread is the actual updater thread. This is only really used for the thread assertions, but might be useful for debugging and such as well. MozReview-Commit-ID: IIDm6Ui3Sh4
gfx/layers/apz/public/APZUpdater.h
gfx/layers/apz/src/APZUpdater.cpp
--- a/gfx/layers/apz/public/APZUpdater.h
+++ b/gfx/layers/apz/public/APZUpdater.h
@@ -4,16 +4,17 @@
  * 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/. */
 
 #ifndef mozilla_layers_APZUpdater_h
 #define mozilla_layers_APZUpdater_h
 
 #include <unordered_map>
 
+#include "base/platform_thread.h"   // for PlatformThreadId
 #include "LayersTypes.h"
 #include "mozilla/layers/APZTestData.h"
 #include "mozilla/StaticMutex.h"
 #include "nsThreadUtils.h"
 #include "Units.h"
 
 namespace mozilla {
 
@@ -39,16 +40,24 @@ class APZUpdater {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(APZUpdater)
 
 public:
   explicit APZUpdater(const RefPtr<APZCTreeManager>& aApz);
 
   bool HasTreeManager(const RefPtr<APZCTreeManager>& aApz);
   void SetWebRenderWindowId(const wr::WindowId& aWindowId);
 
+  /**
+   * This function is invoked from rust on the scene builder thread when it
+   * is created. It effectively tells the APZUpdater "the current thread is
+   * the updater thread for this window id" and allows APZUpdater to remember
+   * which thread it is.
+   */
+  static void SetUpdaterThread(const wr::WrWindowId& aWindowId);
+
   void ClearTree();
   void UpdateFocusState(LayersId aRootLayerTreeId,
                         LayersId aOriginatingLayersId,
                         const FocusTarget& aFocusTarget);
   void UpdateHitTestingTree(LayersId aRootLayerTreeId,
                             Layer* aRoot,
                             bool aIsFirstPaint,
                             LayersId aOriginatingLayersId,
@@ -99,23 +108,41 @@ public:
    * thread), that is when the task gets dispatched to the controller thread.
    * The controller thread then actually runs the task.
    */
   void RunOnControllerThread(already_AddRefed<Runnable> aTask);
 
 protected:
   virtual ~APZUpdater();
 
+  bool UsingWebRenderUpdaterThread() const;
+  static already_AddRefed<APZUpdater> GetUpdater(const wr::WrWindowId& aWindowId);
+
 private:
   RefPtr<APZCTreeManager> mApz;
 
   // 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.
   static StaticMutex sWindowIdLock;
   static std::unordered_map<uint64_t, APZUpdater*> sWindowIdMap;
   Maybe<wr::WrWindowId> mWindowId;
+
+  // If WebRender and async scene building are enabled, this holds the thread id
+  // of the scene builder thread (which is the updater thread) for the
+  // compositor associated with this APZUpdater instance. It may be populated
+  // even if async scene building is not enabled, but in that case we don't
+  // care about the contents.
+  // This is written to once during init and never cleared, and so reading it
+  // from multiple threads during normal operation (after initialization)
+  // 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
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // mozilla_layers_APZUpdater_h
--- a/gfx/layers/apz/src/APZUpdater.cpp
+++ b/gfx/layers/apz/src/APZUpdater.cpp
@@ -19,16 +19,19 @@ namespace mozilla {
 namespace layers {
 
 StaticMutex APZUpdater::sWindowIdLock;
 std::unordered_map<uint64_t, APZUpdater*> APZUpdater::sWindowIdMap;
 
 
 APZUpdater::APZUpdater(const RefPtr<APZCTreeManager>& aApz)
   : mApz(aApz)
+#ifdef DEBUG
+  , mUpdaterThreadQueried(false)
+#endif
 {
   MOZ_ASSERT(aApz);
   mApz->SetUpdater(this);
 }
 
 APZUpdater::~APZUpdater()
 {
   mApz->SetUpdater(nullptr);
@@ -50,16 +53,26 @@ void
 APZUpdater::SetWebRenderWindowId(const wr::WindowId& aWindowId)
 {
   StaticMutexAutoLock lock(sWindowIdLock);
   MOZ_ASSERT(!mWindowId);
   mWindowId = Some(aWindowId);
   sWindowIdMap[wr::AsUint64(aWindowId)] = this;
 }
 
+/*static*/ void
+APZUpdater::SetUpdaterThread(const wr::WrWindowId& aWindowId)
+{
+  if (RefPtr<APZUpdater> updater = GetUpdater(aWindowId)) {
+    // Ensure nobody tried to use the updater thread before this point.
+    MOZ_ASSERT(!updater->mUpdaterThreadQueried);
+    updater->mUpdaterThreadId = Some(PlatformThread::CurrentId());
+  }
+}
+
 void
 APZUpdater::ClearTree()
 {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   RefPtr<APZUpdater> self = this;
   RunOnUpdaterThread(NS_NewRunnableFunction(
     "APZUpdater::ClearTree",
     [=]() {
@@ -230,55 +243,96 @@ APZUpdater::AssertOnUpdaterThread()
   }
 }
 
 void
 APZUpdater::RunOnUpdaterThread(already_AddRefed<Runnable> aTask)
 {
   RefPtr<Runnable> task = aTask;
 
-  MessageLoop* loop = CompositorThreadHolder::Loop();
-  if (!loop) {
-    // Could happen during startup
+  if (IsUpdaterThread()) {
+    task->Run();
+    return;
+  }
+
+  if (UsingWebRenderUpdaterThread()) {
+    // TODO
     NS_WARNING("Dropping task posted to updater thread");
     return;
   }
 
-  if (IsUpdaterThread()) {
-    task->Run();
+  if (MessageLoop* loop = CompositorThreadHolder::Loop()) {
+    loop->PostTask(task.forget());
   } else {
-    loop->PostTask(task.forget());
+    // Could happen during startup
+    NS_WARNING("Dropping task posted to updater thread");
   }
 }
 
 bool
 APZUpdater::IsUpdaterThread()
 {
+  if (UsingWebRenderUpdaterThread()) {
+    return PlatformThread::CurrentId() == *mUpdaterThreadId;
+  }
   return CompositorThreadHolder::IsInCompositorThread();
 }
 
 void
 APZUpdater::RunOnControllerThread(already_AddRefed<Runnable> aTask)
 {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
 
   RunOnUpdaterThread(NewRunnableFunction(
       "APZUpdater::RunOnControllerThread",
       &APZThreadUtils::RunOnControllerThread,
       Move(aTask)));
 }
 
+bool
+APZUpdater::UsingWebRenderUpdaterThread() const
+{
+  if (!gfxPrefs::WebRenderAsyncSceneBuild()) {
+    return false;
+  }
+  // If mUpdaterThreadId is not set at the point that this is called, then
+  // that means that either (a) WebRender is not enabled for the compositor
+  // to which this APZUpdater is attached or (b) we are attempting to do
+  // something updater-related before WebRender is up and running. In case
+  // (a) falling back to the compositor thread is correct, and in case (b)
+  // we should stop doing the updater-related thing so early. We catch this
+  // case by setting the mUpdaterThreadQueried flag and asserting on WR
+  // initialization.
+#ifdef DEBUG
+  mUpdaterThreadQueried = true;
+#endif
+  return mUpdaterThreadId.isSome();
+}
+
+/*static*/ already_AddRefed<APZUpdater>
+APZUpdater::GetUpdater(const wr::WrWindowId& aWindowId)
+{
+  RefPtr<APZUpdater> updater;
+  StaticMutexAutoLock lock(sWindowIdLock);
+  auto it = sWindowIdMap.find(wr::AsUint64(aWindowId));
+  if (it != sWindowIdMap.end()) {
+    updater = it->second;
+  }
+  return updater.forget();
+}
+
 } // namespace layers
 } // namespace mozilla
 
 // Rust callback implementations
 
 void
 apz_register_updater(mozilla::wr::WrWindowId aWindowId)
 {
+  mozilla::layers::APZUpdater::SetUpdaterThread(aWindowId);
 }
 
 void
 apz_pre_scene_swap(mozilla::wr::WrWindowId aWindowId)
 {
 }
 
 void