Bug 1392217 - Part 1: Refactor the VR vibrate thread to be as a VRThread and manage its lifetime; r?kip draft
authorDaosheng Mu <daoshengmu@gmail.com>
Wed, 15 Nov 2017 17:39:38 +0800
changeset 707300 5811a0b9d68ea60ac8b675c0041232487b658d7e
parent 707249 f2cf6d1473808039be5ecd8727cc3791d5d7d2d4
child 707301 6f8fc00a733eebec1c1002c12e0141d0484cbb2e
push id92081
push userbmo:dmu@mozilla.com
push dateTue, 05 Dec 2017 04:08:07 +0000
reviewerskip
bugs1392217
milestone59.0a1
Bug 1392217 - Part 1: Refactor the VR vibrate thread to be as a VRThread and manage its lifetime; r?kip MozReview-Commit-ID: 7svQQGxDT6j
gfx/vr/VRThread.cpp
gfx/vr/VRThread.h
gfx/vr/gfxVROculus.cpp
gfx/vr/gfxVROculus.h
gfx/vr/gfxVROpenVR.cpp
gfx/vr/gfxVROpenVR.h
--- a/gfx/vr/VRThread.cpp
+++ b/gfx/vr/VRThread.cpp
@@ -1,24 +1,25 @@
 /* -*- 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 "VRThread.h"
 #include "nsThreadUtils.h"
 
 namespace mozilla {
 
 namespace gfx {
 
 static StaticRefPtr<VRListenerThreadHolder> sVRListenerThreadHolder;
 static bool sFinishedVRListenerShutDown = false;
+static const uint32_t kDefaultThreadLifeTime = 60; // in 60 seconds.
+static const uint32_t kDelayPostTaskTime = 20000; // in 20000 ms.
 
 VRListenerThreadHolder* GetVRListenerThreadHolder()
 {
   return sVRListenerThreadHolder;
 }
 
 base::Thread*
 VRListenerThread()
@@ -109,16 +110,133 @@ VRListenerThreadHolder::Shutdown()
 
 /* static */ bool
 VRListenerThreadHolder::IsInVRListenerThread()
 {
   return VRListenerThread() &&
 		 VRListenerThread()->thread_id() == PlatformThread::CurrentId();
 }
 
-} // namespace gfx
-} // namespace mozilla
+VRThread::VRThread(const nsCString& aName)
+ : mThread(nullptr)
+ , mLifeTime(kDefaultThreadLifeTime)
+ , mStarted(false)
+{
+  mName = aName;
+}
+
+VRThread::~VRThread()
+{
+  Shutdown();
+}
+
+void
+VRThread::Start()
+{
+  MOZ_ASSERT(VRListenerThreadHolder::IsInVRListenerThread());
+
+  if (!mThread) {
+    nsresult rv = NS_NewNamedThread(mName, getter_AddRefs(mThread));
+    MOZ_ASSERT(mThread);
+
+    if (NS_FAILED(rv)) {
+      MOZ_ASSERT(false, "Failed to create a vr thread.");
+    }
+    RefPtr<Runnable> runnable =
+      NewRunnableMethod<TimeStamp>(
+        "gfx::VRThread::CheckLife", this, &VRThread::CheckLife, TimeStamp::Now());
+    // Post it to the main thread for tracking the lifetime.
+    nsCOMPtr<nsIThread> mainThread;
+    rv = NS_GetMainThread(getter_AddRefs(mainThread));
+    if (NS_FAILED(rv)) {
+      NS_WARNING("VRThread::Start() could not get Main thread");
+      return;
+    }
+    mainThread->DelayedDispatch(runnable.forget(), kDelayPostTaskTime);
+  }
+  mStarted = true;
+  mLastActiveTime = TimeStamp::Now();
+}
+
+void
+VRThread::Shutdown()
+{
+  if (mThread) {
+    mThread->Shutdown();
+    mThread = nullptr;
+  }
+  mStarted = false;
+}
+
+const nsCOMPtr<nsIThread>
+VRThread::GetThread() const
+{
+  return mThread;
+}
+
+void
+VRThread::PostTask(already_AddRefed<Runnable> aTask)
+{
+  PostDelayedTask(Move(aTask), 0);
+}
+
+void
+VRThread::PostDelayedTask(already_AddRefed<Runnable> aTask,
+                          uint32_t aTime)
+{
+  MOZ_ASSERT(mStarted, "Must call Start() before posting tasks.");
+  MOZ_ASSERT(mThread);
+  mLastActiveTime = TimeStamp::Now();
+
+  if (!aTime) {
+    mThread->Dispatch(Move(aTask), NS_DISPATCH_NORMAL);
+  } else {
+    mThread->DelayedDispatch(Move(aTask), aTime);
+  }
+}
+
+void
+VRThread::CheckLife(TimeStamp aCheckTimestamp)
+{
+  // VR system is going to shutdown.
+  if (!mStarted) {
+    Shutdown();
+    return;
+  }
+
+  const TimeDuration timeout = TimeDuration::FromSeconds(mLifeTime);
+  if ((aCheckTimestamp - mLastActiveTime) > timeout) {
+    Shutdown();
+  } else {
+    RefPtr<Runnable> runnable =
+      NewRunnableMethod<TimeStamp>(
+        "gfx::VRThread::CheckLife", this, &VRThread::CheckLife, TimeStamp::Now());
+    // Post it to the main thread for tracking the lifetime.
+    nsCOMPtr<nsIThread> mainThread;
+    nsresult rv = NS_GetMainThread(getter_AddRefs(mainThread));
+    if (NS_FAILED(rv)) {
+      NS_WARNING("VRThread::CheckLife() could not get Main thread");
+      return;
+    }
+    mainThread->DelayedDispatch(runnable.forget(), kDelayPostTaskTime);
+  }
+}
+
+void
+VRThread::SetLifeTime(uint32_t aLifeTime)
+{
+  mLifeTime = aLifeTime;
+}
+
+uint32_t
+VRThread::GetLifeTime()
+{
+  return mLifeTime;
+}
 
 bool
-NS_IsInVRListenerThread()
+VRThread::IsActive()
 {
-  return mozilla::gfx::VRListenerThreadHolder::IsInVRListenerThread();
-}
\ No newline at end of file
+  return !!mThread;
+}
+
+} // namespace gfx
+} // namespace mozilla
--- a/gfx/vr/VRThread.h
+++ b/gfx/vr/VRThread.h
@@ -41,12 +41,40 @@ private:
   base::Thread* const mThread;
 
   static base::Thread* CreateThread();
   static void DestroyThread(base::Thread* aThread);
 };
 
 base::Thread* VRListenerThread();
 
+class VRThread final
+{
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VRThread)
+
+public:
+  explicit VRThread(const nsCString& aName);
+
+  void Start();
+  void Shutdown();
+  void SetLifeTime(uint32_t aLifeTime);
+  uint32_t GetLifeTime();
+  void CheckLife(TimeStamp aCheckTimestamp);
+  void PostTask(already_AddRefed<Runnable> aTask);
+  void PostDelayedTask(already_AddRefed<Runnable> aTask, uint32_t aTime);
+  const nsCOMPtr<nsIThread> GetThread() const;
+  bool IsActive();
+
+protected:
+  ~VRThread();
+
+private:
+  nsCOMPtr<nsIThread> mThread;
+  TimeStamp mLastActiveTime;
+  nsCString mName;
+  uint32_t mLifeTime;
+  Atomic<bool> mStarted;
+};
+
 } // namespace gfx
 } // namespace mozilla
 
 #endif // GFX_VR_THREAD_H
\ No newline at end of file
--- a/gfx/vr/gfxVROculus.cpp
+++ b/gfx/vr/gfxVROculus.cpp
@@ -1342,17 +1342,17 @@ void
 VRControllerOculus::UpdateVibrateHaptic(ovrSession aSession,
                                         uint32_t aHapticIndex,
                                         double aIntensity,
                                         double aDuration,
                                         uint64_t aVibrateIndex,
                                         const VRManagerPromise& aPromise)
 {
   // UpdateVibrateHaptic() only can be called by mVibrateThread
-  MOZ_ASSERT(mVibrateThread == NS_GetCurrentThread());
+  MOZ_ASSERT(mVibrateThread->GetThread() == NS_GetCurrentThread());
 
   // It has been interrupted by loss focus.
   if (mIsVibrateStopped) {
     VibrateHapticComplete(aSession, aPromise, true);
     return;
   }
   // Avoid the previous vibrate event to override the new one.
   if (mVibrateIndex != aVibrateIndex) {
@@ -1401,18 +1401,17 @@ VRControllerOculus::UpdateVibrateHaptic(
     MOZ_ASSERT(mVibrateThread);
 
     RefPtr<Runnable> runnable =
       NewRunnableMethod<ovrSession, uint32_t, double, double, uint64_t,
         StoreCopyPassByConstLRef<VRManagerPromise>>(
           "VRControllerOculus::UpdateVibrateHaptic",
           this, &VRControllerOculus::UpdateVibrateHaptic, aSession,
           aHapticIndex, aIntensity, (duration > kVibrateRate) ? remainingTime : 0, aVibrateIndex, aPromise);
-    NS_DelayedDispatchToCurrentThread(runnable.forget(),
-                                      (duration > kVibrateRate) ? kVibrateRate : remainingTime);
+    mVibrateThread->PostDelayedTask(runnable.forget(), (duration > kVibrateRate) ? kVibrateRate : remainingTime);
   } else {
     VibrateHapticComplete(aSession, aPromise, true);
   }
 }
 
 void
 VRControllerOculus::VibrateHapticComplete(ovrSession aSession, const VRManagerPromise& aPromise,
                                           bool aStop)
@@ -1452,33 +1451,29 @@ void
 VRControllerOculus::VibrateHaptic(ovrSession aSession,
                                   uint32_t aHapticIndex,
                                   double aIntensity,
                                   double aDuration,
                                   const VRManagerPromise& aPromise)
 {
   // Spinning up the haptics thread at the first haptics call.
   if (!mVibrateThread) {
-    nsresult rv = NS_NewThread(getter_AddRefs(mVibrateThread));
-    MOZ_ASSERT(mVibrateThread);
-
-    if (NS_FAILED(rv)) {
-      MOZ_ASSERT(false, "Failed to create async thread.");
-    }
+    mVibrateThread = new VRThread(NS_LITERAL_CSTRING("Oculus_Vibration"));
   }
+  mVibrateThread->Start();
   ++mVibrateIndex;
   mIsVibrateStopped = false;
 
   RefPtr<Runnable> runnable =
-       NewRunnableMethod<ovrSession, uint32_t, double, double, uint64_t,
-        StoreCopyPassByConstLRef<VRManagerPromise>>(
-          "VRControllerOculus::UpdateVibrateHaptic",
-          this, &VRControllerOculus::UpdateVibrateHaptic, aSession,
-          aHapticIndex, aIntensity, aDuration, mVibrateIndex, aPromise);
-  mVibrateThread->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
+    NewRunnableMethod<ovrSession, uint32_t, double, double, uint64_t,
+      StoreCopyPassByConstLRef<VRManagerPromise>>(
+        "VRControllerOculus::UpdateVibrateHaptic",
+        this, &VRControllerOculus::UpdateVibrateHaptic, aSession,
+        aHapticIndex, aIntensity, aDuration, mVibrateIndex, aPromise);
+  mVibrateThread->PostTask(runnable.forget());
 }
 
 void
 VRControllerOculus::StopVibrateHaptic()
 {
   mIsVibrateStopped = true;
 }
 
--- a/gfx/vr/gfxVROculus.h
+++ b/gfx/vr/gfxVROculus.h
@@ -22,16 +22,18 @@ struct ID3D11Device;
 
 namespace mozilla {
 namespace layers {
 class CompositingRenderTargetD3D11;
 struct VertexShaderConstants;
 struct PixelShaderConstants;
 }
 namespace gfx {
+class VRThread;
+
 namespace impl {
 
 enum class OculusControllerAxisType : uint16_t {
   ThumbstickXAxis,
   ThumbstickYAxis,
   NumVRControllerAxisType
 };
 
@@ -159,17 +161,17 @@ private:
                            uint64_t aVibrateIndex,
                            const VRManagerPromise& aPromise);
   void VibrateHapticComplete(ovrSession aSession, const VRManagerPromise& aPromise, bool aStop);
 
   float mAxisMove[static_cast<uint32_t>(
                   OculusControllerAxisType::NumVRControllerAxisType)];
   float mIndexTrigger;
   float mHandTrigger;
-  nsCOMPtr<nsIThread> mVibrateThread;
+  RefPtr<VRThread> mVibrateThread;
   Atomic<bool> mIsVibrateStopped;
 };
 
 } // namespace impl
 
 class VRSystemManagerOculus : public VRSystemManager
 {
 public:
--- a/gfx/vr/gfxVROpenVR.cpp
+++ b/gfx/vr/gfxVROpenVR.cpp
@@ -509,17 +509,17 @@ void
 VRControllerOpenVR::UpdateVibrateHaptic(::vr::IVRSystem* aVRSystem,
                                         uint32_t aHapticIndex,
                                         double aIntensity,
                                         double aDuration,
                                         uint64_t aVibrateIndex,
                                         const VRManagerPromise& aPromise)
 {
   // UpdateVibrateHaptic() only can be called by mVibrateThread
-  MOZ_ASSERT(mVibrateThread == NS_GetCurrentThread());
+  MOZ_ASSERT(mVibrateThread->GetThread() == NS_GetCurrentThread());
 
   // It has been interrupted by loss focus.
   if (mIsVibrateStopped) {
     VibrateHapticComplete(aPromise);
     return;
   }
   // Avoid the previous vibrate event to override the new one.
   if (mVibrateIndex != aVibrateIndex) {
@@ -535,24 +535,25 @@ VRControllerOpenVR::UpdateVibrateHaptic(
   aVRSystem->TriggerHapticPulse(GetTrackedIndex(),
                                 aHapticIndex, microSec);
 
   // In OpenVR spec, it mentions TriggerHapticPulse() may not trigger another haptic pulse
   // on this controller and axis combination for 5ms.
   const double kVibrateRate = 5.0;
   if (duration >= kVibrateRate) {
     MOZ_ASSERT(mVibrateThread);
+    MOZ_ASSERT(mVibrateThread->IsActive());
 
     RefPtr<Runnable> runnable =
       NewRunnableMethod<::vr::IVRSystem*, uint32_t, double, double, uint64_t,
         StoreCopyPassByConstLRef<VRManagerPromise>>(
           "VRControllerOpenVR::UpdateVibrateHaptic",
           this, &VRControllerOpenVR::UpdateVibrateHaptic, aVRSystem,
           aHapticIndex, aIntensity, duration - kVibrateRate, aVibrateIndex, aPromise);
-    NS_DelayedDispatchToCurrentThread(runnable.forget(), kVibrateRate);
+    mVibrateThread->PostDelayedTask(runnable.forget(), kVibrateRate);
   } else {
     // The pulse has completed
     VibrateHapticComplete(aPromise);
   }
 }
 
 void
 VRControllerOpenVR::VibrateHapticComplete(const VRManagerPromise& aPromise)
@@ -568,33 +569,29 @@ void
 VRControllerOpenVR::VibrateHaptic(::vr::IVRSystem* aVRSystem,
                                   uint32_t aHapticIndex,
                                   double aIntensity,
                                   double aDuration,
                                   const VRManagerPromise& aPromise)
 {
   // Spinning up the haptics thread at the first haptics call.
   if (!mVibrateThread) {
-    nsresult rv = NS_NewThread(getter_AddRefs(mVibrateThread));
-    MOZ_ASSERT(mVibrateThread);
-
-    if (NS_FAILED(rv)) {
-      MOZ_ASSERT(false, "Failed to create async thread.");
-    }
+    mVibrateThread = new VRThread(NS_LITERAL_CSTRING("OpenVR_Vibration"));
   }
+  mVibrateThread->Start();
   ++mVibrateIndex;
   mIsVibrateStopped = false;
 
   RefPtr<Runnable> runnable =
-      NewRunnableMethod<::vr::IVRSystem*, uint32_t, double, double, uint64_t,
-        StoreCopyPassByConstLRef<VRManagerPromise>>(
-          "VRControllerOpenVR::UpdateVibrateHaptic",
-          this, &VRControllerOpenVR::UpdateVibrateHaptic, aVRSystem,
-          aHapticIndex, aIntensity, aDuration, mVibrateIndex, aPromise);
-  mVibrateThread->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
+    NewRunnableMethod<::vr::IVRSystem*, uint32_t, double, double, uint64_t,
+      StoreCopyPassByConstLRef<VRManagerPromise>>(
+        "VRControllerOpenVR::UpdateVibrateHaptic",
+        this, &VRControllerOpenVR::UpdateVibrateHaptic, aVRSystem,
+        aHapticIndex, aIntensity, aDuration, mVibrateIndex, aPromise);
+  mVibrateThread->PostTask(runnable.forget());
 }
 
 void
 VRControllerOpenVR::StopVibrateHaptic()
 {
   mIsVibrateStopped = true;
 }
 
--- a/gfx/vr/gfxVROpenVR.h
+++ b/gfx/vr/gfxVROpenVR.h
@@ -4,32 +4,33 @@
  * 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 GFX_VR_OPENVR_H
 #define GFX_VR_OPENVR_H
 
 #include "nsTArray.h"
 #include "nsIScreen.h"
-#include "nsIThread.h"
 #include "nsCOMPtr.h"
 #include "mozilla/RefPtr.h"
 
 #include "mozilla/gfx/2D.h"
 #include "mozilla/EnumeratedArray.h"
 
 #include "openvr.h"
 #include "gfxVR.h"
 #include "VRDisplayHost.h"
 
 #if defined(XP_MACOSX)
 class MacIOSurface;
 #endif
 namespace mozilla {
 namespace gfx {
+class VRThread;
+
 namespace impl {
 
 class VRDisplayOpenVR : public VRDisplayHost
 {
 public:
   virtual void NotifyVSync() override;
   void ZeroSensor() override;
   bool GetIsHmdPresent();
@@ -109,17 +110,17 @@ private:
                            uint64_t aVibrateIndex,
                            const VRManagerPromise& aPromise);
   void VibrateHapticComplete(const VRManagerPromise& aPromise);
 
   // The index of tracked devices from ::vr::IVRSystem.
   uint32_t mTrackedIndex;
   nsTArray<float> mTrigger;
   nsTArray<float> mAxisMove;
-  nsCOMPtr<nsIThread> mVibrateThread;
+  RefPtr<VRThread> mVibrateThread;
   Atomic<bool> mIsVibrateStopped;
 };
 
 } // namespace impl
 
 class VRSystemManagerOpenVR : public VRSystemManager
 {
 public: