Bug 1371274 - Don't call SetDeadline on receivers without one. r=froydnj
authorAndreas Farre <farre@mozilla.com>
Fri, 16 Jun 2017 07:50:00 +0200
changeset 596647 88285532a879a9c97f753001e2c3655009747844
parent 596646 b1fcadce1fed009ae1da44f14ac531dcf81ebb55
child 596648 0f7345574bce1a0e1cb57de8f165c5f5ba7706ca
push id64704
push userbmo:rail@mozilla.com
push dateMon, 19 Jun 2017 15:14:27 +0000
reviewersfroydnj
bugs1371274
milestone56.0a1
Bug 1371274 - Don't call SetDeadline on receivers without one. r=froydnj Check if the receiver has SetDeadline and use a specialization that doesn't use it if it isn't present. This makes it possible to call: NewIdleRunnable(obj, Obj::Method) where Obj doesn't need to implement SetDeadline. This makes it easier to avoid unnecessary wrappers.
xpcom/threads/nsThreadUtils.h
--- a/xpcom/threads/nsThreadUtils.h
+++ b/xpcom/threads/nsThreadUtils.h
@@ -668,64 +668,41 @@ public:
   {
     // No ReturnTypeIsSafe makes this illegal!
   };
 
   // Make sure this return type is safe.
   typedef typename ReturnTypeEnforcer<ReturnType>::ReturnTypeIsSafe check;
 };
 
-template<class ClassType, bool Owning, bool Idle>
+template<class ClassType, bool Owning>
 struct nsRunnableMethodReceiver
 {
   RefPtr<ClassType> mObj;
   explicit nsRunnableMethodReceiver(ClassType* aObj) : mObj(aObj) {}
   ~nsRunnableMethodReceiver() { Revoke(); }
   ClassType* Get() const { return mObj.get(); }
   void Revoke() { mObj = nullptr; }
-  void SetDeadline(mozilla::TimeStamp aDeadline) { if (mObj) mObj->SetDeadline(aDeadline); }
 };
 
 template<class ClassType>
-struct nsRunnableMethodReceiver<ClassType, false, false>
+struct nsRunnableMethodReceiver<ClassType, false>
 {
   ClassType* MOZ_NON_OWNING_REF mObj;
   explicit nsRunnableMethodReceiver(ClassType* aObj) : mObj(aObj) {}
   ClassType* Get() const { return mObj; }
   void Revoke() { mObj = nullptr; }
-  void SetDeadline(mozilla::TimeStamp aDeadline) {}
-};
-
-template<class ClassType>
-struct nsRunnableMethodReceiver<ClassType, false, true>
-{
-  ClassType* MOZ_NON_OWNING_REF mObj;
-  explicit nsRunnableMethodReceiver(ClassType* aObj) : mObj(aObj) {}
-  ClassType* Get() const { return mObj; }
-  void Revoke() { mObj = nullptr; }
-  void SetDeadline(mozilla::TimeStamp aDeadline) { if (mObj) mObj->SetDeadline(aDeadline); }
 };
 
 static inline constexpr bool
 IsIdle(mozilla::RunnableKind aKind)
 {
   return aKind == mozilla::Idle || aKind == mozilla::IdleWithTimer;
 }
 
-template<class ClassType>
-struct nsRunnableMethodReceiver<ClassType, true, false>
-{
-  RefPtr<ClassType> mObj;
-  explicit nsRunnableMethodReceiver(ClassType* aObj) : mObj(aObj) {}
-  ~nsRunnableMethodReceiver() { Revoke(); }
-  ClassType* Get() const { return mObj.get(); }
-  void Revoke() { mObj = nullptr; }
-  void SetDeadline(mozilla::TimeStamp aDeadline) {}
-};
-
 template<typename PtrType, typename Method, bool Owning, mozilla::RunnableKind Kind>
 struct nsRunnableMethodTraits;
 
 template<typename PtrType, class C, typename R, bool Owning, mozilla::RunnableKind Kind, typename... As>
 struct nsRunnableMethodTraits<PtrType, R(C::*)(As...), Owning, Kind>
 {
   typedef typename mozilla::RemoveRawOrSmartPointer<PtrType>::Type class_type;
   static_assert(mozilla::IsBaseOf<C, class_type>::value,
@@ -1056,20 +1033,47 @@ struct NonParameterStorageClass
 // clean-up in destructor, and with associated IsParameterStorageClass<>.
 template<typename T>
 struct ParameterStorage
   : mozilla::Conditional<IsParameterStorageClass<T>::value,
                          T,
                          typename NonParameterStorageClass<T>::Type>
 {};
 
+template<class T>
+using SetDeadline_t = decltype(
+  mozilla::DeclVal<T>().SetDeadline(mozilla::DeclVal<mozilla::TimeStamp>()));
+
+template<class T>
+static auto
+HasSetDeadlineTest(int) -> SFINAE1True<SetDeadline_t<T>>;
+
+template<class T>
+static auto
+HasSetDeadlineTest(long) -> mozilla::FalseType;
+
+template<class T>
+struct HasSetDeadline : decltype(HasSetDeadlineTest<T>(0))
+{};
+
+template <class T>
+typename mozilla::EnableIf<::detail::HasSetDeadline<T>::value>::Type
+SetDeadlineImpl(T* aObj, mozilla::TimeStamp aTimeStamp)
+{
+  aObj->SetDeadline(aTimeStamp);
+}
+
+template <class T>
+typename mozilla::EnableIf<!::detail::HasSetDeadline<T>::value>::Type
+SetDeadlineImpl(T* aObj, mozilla::TimeStamp aTimeStamp)
+{
+}
 } /* namespace detail */
 
 namespace mozilla {
-
 namespace detail {
 
 // struct used to store arguments and later apply them to a method.
 template <typename... Ts>
 struct RunnableMethodArguments final
 {
   Tuple<typename ::detail::ParameterStorage<Ts>::Type...> mArguments;
   template <typename... As>
@@ -1095,17 +1099,17 @@ struct RunnableMethodArguments final
 template<typename PtrType, typename Method, bool Owning, RunnableKind Kind, typename... Storages>
 class RunnableMethodImpl final
   : public ::nsRunnableMethodTraits<PtrType, Method, Owning, Kind>::base_type
 {
   typedef typename ::nsRunnableMethodTraits<PtrType, Method, Owning, Kind> Traits;
 
   typedef typename Traits::class_type ClassType;
   typedef typename Traits::base_type BaseType;
-  ::nsRunnableMethodReceiver<ClassType, Owning, IsIdle(Kind)> mReceiver;
+  ::nsRunnableMethodReceiver<ClassType, Owning> mReceiver;
   Method mMethod;
   RunnableMethodArguments<Storages...> mArgs;
   using BaseType::GetTimer;
   using BaseType::CancelTimer;
 private:
   virtual ~RunnableMethodImpl() { Revoke(); };
   static void TimedOut(nsITimer* aTimer, void* aClosure)
   {
@@ -1146,17 +1150,19 @@ public:
   void Revoke()
   {
     CancelTimer();
     mReceiver.Revoke();
   }
 
   void SetDeadline(TimeStamp aDeadline)
   {
-    mReceiver.SetDeadline(aDeadline);
+    if (MOZ_LIKELY(mReceiver.Get())) {
+      ::detail::SetDeadlineImpl(mReceiver.Get(), aDeadline);
+    }
   }
 
   void SetTimer(uint32_t aDelay, nsIEventTarget* aTarget)
   {
     MOZ_ASSERT(aTarget);
 
     if (nsCOMPtr<nsITimer> timer = GetTimer()) {
       timer->Cancel();