--- a/xpcom/threads/MozPromise.h
+++ b/xpcom/threads/MozPromise.h
@@ -327,24 +327,35 @@ protected:
return NS_OK;
}
private:
RefPtr<ThenValueBase> mThenValue;
RefPtr<MozPromise> mPromise;
};
- explicit ThenValueBase(AbstractThread* aResponseTarget, const char* aCallSite)
- : mResponseTarget(aResponseTarget), mCallSite(aCallSite) {}
+ ThenValueBase(AbstractThread* aResponseTarget,
+ const char* aCallSite,
+ bool aInitCompletionPromise = false)
+ : mResponseTarget(aResponseTarget)
+ , mCallSite(aCallSite)
+ , mInitCompletionPromise(aInitCompletionPromise)
+ {
+ if (mInitCompletionPromise) {
+ mCompletionPromise = new MozPromise::Private(
+ "<completion promise>", true /* aIsCompletionPromise */);
+ }
+ }
MozPromise* CompletionPromise() override
{
- MOZ_DIAGNOSTIC_ASSERT(mResponseTarget->IsCurrentThreadIn());
+ MOZ_DIAGNOSTIC_ASSERT(mInitCompletionPromise ||
+ mResponseTarget->IsCurrentThreadIn());
MOZ_DIAGNOSTIC_ASSERT(!Request::mComplete);
- if (!mCompletionPromise) {
+ if (!mInitCompletionPromise && !mCompletionPromise) {
mCompletionPromise = new MozPromise::Private(
"<completion promise>", true /* aIsCompletionPromise */);
}
return mCompletionPromise;
}
void AssertIsDead() override
{
@@ -428,16 +439,20 @@ protected:
// Declaring RefPtr<MozPromise::Private> here causes build failures
// on MSVC because MozPromise::Private is only forward-declared at this
// point. This hack can go away when we inline-declare MozPromise::Private,
// which is blocked on the B2G ICS compiler being too old.
RefPtr<MozPromise> mCompletionPromise;
const char* mCallSite;
+
+ // True if mCompletionPromise should be initialized in the constructor
+ // to make CompletionPromise() thread-safe.
+ const bool mInitCompletionPromise;
};
/*
* 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>
@@ -479,18 +494,18 @@ protected:
}
template<typename ThisType, typename ResolveMethodType, typename RejectMethodType>
class MethodThenValue : public ThenValueBase
{
public:
MethodThenValue(AbstractThread* aResponseTarget, ThisType* aThisVal,
ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod,
- const char* aCallSite)
- : ThenValueBase(aResponseTarget, aCallSite)
+ const char* aCallSite, bool aInitCompletionPromise = false)
+ : ThenValueBase(aResponseTarget, aCallSite, aInitCompletionPromise)
, mThisVal(aThisVal)
, mResolveMethod(aResolveMethod)
, mRejectMethod(aRejectMethod) {}
virtual void Disconnect() override
{
ThenValueBase::Disconnect();
@@ -528,18 +543,19 @@ protected:
// NB: We could use std::function here instead of a template if it were supported. :-(
template<typename ResolveFunction, typename RejectFunction>
class FunctionThenValue : public ThenValueBase
{
public:
FunctionThenValue(AbstractThread* aResponseTarget,
ResolveFunction&& aResolveFunction,
RejectFunction&& aRejectFunction,
- const char* aCallSite)
- : ThenValueBase(aResponseTarget, aCallSite)
+ const char* aCallSite,
+ bool aInitCompletionPromise = false)
+ : ThenValueBase(aResponseTarget, aCallSite, aInitCompletionPromise)
{
mResolveFunction.emplace(Move(aResolveFunction));
mRejectFunction.emplace(Move(aRejectFunction));
}
virtual void Disconnect() override
{
ThenValueBase::Disconnect();
@@ -598,34 +614,69 @@ public:
mThenValues.AppendElement(aThenValue);
}
}
public:
template<typename ThisType, typename ResolveMethodType, typename RejectMethodType>
RefPtr<Request> Then(AbstractThread* aResponseThread, const char* aCallSite, ThisType* aThisVal,
- ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod)
+ ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod)
{
RefPtr<ThenValueBase> thenValue = new MethodThenValue<ThisType, ResolveMethodType, RejectMethodType>(
aResponseThread, aThisVal, aResolveMethod, aRejectMethod, aCallSite);
ThenInternal(aResponseThread, thenValue, aCallSite);
return thenValue.forget(); // Implicit conversion from already_AddRefed<ThenValueBase> to RefPtr<Request>.
}
template<typename ResolveFunction, typename RejectFunction>
RefPtr<Request> Then(AbstractThread* aResponseThread, const char* aCallSite,
- ResolveFunction&& aResolveFunction, RejectFunction&& aRejectFunction)
+ ResolveFunction&& aResolveFunction, RejectFunction&& aRejectFunction)
{
RefPtr<ThenValueBase> thenValue = new FunctionThenValue<ResolveFunction, RejectFunction>(aResponseThread,
Move(aResolveFunction), Move(aRejectFunction), aCallSite);
ThenInternal(aResponseThread, thenValue, aCallSite);
return thenValue.forget(); // Implicit conversion from already_AddRefed<ThenValueBase> to RefPtr<Request>.
}
+ // Equivalent to Then(target, ...)->CompletionPromise()
+ // without the restriction that CompletionPromise() must be called on the
+ // |target| thread. So ThenPromise() can be called on any thread as Then().
+ // The syntax is close to JS promise and makes promise chaining easier
+ // where you can do: p->ThenPromise()->ThenPromise()->ThenPromise();
+ //
+ // Note you would have to call Then() instead when the result needs to be held
+ // by a MozPromiseRequestHolder for future disconnection.
+ //
+ // TODO: replace Then()->CompletionPromise() with ThenPromise() and
+ // stop exposing CompletionPromise() to the client code.
+ template<typename ThisType, typename ResolveMethodType, typename RejectMethodType>
+ MOZ_MUST_USE RefPtr<MozPromise>
+ ThenPromise(AbstractThread* aResponseThread, const char* aCallSite, ThisType* aThisVal,
+ ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod)
+ {
+ using ThenType = MethodThenValue<ThisType, ResolveMethodType, RejectMethodType>;
+ RefPtr<ThenValueBase> thenValue = new ThenType(aResponseThread, aThisVal, aResolveMethod,
+ aRejectMethod, aCallSite, true /* aInitCompletionPromise */);
+ ThenInternal(aResponseThread, thenValue, aCallSite);
+ return thenValue->CompletionPromise();
+ }
+
+ template<typename ResolveFunction, typename RejectFunction>
+ MOZ_MUST_USE RefPtr<MozPromise>
+ ThenPromise(AbstractThread* aResponseThread, const char* aCallSite,
+ ResolveFunction&& aResolveFunction, RejectFunction&& aRejectFunction)
+ {
+ using ThenType = FunctionThenValue<ResolveFunction, RejectFunction>;
+ RefPtr<ThenValueBase> thenValue = new ThenType(aResponseThread, Move(aResolveFunction),
+ Move(aRejectFunction), aCallSite, true /* aInitCompletionPromise */);
+ ThenInternal(aResponseThread, thenValue, aCallSite);
+ return thenValue->CompletionPromise();
+ }
+
void ChainTo(already_AddRefed<Private> aChainedPromise, const char* aCallSite)
{
MutexAutoLock lock(mMutex);
MOZ_DIAGNOSTIC_ASSERT(!IsExclusive || !mHaveRequest);
mHaveRequest = true;
RefPtr<Private> chainedPromise = aChainedPromise;
PROMISE_LOG("%s invoking Chain() [this=%p, chainedPromise=%p, isPending=%d]",
aCallSite, this, chainedPromise.get(), (int) IsPending());