Bug 1367679. P4 - specialize the type of mCompletionPromise according to whether chaining is supported. r=gerald
authorJW Wang <jwwang@mozilla.com>
Fri, 02 Jun 2017 10:16:31 +0800
changeset 361967 499cb7e486c0de47b2cdca7a6116045ca3a069c7
parent 361966 3297397ead64b04a1c92cb38cd367fa20b8e68ff
child 361968 b5cb93142cb0ba66bb76256faea06316a439724d
push id31952
push usercbook@mozilla.com
push dateFri, 02 Jun 2017 12:17:25 +0000
treeherdermozilla-central@194c009d6295 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgerald
bugs1367679
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1367679. P4 - specialize the type of mCompletionPromise according to whether chaining is supported. r=gerald 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/