Bug 1370005. P1 - backout bug 1367679. r=gerald
authorJW Wang <jwwang@mozilla.com>
Mon, 05 Jun 2017 14:03:34 +0800
changeset 410401 02b76f664ebba4cde6d94646b2a7dda9c98a9831
parent 410400 a3c3e847db4a32c61ea2229855f1c0d02ae7201f
child 410402 2b23c8e294e39d57c076d66f563fa1d0fda9e5e5
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgerald
bugs1370005, 1367679
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 1370005. P1 - backout bug 1367679. r=gerald MozReview-Commit-ID: 4SUCinaz3Cj
dom/media/gtest/TestMozPromise.cpp
xpcom/threads/MozPromise.h
--- a/dom/media/gtest/TestMozPromise.cpp
+++ b/dom/media/gtest/TestMozPromise.cpp
@@ -345,66 +345,9 @@ TEST(MozPromise, MoveOnlyType)
       EXPECT_TRUE(aVal.IsResolve());
       EXPECT_EQ(nullptr, aVal.ResolveValue().get());
       EXPECT_EQ(87, *val.ResolveValue());
 
       queue->BeginShutdown();
     });
 }
 
-TEST(MozPromise, HeterogeneousChaining)
-{
-  using Promise1 = MozPromise<UniquePtr<char>, bool, true>;
-  using Promise2 = MozPromise<UniquePtr<int>, bool, true>;
-  using RRValue1 = Promise1::ResolveOrRejectValue;
-  using RRValue2 = Promise2::ResolveOrRejectValue;
-
-  MozPromiseRequestHolder<Promise2> holder;
-
-  AutoTaskQueue atq;
-  RefPtr<TaskQueue> queue = atq.Queue();
-
-  RunOnTaskQueue(queue, [queue, &holder]() {
-    Promise1::CreateAndResolve(MakeUnique<char>(0), __func__)
-      ->Then(queue,
-             __func__,
-             [&holder]() {
-               holder.Disconnect();
-               return Promise2::CreateAndResolve(MakeUnique<int>(0), __func__);
-             })
-      ->Then(queue,
-             __func__,
-             []() {
-               // Shouldn't be called for we've disconnected the request.
-               EXPECT_FALSE(true);
-             })
-      ->Track(holder);
-  });
-
-  Promise1::CreateAndResolve(MakeUnique<char>(87), __func__)
-    ->Then(queue,
-           __func__,
-           [](UniquePtr<char> aVal) {
-             EXPECT_EQ(87, *aVal);
-             return Promise2::CreateAndResolve(MakeUnique<int>(94), __func__);
-           },
-           []() {
-             return Promise2::CreateAndResolve(MakeUnique<int>(95), __func__);
-           })
-    ->Then(queue,
-           __func__,
-           [](UniquePtr<int> aVal) { EXPECT_EQ(94, *aVal); },
-           []() { EXPECT_FALSE(true); });
-
-  Promise1::CreateAndResolve(MakeUnique<char>(87), __func__)
-    ->Then(queue,
-           __func__,
-           [](RRValue1&& aVal) {
-             EXPECT_EQ(87, *aVal.ResolveValue());
-             return Promise2::CreateAndResolve(MakeUnique<int>(94), __func__);
-           })
-    ->Then(queue, __func__, [queue](RRValue2&& aVal) {
-      EXPECT_EQ(94, *aVal.ResolveValue());
-      queue->BeginShutdown();
-    });
-}
-
 #undef DO_FAIL
--- a/xpcom/threads/MozPromise.h
+++ b/xpcom/threads/MozPromise.h
@@ -76,30 +76,16 @@ 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.
@@ -512,94 +498,72 @@ protected:
     uint32_t mMagic2 = sMagic;
 #endif
   };
 
   /*
    * We create two overloads for invoking Resolve/Reject Methods so as to
    * make the resolve/reject value argument "optional".
    */
+
   template<typename ThisType, typename MethodType, typename ValueType>
-  static typename EnableIf<
-    TakesArgument<MethodType>::value,
-    typename detail::MethodTrait<MethodType>::ReturnType>::Type
-  InvokeMethod(ThisType* aThisVal, MethodType aMethod, ValueType&& aValue)
+  static typename EnableIf<ReturnTypeIs<MethodType, RefPtr<MozPromise>>::value &&
+                           TakesArgument<MethodType>::value,
+                           already_AddRefed<MozPromise>>::Type
+  InvokeCallbackMethod(ThisType* aThisVal, MethodType aMethod, ValueType&& aValue)
   {
-    return (aThisVal->*aMethod)(Forward<ValueType>(aValue));
+    return ((*aThisVal).*aMethod)(Forward<ValueType>(aValue)).forget();
   }
 
   template<typename ThisType, typename MethodType, typename ValueType>
-  static typename EnableIf<
-    !TakesArgument<MethodType>::value,
-    typename detail::MethodTrait<MethodType>::ReturnType>::Type
-  InvokeMethod(ThisType* aThisVal, MethodType aMethod, ValueType&& aValue)
+  static typename EnableIf<ReturnTypeIs<MethodType, void>::value &&
+                           TakesArgument<MethodType>::value,
+                           already_AddRefed<MozPromise>>::Type
+  InvokeCallbackMethod(ThisType* aThisVal, MethodType aMethod, ValueType&& aValue)
   {
-    return (aThisVal->*aMethod)();
+    ((*aThisVal).*aMethod)(Forward<ValueType>(aValue));
+    return nullptr;
   }
 
-  // Called when promise chaining is supported.
-  template<bool SupportChaining,
-           typename ThisType,
-           typename MethodType,
-           typename ValueType,
-           typename CompletionPromiseType>
-  static typename EnableIf<SupportChaining, void>::Type InvokeCallbackMethod(
-    ThisType* aThisVal,
-    MethodType aMethod,
-    ValueType&& aValue,
-    CompletionPromiseType&& aCompletionPromise)
+  template<typename ThisType, typename MethodType, typename ValueType>
+  static typename EnableIf<ReturnTypeIs<MethodType, RefPtr<MozPromise>>::value &&
+                           !TakesArgument<MethodType>::value,
+                           already_AddRefed<MozPromise>>::Type
+  InvokeCallbackMethod(ThisType* aThisVal, MethodType aMethod, ValueType&& aValue)
   {
-    auto p = InvokeMethod(aThisVal, aMethod, Forward<ValueType>(aValue));
-    if (aCompletionPromise) {
-      p->ChainTo(aCompletionPromise.forget(), "<chained completion promise>");
-    }
+    return ((*aThisVal).*aMethod)().forget();
   }
 
-  // Called when promise chaining is not supported.
-  template<bool SupportChaining,
-           typename ThisType,
-           typename MethodType,
-           typename ValueType,
-           typename CompletionPromiseType>
-  static typename EnableIf<!SupportChaining, void>::Type InvokeCallbackMethod(
-    ThisType* aThisVal,
-    MethodType aMethod,
-    ValueType&& aValue,
-    CompletionPromiseType&& aCompletionPromise)
+  template<typename ThisType, typename MethodType, typename ValueType>
+  static typename EnableIf<ReturnTypeIs<MethodType, void>::value &&
+                           !TakesArgument<MethodType>::value,
+                           already_AddRefed<MozPromise>>::Type
+  InvokeCallbackMethod(ThisType* aThisVal, MethodType aMethod, ValueType&& aValue)
   {
-    MOZ_DIAGNOSTIC_ASSERT(
-      !aCompletionPromise,
-      "Can't do promise chaining for a non-promise-returning method.");
-    InvokeMethod(aThisVal, aMethod, Forward<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 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;
+    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)
@@ -622,56 +586,54 @@ protected:
   protected:
     MozPromiseBase* CompletionPromise() const override
     {
       return mCompletionPromise;
     }
 
     void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) override
     {
+      RefPtr<MozPromise> result;
       if (aValue.IsResolve()) {
-        InvokeCallbackMethod<SupportChaining::value>(
-          mThisVal.get(),
-          mResolveMethod,
-          MaybeMove(aValue.ResolveValue()),
-          Move(mCompletionPromise));
+        result = InvokeCallbackMethod(
+          mThisVal.get(), mResolveMethod, MaybeMove(aValue.ResolveValue()));
       } else {
-        InvokeCallbackMethod<SupportChaining::value>(
-          mThisVal.get(),
-          mRejectMethod,
-          MaybeMove(aValue.RejectValue()),
-          Move(mCompletionPromise));
+        result = InvokeCallbackMethod(
+          mThisVal.get(), mRejectMethod, MaybeMove(aValue.RejectValue()));
       }
 
       // Null out mThisVal after invoking the callback so that any references are
       // released predictably on the dispatch thread. Otherwise, it would be
       // released on whatever thread last drops its reference to the ThenValue,
       // which may or may not be ok.
       mThisVal = nullptr;
+
+      MOZ_DIAGNOSTIC_ASSERT(
+        !mCompletionPromise || result,
+        "Can't do promise chaining for a non-promise-returning method.");
+
+      if (mCompletionPromise && result) {
+        result->ChainTo(mCompletionPromise.forget(),
+                        "<chained completion promise>");
+      }
     }
 
   private:
     RefPtr<ThisType> mThisVal; // Only accessed and refcounted on dispatch thread.
     ResolveMethodType mResolveMethod;
     RejectMethodType mRejectMethod;
-    RefPtr<typename PromiseType::Private> mCompletionPromise;
+    RefPtr<Private> mCompletionPromise;
   };
 
   template<typename ThisType, typename ResolveRejectMethodType>
   class ThenValue<ThisType*, ResolveRejectMethodType> : public ThenValueBase
   {
     friend class ThenCommand<ThenValue>;
-
-    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;
+    using SupportChaining =
+      ReturnTypeIs<ResolveRejectMethodType, RefPtr<MozPromise>>;
 
   public:
     ThenValue(AbstractThread* aResponseTarget,
               ThisType* aThisVal,
               ResolveRejectMethodType aResolveRejectMethod,
               const char* aCallSite)
       : ThenValueBase(aResponseTarget, aCallSite)
       , mThisVal(aThisVal)
@@ -691,50 +653,50 @@ protected:
   protected:
     MozPromiseBase* CompletionPromise() const override
     {
       return mCompletionPromise;
     }
 
     void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) override
     {
-      InvokeCallbackMethod<SupportChaining::value>(mThisVal.get(),
-                                                   mResolveRejectMethod,
-                                                   MaybeMove(aValue),
-                                                   Move(mCompletionPromise));
+      RefPtr<MozPromise> result = InvokeCallbackMethod(
+        mThisVal.get(), mResolveRejectMethod, MaybeMove(aValue));
 
       // Null out mThisVal after invoking the callback so that any references are
       // released predictably on the dispatch thread. Otherwise, it would be
       // released on whatever thread last drops its reference to the ThenValue,
       // which may or may not be ok.
       mThisVal = nullptr;
+
+      MOZ_DIAGNOSTIC_ASSERT(
+        !mCompletionPromise || result,
+        "Can't do promise chaining for a non-promise-returning method.");
+
+      if (mCompletionPromise && result) {
+        result->ChainTo(mCompletionPromise.forget(),
+                        "<chained completion promise>");
+      }
     }
 
   private:
     RefPtr<ThisType> mThisVal; // Only accessed and refcounted on dispatch thread.
     ResolveRejectMethodType mResolveRejectMethod;
-    RefPtr<typename PromiseType::Private> mCompletionPromise;
+    RefPtr<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 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;
+    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)
     {
@@ -762,56 +724,56 @@ protected:
 
     void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) override
     {
       // Note: The usage of InvokeCallbackMethod here requires that
       // ResolveFunction/RejectFunction are capture-lambdas (i.e. anonymous
       // classes with ::operator()), since it allows us to share code more easily.
       // We could fix this if need be, though it's quite easy to work around by
       // just capturing something.
+      RefPtr<MozPromise> result;
       if (aValue.IsResolve()) {
-        InvokeCallbackMethod<SupportChaining::value>(
-          mResolveFunction.ptr(),
-          &ResolveFunction::operator(),
-          MaybeMove(aValue.ResolveValue()),
-          Move(mCompletionPromise));
+        result = InvokeCallbackMethod(mResolveFunction.ptr(),
+                                      &ResolveFunction::operator(),
+                                      MaybeMove(aValue.ResolveValue()));
       } else {
-        InvokeCallbackMethod<SupportChaining::value>(
-          mRejectFunction.ptr(),
-          &RejectFunction::operator(),
-          MaybeMove(aValue.RejectValue()),
-          Move(mCompletionPromise));
+        result = InvokeCallbackMethod(mRejectFunction.ptr(),
+                                      &RejectFunction::operator(),
+                                      MaybeMove(aValue.RejectValue()));
       }
 
       // Destroy callbacks after invocation so that any references in closures are
       // 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.
       mResolveFunction.reset();
       mRejectFunction.reset();
+
+      MOZ_DIAGNOSTIC_ASSERT(
+        !mCompletionPromise || result,
+        "Can't do promise chaining for a non-promise-returning method.");
+
+      if (mCompletionPromise && result) {
+        result->ChainTo(mCompletionPromise.forget(),
+                        "<chained completion promise>");
+      }
     }
 
   private:
     Maybe<ResolveFunction> mResolveFunction; // Only accessed and deleted on dispatch thread.
     Maybe<RejectFunction> mRejectFunction; // Only accessed and deleted on dispatch thread.
-    RefPtr<typename PromiseType::Private> mCompletionPromise;
+    RefPtr<Private> mCompletionPromise;
   };
 
   template<typename ResolveRejectFunction>
   class ThenValue<ResolveRejectFunction> : public ThenValueBase
   {
     friend class ThenCommand<ThenValue>;
-
-    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;
+    using SupportChaining =
+      ReturnTypeIs<ResolveRejectFunction, RefPtr<MozPromise>>;
 
   public:
     ThenValue(AbstractThread* aResponseTarget,
               ResolveRejectFunction&& aResolveRejectFunction,
               const char* aCallSite)
       : ThenValueBase(aResponseTarget, aCallSite)
     {
       mResolveRejectFunction.emplace(Move(aResolveRejectFunction));
@@ -836,32 +798,40 @@ protected:
 
     void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) override
     {
       // Note: The usage of InvokeCallbackMethod here requires that
       // ResolveRejectFunction is capture-lambdas (i.e. anonymous
       // classes with ::operator()), since it allows us to share code more easily.
       // We could fix this if need be, though it's quite easy to work around by
       // just capturing something.
-      InvokeCallbackMethod<SupportChaining::value>(
-        mResolveRejectFunction.ptr(),
-        &ResolveRejectFunction::operator(),
-        MaybeMove(aValue),
-        Move(mCompletionPromise));
+      RefPtr<MozPromise> result =
+        InvokeCallbackMethod(mResolveRejectFunction.ptr(),
+                             &ResolveRejectFunction::operator(),
+                             MaybeMove(aValue));
 
       // Destroy callbacks after invocation so that any references in closures are
       // 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();
+
+      MOZ_DIAGNOSTIC_ASSERT(
+        !mCompletionPromise || result,
+        "Can't do promise chaining for a non-promise-returning method.");
+
+      if (mCompletionPromise && result) {
+        result->ChainTo(mCompletionPromise.forget(),
+                        "<chained completion promise>");
+      }
     }
 
   private:
     Maybe<ResolveRejectFunction> mResolveRejectFunction; // Only accessed and deleted on dispatch thread.
-    RefPtr<typename PromiseType::Private> mCompletionPromise;
+    RefPtr<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);
@@ -885,24 +855,18 @@ 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)
@@ -919,40 +883,40 @@ protected:
       if (mThenValue) {
         mReceiver->ThenInternal(mResponseThread, mThenValue, mCallSite);
       }
     }
 
     // Allow RefPtr<MozPromise> p = somePromise->Then();
     //       p->Then(thread1, ...);
     //       p->Then(thread2, ...);
-    operator RefPtr<PromiseType>()
+    template <typename...>
+    operator RefPtr<MozPromise>()
     {
       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<Private> p =
-        new Private("<completion promise>", true /* aIsCompletionPromise */);
+      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;
     }
 
-    template<typename... Ts>
+    template <typename... Ts>
     auto Then(Ts&&... aArgs)
-      -> decltype(DeclVal<PromiseType>().Then(Forward<Ts>(aArgs)...))
+      -> decltype(DeclVal<MozPromise>().Then(Forward<Ts>(aArgs)...))
     {
-      return static_cast<RefPtr<PromiseType>>(*this)->Then(
-        Forward<Ts>(aArgs)...);
+      return static_cast<RefPtr<MozPromise>>(*this)->Then(Forward<Ts>(aArgs)...);
     }
 
     void Track(MozPromiseRequestHolder<MozPromise>& aRequestHolder)
     {
       RefPtr<ThenValueType> thenValue = mThenValue.forget();
       mReceiver->ThenInternal(mResponseThread, thenValue, mCallSite);
       aRequestHolder.Track(thenValue.forget());
     }
@@ -1334,16 +1298,26 @@ 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/