Bug 1368382. P2 - let ThenCommand reference the sub-type of ThenValueBase. draft
authorJW Wang <jwwang@mozilla.com>
Wed, 31 May 2017 15:23:43 +0800
changeset 587507 daee28588ca865f4141244edc1759657bdf83ea0
parent 587506 3fc785baf6ba5794f02f8375704767349f7a6638
child 587508 886e4dbe7444aa4cef2705b9ec44158d9aae2502
push id61734
push userjwwang@mozilla.com
push dateThu, 01 Jun 2017 06:57:58 +0000
bugs1368382
milestone55.0a1
Bug 1368382. P2 - let ThenCommand reference the sub-type of ThenValueBase. Since we will store mCompletionPromise in the sub-class of ThenValueBase, ThenCommand needs to reference the sub-type in order to access mCompletionPromise. MozReview-Commit-ID: BUi7jElOhP7
xpcom/threads/MozPromise.h
--- a/xpcom/threads/MozPromise.h
+++ b/xpcom/threads/MozPromise.h
@@ -538,25 +538,34 @@ protected:
                            !TakesArgument<MethodType>::value,
                            already_AddRefed<MozPromise>>::Type
   InvokeCallbackMethod(ThisType* aThisVal, MethodType aMethod, ValueType&& aValue)
   {
     ((*aThisVal).*aMethod)();
     return nullptr;
   }
 
+  template<typename>
+  class ThenCommand;
+
   template<typename...>
   class ThenValue;
 
   template<typename ThisType,
            typename ResolveMethodType,
            typename RejectMethodType>
   class ThenValue<ThisType*, ResolveMethodType, RejectMethodType>
     : public ThenValueBase
   {
+    friend class ThenCommand<ThenValue>;
+    using SupportChaining = IntegralConstant<
+      bool,
+      ReturnTypeIs<ResolveMethodType, RefPtr<MozPromise>>::value &&
+        ReturnTypeIs<RejectMethodType, RefPtr<MozPromise>>::value>;
+
   public:
     ThenValue(AbstractThread* aResponseTarget,
               ThisType* aThisVal,
               ResolveMethodType aResolveMethod,
               RejectMethodType aRejectMethod,
               const char* aCallSite)
       : ThenValueBase(aResponseTarget, aCallSite)
       , mThisVal(aThisVal)
@@ -600,16 +609,20 @@ protected:
     RefPtr<ThisType> mThisVal; // Only accessed and refcounted on dispatch thread.
     ResolveMethodType mResolveMethod;
     RejectMethodType mRejectMethod;
   };
 
   template<typename ThisType, typename ResolveRejectMethodType>
   class ThenValue<ThisType*, ResolveRejectMethodType> : public ThenValueBase
   {
+    friend class ThenCommand<ThenValue>;
+    using SupportChaining =
+      ReturnTypeIs<ResolveRejectMethodType, RefPtr<MozPromise>>;
+
   public:
     ThenValue(AbstractThread* aResponseTarget,
               ThisType* aThisVal,
               ResolveRejectMethodType aResolveRejectMethod,
               const char* aCallSite)
       : ThenValueBase(aResponseTarget, aCallSite)
       , mThisVal(aThisVal)
       , mResolveRejectMethod(aResolveRejectMethod)
@@ -644,16 +657,22 @@ protected:
     RefPtr<ThisType> mThisVal; // Only accessed and refcounted on dispatch thread.
     ResolveRejectMethodType mResolveRejectMethod;
   };
 
   // NB: We could use std::function here instead of a template if it were supported. :-(
   template<typename ResolveFunction, typename RejectFunction>
   class ThenValue<ResolveFunction, RejectFunction> : public ThenValueBase
   {
+    friend class ThenCommand<ThenValue>;
+    using SupportChaining = IntegralConstant<
+      bool,
+      ReturnTypeIs<ResolveFunction, RefPtr<MozPromise>>::value &&
+        ReturnTypeIs<RejectFunction, RefPtr<MozPromise>>::value>;
+
   public:
     ThenValue(AbstractThread* aResponseTarget,
               ResolveFunction&& aResolveFunction,
               RejectFunction&& aRejectFunction,
               const char* aCallSite)
       : ThenValueBase(aResponseTarget, aCallSite)
     {
       mResolveFunction.emplace(Move(aResolveFunction));
@@ -702,16 +721,20 @@ protected:
   private:
     Maybe<ResolveFunction> mResolveFunction; // Only accessed and deleted on dispatch thread.
     Maybe<RejectFunction> mRejectFunction; // Only accessed and deleted on dispatch thread.
   };
 
   template<typename ResolveRejectFunction>
   class ThenValue<ResolveRejectFunction> : public ThenValueBase
   {
+    friend class ThenCommand<ThenValue>;
+    using SupportChaining =
+      ReturnTypeIs<ResolveRejectFunction, RefPtr<MozPromise>>;
+
   public:
     ThenValue(AbstractThread* aResponseTarget,
               ResolveRejectFunction&& aResolveRejectFunction,
               const char* aCallSite)
       : ThenValueBase(aResponseTarget, aCallSite)
     {
       mResolveRejectFunction.emplace(Move(aResolveRejectFunction));
     }
@@ -766,34 +789,34 @@ public:
                 aCallSite, this, aThenValue, (int) IsPending());
     if (!IsPending()) {
       aThenValue->Dispatch(this);
     } else {
       mThenValues.AppendElement(aThenValue);
     }
   }
 
-private:
+protected:
   /*
    * A command object to store all information needed to make a request to
    * the promise. This allows us to delay the request until further use is
    * known (whether it is ->Then() again for more promise chaining or ->Track()
    * to terminate chaining and issue the request).
    *
    * This allows a unified syntax for promise chaining and disconnection
    * and feels more like its JS counterpart.
    */
-  template <bool SupportChaining>
+  template<typename ThenValueType>
   class ThenCommand
   {
     friend class MozPromise;
 
     ThenCommand(AbstractThread* aResponseThread,
                 const char* aCallSite,
-                already_AddRefed<ThenValueBase> aThenValue,
+                already_AddRefed<ThenValueType> aThenValue,
                 MozPromise* aReceiver)
       : mResponseThread(aResponseThread)
       , mCallSite(aCallSite)
       , mThenValue(aThenValue)
       , mReceiver(aReceiver)
     {
       MOZ_ASSERT(aResponseThread);
     }
@@ -810,21 +833,22 @@ private:
     }
 
     // Allow RefPtr<MozPromise> p = somePromise->Then();
     //       p->Then(thread1, ...);
     //       p->Then(thread2, ...);
     template <typename...>
     operator RefPtr<MozPromise>()
     {
-      static_assert(SupportChaining,
+      static_assert(
+        ThenValueType::SupportChaining::value,
         "The resolve/reject callback needs to return a RefPtr<MozPromise> "
         "in order to do promise chaining.");
 
-      RefPtr<ThenValueBase> thenValue = mThenValue.forget();
+      RefPtr<ThenValueType> thenValue = mThenValue.forget();
       // mCompletionPromise must be created before ThenInternal() to avoid race.
       RefPtr<MozPromise::Private> p = new MozPromise::Private(
         "<completion promise>", true /* aIsCompletionPromise */);
       thenValue->mCompletionPromise = p;
       // Note ThenInternal() might nullify mCompletionPromise before return.
       // So we need to return p instead of mCompletionPromise.
       mReceiver->ThenInternal(mResponseThread, thenValue, mCallSite);
       return p;
@@ -834,120 +858,94 @@ private:
     auto Then(Ts&&... aArgs)
       -> decltype(DeclVal<MozPromise>().Then(Forward<Ts>(aArgs)...))
     {
       return static_cast<RefPtr<MozPromise>>(*this)->Then(Forward<Ts>(aArgs)...);
     }
 
     void Track(MozPromiseRequestHolder<MozPromise>& aRequestHolder)
     {
-      RefPtr<ThenValueBase> thenValue = mThenValue.forget();
+      RefPtr<ThenValueType> thenValue = mThenValue.forget();
       mReceiver->ThenInternal(mResponseThread, thenValue, mCallSite);
       aRequestHolder.Track(thenValue.forget());
     }
 
     // Allow calling ->Then() again for more promise chaining or ->Track() to
     // end chaining and track the request for future disconnection.
     ThenCommand* operator->()
     {
       return this;
     }
 
   private:
     AbstractThread* mResponseThread;
     const char* mCallSite;
-    RefPtr<ThenValueBase> mThenValue;
+    RefPtr<ThenValueType> mThenValue;
     MozPromise* mReceiver;
   };
 
-  template<typename Method>
-  using MethodReturnPromise =
-    ReturnTypeIs<Method, RefPtr<MozPromise>>;
-
-  template<typename Function>
-  using FunctionReturnPromise =
-    MethodReturnPromise<decltype(&Function::operator())>;
-
-  template <typename M1, typename... Ms>
-  struct MethodThenCommand
-  {
-    static const bool value =
-      MethodThenCommand<M1>::value && MethodThenCommand<Ms...>::value;
-    using type = ThenCommand<value>;
-  };
-
-  template <typename M1>
-  struct MethodThenCommand<M1>
-  {
-    static const bool value = MethodReturnPromise<M1>::value;
-    using type = ThenCommand<value>;
-  };
-
-  template <typename F1, typename... Fs>
-  struct FunctionThenCommand
-  {
-    static const bool value =
-      FunctionThenCommand<F1>::value && FunctionThenCommand<Fs...>::value;
-    using type = ThenCommand<value>;
-  };
-
-  template <typename F1>
-  struct FunctionThenCommand<F1>
-  {
-    static const bool value = FunctionReturnPromise<F1>::value;
-    using type = ThenCommand<value>;
-  };
-
 public:
-  template<typename ThisType, typename ResolveMethodType, typename RejectMethodType>
-  typename MethodThenCommand<ResolveMethodType, RejectMethodType>::type
-  Then(AbstractThread* aResponseThread, const char* aCallSite,
-    ThisType* aThisVal, ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod)
+  template<typename ThisType,
+           typename ResolveMethodType,
+           typename RejectMethodType,
+           typename ThenValueType =
+             ThenValue<ThisType*, ResolveMethodType, RejectMethodType>,
+           typename ReturnType = ThenCommand<ThenValueType>>
+  ReturnType Then(AbstractThread* aResponseThread,
+                  const char* aCallSite,
+                  ThisType* aThisVal,
+                  ResolveMethodType aResolveMethod,
+                  RejectMethodType aRejectMethod)
   {
-    using ThenType = ThenValue<ThisType*, ResolveMethodType, RejectMethodType>;
-    RefPtr<ThenValueBase> thenValue = new ThenType(aResponseThread,
-       aThisVal, aResolveMethod, aRejectMethod, aCallSite);
-    return typename MethodThenCommand<ResolveMethodType, RejectMethodType>::type(
-      aResponseThread, aCallSite, thenValue.forget(), this);
+    RefPtr<ThenValueType> thenValue = new ThenValueType(
+      aResponseThread, aThisVal, aResolveMethod, aRejectMethod, aCallSite);
+    return ReturnType(aResponseThread, aCallSite, thenValue.forget(), this);
   }
 
-  template<typename ThisType, typename ResolveRejectMethodType>
-  typename MethodThenCommand<ResolveRejectMethodType>::type
-  Then(AbstractThread* aResponseThread, const char* aCallSite,
-    ThisType* aThisVal, ResolveRejectMethodType aResolveRejectMethod)
+  template<
+    typename ThisType,
+    typename ResolveRejectMethodType,
+    typename ThenValueType = ThenValue<ThisType*, ResolveRejectMethodType>,
+    typename ReturnType = ThenCommand<ThenValueType>>
+  ReturnType Then(AbstractThread* aResponseThread,
+                  const char* aCallSite,
+                  ThisType* aThisVal,
+                  ResolveRejectMethodType aResolveRejectMethod)
   {
-    using ThenType = ThenValue<ThisType*, ResolveRejectMethodType>;
-    RefPtr<ThenValueBase> thenValue = new ThenType(aResponseThread,
-       aThisVal, aResolveRejectMethod, aCallSite);
-    return typename MethodThenCommand<ResolveRejectMethodType>::type(
-      aResponseThread, aCallSite, thenValue.forget(), this);
+    RefPtr<ThenValueType> thenValue = new ThenValueType(
+      aResponseThread, aThisVal, aResolveRejectMethod, aCallSite);
+    return ReturnType(aResponseThread, aCallSite, thenValue.forget(), this);
   }
 
-  template<typename ResolveFunction, typename RejectFunction>
-  typename FunctionThenCommand<ResolveFunction, RejectFunction>::type
-  Then(AbstractThread* aResponseThread, const char* aCallSite,
-    ResolveFunction&& aResolveFunction, RejectFunction&& aRejectFunction)
+  template<typename ResolveFunction,
+           typename RejectFunction,
+           typename ThenValueType = ThenValue<ResolveFunction, RejectFunction>,
+           typename ReturnType = ThenCommand<ThenValueType>>
+  ReturnType Then(AbstractThread* aResponseThread,
+                  const char* aCallSite,
+                  ResolveFunction&& aResolveFunction,
+                  RejectFunction&& aRejectFunction)
   {
-    using ThenType = ThenValue<ResolveFunction, RejectFunction>;
-    RefPtr<ThenValueBase> thenValue = new ThenType(aResponseThread,
-      Move(aResolveFunction), Move(aRejectFunction), aCallSite);
-    return typename FunctionThenCommand<ResolveFunction, RejectFunction>::type(
-      aResponseThread, aCallSite, thenValue.forget(), this);
+    RefPtr<ThenValueType> thenValue = new ThenValueType(aResponseThread,
+                                                        Move(aResolveFunction),
+                                                        Move(aRejectFunction),
+                                                        aCallSite);
+    return ReturnType(aResponseThread, aCallSite, thenValue.forget(), this);
   }
 
-  template<typename ResolveRejectFunction>
-  typename FunctionThenCommand<ResolveRejectFunction>::type
-  Then(AbstractThread* aResponseThread, const char* aCallSite,
-                   ResolveRejectFunction&& aResolveRejectFunction)
+  template<typename ResolveRejectFunction,
+           typename ThenValueType = ThenValue<ResolveRejectFunction>,
+           typename ReturnType = ThenCommand<ThenValueType>>
+  ReturnType Then(AbstractThread* aResponseThread,
+                  const char* aCallSite,
+                  ResolveRejectFunction&& aResolveRejectFunction)
   {
-    using ThenType = ThenValue<ResolveRejectFunction>;
-    RefPtr<ThenValueBase> thenValue = new ThenType(aResponseThread,
-      Move(aResolveRejectFunction), aCallSite);
-    return typename FunctionThenCommand<ResolveRejectFunction>::type(
-      aResponseThread, aCallSite, thenValue.forget(), this);
+    RefPtr<ThenValueType> thenValue = new ThenValueType(
+      aResponseThread, Move(aResolveRejectFunction), aCallSite);
+    return ReturnType(aResponseThread, aCallSite, thenValue.forget(), this);
   }
 
   void ChainTo(already_AddRefed<Private> aChainedPromise, const char* aCallSite)
   {
     MutexAutoLock lock(mMutex);
     MOZ_DIAGNOSTIC_ASSERT(!IsExclusive || !mHaveRequest);
     mHaveRequest = true;
     RefPtr<Private> chainedPromise = aChainedPromise;