--- 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/