Bug 1367679. P4 - specialize the type of mCompletionPromise according to whether chaining is supported. draft
authorJW Wang <jwwang@mozilla.com>
Fri, 02 Jun 2017 10:16:31 +0800
changeset 588115 99ef0109a439d88645181d4a3c5ce09f37a49191
parent 588114 d4fc95136513ef4d0d427d0d72a20f5eaa90cf4b
child 588116 1056242fa74258ac15854cfd54507f3232c2107f
push id61915
push userjwwang@mozilla.com
push dateFri, 02 Jun 2017 06:48:13 +0000
bugs1367679
milestone55.0a1
Bug 1367679. P4 - specialize the type of mCompletionPromise according to whether chaining is supported. MozReview-Commit-ID: 8dPXiGl5njE
xpcom/threads/MozPromise.h
--- a/xpcom/threads/MozPromise.h
+++ b/xpcom/threads/MozPromise.h
@@ -76,16 +76,30 @@ struct MethodTrait : MethodTraitsHelper<
 template<typename MethodType>
 using TakesArgument =
   IntegralConstant<bool, detail::MethodTrait<MethodType>::ArgSize != 0>;
 
 template<typename MethodType, typename TargetType>
 using ReturnTypeIs =
   IsConvertible<typename detail::MethodTrait<MethodType>::ReturnType, TargetType>;
 
+template<typename ResolveValueT, typename RejectValueT, bool IsExclusive>
+class MozPromise;
+
+template<typename Return>
+struct IsMozPromise : FalseType
+{
+};
+
+template<typename ResolveValueT, typename RejectValueT, bool IsExclusive>
+struct IsMozPromise<MozPromise<ResolveValueT, RejectValueT, IsExclusive>>
+  : TrueType
+{
+};
+
 /*
  * A promise manages an asynchronous request that may or may not be able to be
  * fulfilled immediately. When an API returns a promise, the consumer may attach
  * callbacks to be invoked (asynchronously, on a specified thread) when the
  * request is either completed (resolved) or cannot be completed (rejected).
  * Whereas JS promise callbacks are dispatched from Microtask checkpoints,
  * MozPromises resolution/rejection make a normal round-trip through the event
  * loop, which simplifies their ordering semantics relative to other native code.
@@ -520,39 +534,41 @@ protected:
   {
     return (aThisVal->*aMethod)();
   }
 
   // Called when promise chaining is supported.
   template<bool SupportChaining,
            typename ThisType,
            typename MethodType,
-           typename ValueType>
+           typename ValueType,
+           typename CompletionPromiseType>
   static typename EnableIf<SupportChaining, void>::Type InvokeCallbackMethod(
     ThisType* aThisVal,
     MethodType aMethod,
     ValueType&& aValue,
-    RefPtr<Private>&& aCompletionPromise)
+    CompletionPromiseType&& aCompletionPromise)
   {
     auto p = InvokeMethod(aThisVal, aMethod, Forward<ValueType>(aValue));
     if (aCompletionPromise) {
       p->ChainTo(aCompletionPromise.forget(), "<chained completion promise>");
     }
   }
 
   // Called when promise chaining is not supported.
   template<bool SupportChaining,
            typename ThisType,
            typename MethodType,
-           typename ValueType>
+           typename ValueType,
+           typename CompletionPromiseType>
   static typename EnableIf<!SupportChaining, void>::Type InvokeCallbackMethod(
     ThisType* aThisVal,
     MethodType aMethod,
     ValueType&& aValue,
-    RefPtr<Private>&& aCompletionPromise)
+    CompletionPromiseType&& aCompletionPromise)
   {
     MOZ_DIAGNOSTIC_ASSERT(
       !aCompletionPromise,
       "Can't do promise chaining for a non-promise-returning method.");
     InvokeMethod(aThisVal, aMethod, Forward<ValueType>(aValue));
   }
 
   template<typename>
@@ -563,20 +579,27 @@ protected:
 
   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>;
+
+    using R1 = typename RemoveSmartPointer<
+      typename detail::MethodTrait<ResolveMethodType>::ReturnType>::Type;
+    using R2 = typename RemoveSmartPointer<
+      typename detail::MethodTrait<RejectMethodType>::ReturnType>::Type;
+    using SupportChaining =
+      IntegralConstant<bool, IsMozPromise<R1>::value && IsSame<R1, R2>::value>;
+
+    // Fall back to MozPromise when promise chaining is not supported to make code compile.
+    using PromiseType =
+      typename Conditional<SupportChaining::value, R1, MozPromise>::Type;
 
   public:
     ThenValue(AbstractThread* aResponseTarget,
               ThisType* aThisVal,
               ResolveMethodType aResolveMethod,
               RejectMethodType aRejectMethod,
               const char* aCallSite)
       : ThenValueBase(aResponseTarget, aCallSite)
@@ -624,25 +647,31 @@ protected:
       // which may or may not be ok.
       mThisVal = nullptr;
     }
 
   private:
     RefPtr<ThisType> mThisVal; // Only accessed and refcounted on dispatch thread.
     ResolveMethodType mResolveMethod;
     RejectMethodType mRejectMethod;
-    RefPtr<Private> mCompletionPromise;
+    RefPtr<typename PromiseType::Private> mCompletionPromise;
   };
 
   template<typename ThisType, typename ResolveRejectMethodType>
   class ThenValue<ThisType*, ResolveRejectMethodType> : public ThenValueBase
   {
     friend class ThenCommand<ThenValue>;
-    using SupportChaining =
-      ReturnTypeIs<ResolveRejectMethodType, RefPtr<MozPromise>>;
+
+    using R1 = typename RemoveSmartPointer<
+      typename detail::MethodTrait<ResolveRejectMethodType>::ReturnType>::Type;
+    using SupportChaining = IntegralConstant<bool, IsMozPromise<R1>::value>;
+
+    // Fall back to MozPromise when promise chaining is not supported to make code compile.
+    using PromiseType =
+      typename Conditional<SupportChaining::value, R1, MozPromise>::Type;
 
   public:
     ThenValue(AbstractThread* aResponseTarget,
               ThisType* aThisVal,
               ResolveRejectMethodType aResolveRejectMethod,
               const char* aCallSite)
       : ThenValueBase(aResponseTarget, aCallSite)
       , mThisVal(aThisVal)
@@ -677,28 +706,35 @@ protected:
       // released on whatever thread last drops its reference to the ThenValue,
       // which may or may not be ok.
       mThisVal = nullptr;
     }
 
   private:
     RefPtr<ThisType> mThisVal; // Only accessed and refcounted on dispatch thread.
     ResolveRejectMethodType mResolveRejectMethod;
-    RefPtr<Private> mCompletionPromise;
+    RefPtr<typename PromiseType::Private> mCompletionPromise;
   };
 
   // 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>;
+
+    using R1 = typename RemoveSmartPointer<
+      typename detail::MethodTrait<ResolveFunction>::ReturnType>::Type;
+    using R2 = typename RemoveSmartPointer<
+      typename detail::MethodTrait<RejectFunction>::ReturnType>::Type;
+    using SupportChaining =
+      IntegralConstant<bool, IsMozPromise<R1>::value && IsSame<R1, R2>::value>;
+
+    // Fall back to MozPromise when promise chaining is not supported to make code compile.
+    using PromiseType =
+      typename Conditional<SupportChaining::value, R1, MozPromise>::Type;
 
   public:
     ThenValue(AbstractThread* aResponseTarget,
               ResolveFunction&& aResolveFunction,
               RejectFunction&& aRejectFunction,
               const char* aCallSite)
       : ThenValueBase(aResponseTarget, aCallSite)
     {
@@ -751,25 +787,31 @@ protected:
       // which may or may not be ok.
       mResolveFunction.reset();
       mRejectFunction.reset();
     }
 
   private:
     Maybe<ResolveFunction> mResolveFunction; // Only accessed and deleted on dispatch thread.
     Maybe<RejectFunction> mRejectFunction; // Only accessed and deleted on dispatch thread.
-    RefPtr<Private> mCompletionPromise;
+    RefPtr<typename PromiseType::Private> mCompletionPromise;
   };
 
   template<typename ResolveRejectFunction>
   class ThenValue<ResolveRejectFunction> : public ThenValueBase
   {
     friend class ThenCommand<ThenValue>;
-    using SupportChaining =
-      ReturnTypeIs<ResolveRejectFunction, RefPtr<MozPromise>>;
+
+    using R1 = typename RemoveSmartPointer<
+      typename detail::MethodTrait<ResolveRejectFunction>::ReturnType>::Type;
+    using SupportChaining = IntegralConstant<bool, IsMozPromise<R1>::value>;
+
+    // Fall back to MozPromise when promise chaining is not supported to make code compile.
+    using PromiseType =
+      typename Conditional<SupportChaining::value, R1, MozPromise>::Type;
 
   public:
     ThenValue(AbstractThread* aResponseTarget,
               ResolveRejectFunction&& aResolveRejectFunction,
               const char* aCallSite)
       : ThenValueBase(aResponseTarget, aCallSite)
     {
       mResolveRejectFunction.emplace(Move(aResolveRejectFunction));
@@ -809,17 +851,17 @@ protected:
       // released predictably on the dispatch thread. Otherwise, they would be
       // released on whatever thread last drops its reference to the ThenValue,
       // which may or may not be ok.
       mResolveRejectFunction.reset();
     }
 
   private:
     Maybe<ResolveRejectFunction> mResolveRejectFunction; // Only accessed and deleted on dispatch thread.
-    RefPtr<Private> mCompletionPromise;
+    RefPtr<typename PromiseType::Private> mCompletionPromise;
   };
 
 public:
   void ThenInternal(AbstractThread* aResponseThread, ThenValueBase* aThenValue,
                     const char* aCallSite)
   {
     PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic && mMagic3 == sMagic && mMagic4 == &mMutex);
     MOZ_ASSERT(aResponseThread);
@@ -843,18 +885,24 @@ protected:
    * 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<typename ThenValueType>
   class ThenCommand
   {
+    // Allow Promise1::ThenCommand to access the private constructor,
+    // Promise2::ThenCommand(ThenCommand&&).
+    template<typename, typename, bool>
     friend class MozPromise;
 
+    using PromiseType = typename ThenValueType::PromiseType;
+    using Private = typename PromiseType::Private;
+
     ThenCommand(AbstractThread* aResponseThread,
                 const char* aCallSite,
                 already_AddRefed<ThenValueType> aThenValue,
                 MozPromise* aReceiver)
       : mResponseThread(aResponseThread)
       , mCallSite(aCallSite)
       , mThenValue(aThenValue)
       , mReceiver(aReceiver)
@@ -871,40 +919,40 @@ protected:
       if (mThenValue) {
         mReceiver->ThenInternal(mResponseThread, mThenValue, mCallSite);
       }
     }
 
     // Allow RefPtr<MozPromise> p = somePromise->Then();
     //       p->Then(thread1, ...);
     //       p->Then(thread2, ...);
-    template <typename...>
-    operator RefPtr<MozPromise>()
+    operator RefPtr<PromiseType>()
     {
       static_assert(
         ThenValueType::SupportChaining::value,
         "The resolve/reject callback needs to return a RefPtr<MozPromise> "
         "in order to do promise chaining.");
 
       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 */);
+      RefPtr<Private> p =
+        new 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;
     }
 
-    template <typename... Ts>
+    template<typename... Ts>
     auto Then(Ts&&... aArgs)
-      -> decltype(DeclVal<MozPromise>().Then(Forward<Ts>(aArgs)...))
+      -> decltype(DeclVal<PromiseType>().Then(Forward<Ts>(aArgs)...))
     {
-      return static_cast<RefPtr<MozPromise>>(*this)->Then(Forward<Ts>(aArgs)...);
+      return static_cast<RefPtr<PromiseType>>(*this)->Then(
+        Forward<Ts>(aArgs)...);
     }
 
     void Track(MozPromiseRequestHolder<MozPromise>& aRequestHolder)
     {
       RefPtr<ThenValueType> thenValue = mThenValue.forget();
       mReceiver->ThenInternal(mResponseThread, thenValue, mCallSite);
       aRequestHolder.Track(thenValue.forget());
     }
@@ -1286,26 +1334,16 @@ public:
   }
 
   bool Exists() const { return !!mRequest; }
 
 private:
   RefPtr<typename PromiseType::Request> mRequest;
 };
 
-template <typename Return>
-struct IsMozPromise
-  : FalseType
-{};
-
-template<typename ResolveValueT, typename RejectValueT, bool IsExclusive>
-struct IsMozPromise<MozPromise<ResolveValueT, RejectValueT, IsExclusive>>
-  : TrueType
-{};
-
 // Asynchronous Potentially-Cross-Thread Method Calls.
 //
 // This machinery allows callers to schedule a promise-returning function
 // (a method and object, or a function object like a lambda) to be invoked
 // asynchronously on a given thread, while at the same time receiving a
 // promise upon which to invoke Then() immediately. InvokeAsync dispatches a
 // task to invoke the function on the proper thread and also chain the
 // resulting promise to the one that the caller received, so that resolve/