/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* * 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. * * MozPromises attempt to mirror the spirit of JS Promises to the extent that * is possible (and desirable) in C++. While the intent is that MozPromises * feel familiar to programmers who are accustomed to their JS-implemented * cousin, we don't shy away from imposing restrictions and adding features that * make sense for the use cases we encounter. * * A MozPromise is ThreadSafe, and may be ->Then()ed on any thread. The Then() * call accepts resolve and reject callbacks, and returns a magic object which * will be implicitly converted to a MozPromise::Request or a MozPromise object * depending on how the return value is used. The magic object serves several * purposes for the consumer. * * (1) When converting to a MozPromise::Request, it allows the caller to * cancel the delivery of the resolve/reject value if it has not already * occurred, via Disconnect() (this must be done on the target thread to * avoid racing). * * (2) When converting to a MozPromise (which is called a completion promise), * it allows promise chaining so ->Then() can be called again to attach * more resolve and reject callbacks. If the resolve/reject callback * returns a new MozPromise, that promise is chained to the completion * promise, such that its resolve/reject value will be forwarded along * when it arrives. If the resolve/reject callback returns void, the * completion promise is resolved/rejected with the same value that was * passed to the callback. * * The MozPromise APIs skirt traditional XPCOM convention by returning nsRefPtrs * (rather than already_AddRefed) from various methods. This is done to allow * elegant chaining of calls without cluttering up the code with intermediate * variables, and without introducing separate API variants for callers that * want a return value (from, say, ->Then()) from those that don't. * * When IsExclusive is true, the MozPromise does a release-mode assertion that * there is at most one call to either Then(...) or ChainTo(...).
*/
class MozPromiseBase : public MozPromiseRefcountable { public: virtualvoid AssertIsDead() = 0;
};
template <typename T> class MozPromiseHolder; template <typename T> class MozPromiseRequestHolder; template <typename ResolveValueT, typename RejectValueT, bool IsExclusive> class MozPromise : public MozPromiseBase { staticconst uint32_t sMagic = 0xcecace11;
// Return a |T&&| to enable move when IsExclusive is true or // a |const T&| to enforce copy otherwise. template <typename T, typename R = std::conditional_t<IsExclusive, T&&, const T&>> static R MaybeMove(T& aX) { returnstatic_cast<R>(aX);
}
public: using ResolveValueType = ResolveValueT; using RejectValueType = RejectValueT; class ResolveOrRejectValue { public: template <typename ResolveValueType_> void SetResolve(ResolveValueType_&& aResolveValue) {
MOZ_ASSERT(IsNothing());
mValue = Storage(VariantIndex<ResolveIndex>{},
std::forward<ResolveValueType_>(aResolveValue));
}
protected: // MozPromise is the public type, and never constructed directly. Construct // a MozPromise::Private, defined below.
MozPromise(StaticString aCreationSite, bool aIsCompletionPromise)
: mCreationSite(aCreationSite),
mMutex("MozPromise Mutex"),
mHaveRequest(false),
mIsCompletionPromise(aIsCompletionPromise) #ifdef PROMISE_DEBUG
,
mMagic4(&mMutex) #endif
{
PROMISE_LOG("%s creating MozPromise (%p)", mCreationSite.get(), this);
}
public: // MozPromise::Private allows us to separate the public interface (upon which // consumers of the promise may invoke methods like Then()) from the private // interface (upon which the creator of the promise may invoke Resolve() or // Reject()). APIs should create and store a MozPromise::Private (usually // via a MozPromiseHolder), and return a MozPromise to consumers. // // NB: We can include the definition of this class inline once B2G ICS is // gone. classPrivate;
template <typename ResolveValueType_>
[[nodiscard]] static RefPtr<MozPromise> CreateAndResolve(
ResolveValueType_&& aResolveValue, StaticString aResolveSite) {
static_assert(std::is_convertible_v<ResolveValueType_, ResolveValueT>, "Resolve() argument must be implicitly convertible to " "MozPromise's ResolveValueT");
RefPtr<typename MozPromise::Private> p = new MozPromise::Private(aResolveSite);
p->Resolve(std::forward<ResolveValueType_>(aResolveValue), aResolveSite); return p;
}
template <typename RejectValueType_>
[[nodiscard]] static RefPtr<MozPromise> CreateAndReject(
RejectValueType_&& aRejectValue, StaticString aRejectSite) {
static_assert(std::is_convertible_v<RejectValueType_, RejectValueT>, "Reject() argument must be implicitly convertible to " "MozPromise's RejectValueT");
RefPtr<typename MozPromise::Private> p = new MozPromise::Private(aRejectSite);
p->Reject(std::forward<RejectValueType_>(aRejectValue), aRejectSite); return p;
}
// Trying to pass ResolveOrRejectValue by value fails static analysis checks, // so we need to use either a const& or an rvalue reference, depending on // whether IsExclusive is true or not. using ResolveOrRejectValueParam =
std::conditional_t<IsExclusive, ResolveOrRejectValue&&, const ResolveOrRejectValue&>;
using ResolveValueTypeParam =
std::conditional_t<IsExclusive, ResolveValueType&&, const ResolveValueType&>;
using RejectValueTypeParam =
std::conditional_t<IsExclusive, RejectValueType&&, const RejectValueType&>;
class AllSettledPromiseHolder : public MozPromiseRefcountable { public: explicit AllSettledPromiseHolder(size_t aDependentPromises)
: mPromise(newtypename AllSettledPromiseType::Private(__func__)),
mOutstandingPromises(aDependentPromises) {
MOZ_ASSERT(aDependentPromises > 0);
mValues.SetLength(aDependentPromises);
}
protected: /* * A ThenValue tracks a single consumer waiting on the promise. When a * consumer invokes promise->Then(...), a ThenValue is created. Once the * Promise is resolved or rejected, a {Resolve,Reject}Runnable is dispatched, * which invokes the resolve/reject method and then deletes the ThenValue.
*/ class ThenValueBase : public Request { friendclass MozPromise; staticconst uint32_t sMagic = 0xfadece11;
public: class ResolveOrRejectRunnable final
: public PrioritizableCancelableRunnable { public:
ResolveOrRejectRunnable(ThenValueBase* aThenValue, MozPromise* aPromise)
: PrioritizableCancelableRunnable(
aPromise->mPriority, "MozPromise::ThenValueBase::ResolveOrRejectRunnable"),
mThenValue(aThenValue),
mPromise(aPromise) {
MOZ_DIAGNOSTIC_ASSERT(!mPromise->IsPending());
}
~ResolveOrRejectRunnable() { if (mThenValue) {
mThenValue->AssertIsDead();
}
}
void AssertIsDead() {
PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic); // We want to assert that this ThenValues is dead - that is to say, that // there are no consumers waiting for the result. In the case of a normal // ThenValue, we check that it has been disconnected, which is the way // that the consumer signals that it no longer wishes to hear about the // result. If this ThenValue has a completion promise (which is mutually // exclusive with being disconnectable), we recursively assert that every // ThenValue associated with the completion promise is dead. if (MozPromiseBase* p = CompletionPromise()) {
p->AssertIsDead();
} else { #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED if (MOZ_UNLIKELY(!Request::mDisconnected)) {
MOZ_CRASH_UNSAFE_PRINTF( "MozPromise::ThenValue created from '%s' destroyed without being " "either disconnected, resolved, or rejected (dispatchRv: %s)",
mCallSite.get(),
mDispatchRv ? GetStaticErrorName(*mDispatchRv)
: "not dispatched");
} #endif
}
}
if (aPromise->mUseDirectTaskDispatch &&
mResponseTarget->IsOnCurrentThread()) {
PROMISE_LOG( "ThenValue::Dispatch dispatch task via direct task queue [this=%p]", this);
nsCOMPtr<nsIDirectTaskDispatcher> dispatcher =
do_QueryInterface(mResponseTarget); if (dispatcher) {
SetDispatchRv(dispatcher->DispatchDirectTask(r.forget())); return;
}
NS_WARNING(
nsPrintfCString( "Direct Task dispatching not available for thread \"%s\"",
PR_GetThreadName(PR_GetCurrentThread()))
.get());
MOZ_DIAGNOSTIC_ASSERT( false, "mResponseTarget must implement nsIDirectTaskDispatcher for direct " "task dispatching");
}
// Promise consumers are allowed to disconnect the Request object and // then shut down the thread or task queue that the promise result would // be dispatched on. So we unfortunately can't assert that promise // dispatch succeeds. :-( // We do record whether or not it succeeds so that if the ThenValueBase is // then destroyed and it was not disconnected, we can include that // information in the assertion message.
SetDispatchRv(mResponseTarget->Dispatch(r.forget()));
}
// We could support rejecting the completion promise on disconnection, but // then we'd need to have some sort of default reject value. The use cases // of disconnection and completion promise chaining seem pretty // orthogonal, so let's use assert against it.
MOZ_DIAGNOSTIC_ASSERT(!CompletionPromise());
}
template <typename PromiseType> staticvoid MaybeChain(PromiseType* aFrom,
RefPtr<typename PromiseType::Private>&& aTo) { if (aTo) {
MOZ_DIAGNOSTIC_ASSERT(
aFrom, "Can't do promise chaining for a non-promise-returning method.");
aFrom->ChainTo(aTo.forget(), "");
}
}
template <typename> class ThenCommand;
template <typename...> class ThenValue;
template <typename ThisType, typename ResolveMethodType, typename RejectMethodType> class ThenValue<ThisType*, ResolveMethodType, RejectMethodType>
: public ThenValueBase { friendclass ThenCommand<ThenValue>;
using R1 = RemoveSmartPointer<MethodReturnType<ResolveMethodType>>; using R2 = RemoveSmartPointer<MethodReturnType<RejectMethodType>>;
constexpr staticbool SupportChaining =
IsMozPromise<R1> && std::is_same_v<R1, R2>;
// Fall back to MozPromise when promise chaining is not supported to make // code compile. using PromiseType = std::conditional_t<SupportChaining, R1, MozPromise>;
// If a Request has been disconnected, we don't guarantee that the // resolve/reject runnable will be dispatched. Null out our refcounted // this-value now so that it's released predictably on the dispatch // thread.
mThisVal = nullptr;
}
// 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;
private:
RefPtr<ThisType>
mThisVal; // Only accessed and refcounted on dispatch thread.
ResolveMethodType mResolveMethod;
RejectMethodType mRejectMethod;
RefPtr<typename PromiseType::Private> mCompletionPromise;
};
template <typename ThisType, typename ResolveRejectMethodType> class ThenValue<ThisType*, ResolveRejectMethodType> : public ThenValueBase { friendclass ThenCommand<ThenValue>;
using R1 = RemoveSmartPointer<MethodReturnType<ResolveRejectMethodType>>;
constexpr staticbool SupportChaining = IsMozPromise<R1>;
// Fall back to MozPromise when promise chaining is not supported to make // code compile. using PromiseType = std::conditional_t<SupportChaining, R1, MozPromise>;
// If a Request has been disconnected, we don't guarantee that the // resolve/reject runnable will be dispatched. Null out our refcounted // this-value now so that it's released predictably on the dispatch // thread.
mThisVal = nullptr;
}
// 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;
private:
RefPtr<ThisType>
mThisVal; // Only accessed and refcounted on dispatch thread.
ResolveRejectMethodType mResolveRejectMethod;
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 { friendclass ThenCommand<ThenValue>;
using R1 = RemoveSmartPointer<MethodReturnType<ResolveFunction>>; using R2 = RemoveSmartPointer<MethodReturnType<RejectFunction>>;
constexpr staticbool SupportChaining =
IsMozPromise<R1> && std::is_same_v<R1, R2>;
// Fall back to MozPromise when promise chaining is not supported to make // code compile. using PromiseType = std::conditional_t<SupportChaining, R1, MozPromise>;
// If a Request has been disconnected, we don't guarantee that the // resolve/reject runnable will be dispatched. Destroy our callbacks // now so that any references in closures are released predictable on // the dispatch thread.
mResolveFunction.reset();
mRejectFunction.reset();
}
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<PromiseType> result =
aValue.IsResolve()
? InvokeCallbackMethod<SupportChaining, PromiseType>(
mResolveFunction.ptr(), &ResolveFunction::operator(),
MaybeMove(aValue.ResolveValue()))
: InvokeCallbackMethod<SupportChaining, PromiseType>(
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();
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;
};
template <typename ResolveRejectFunction> class ThenValue<ResolveRejectFunction> : public ThenValueBase { friendclass ThenCommand<ThenValue>;
using R1 = RemoveSmartPointer<MethodReturnType<ResolveRejectFunction>>;
constexpr staticbool SupportChaining = IsMozPromise<R1>;
// Fall back to MozPromise when promise chaining is not supported to make // code compile. using PromiseType = std::conditional_t<SupportChaining, R1, MozPromise>;
// If a Request has been disconnected, we don't guarantee that the // resolve/reject runnable will be dispatched. Destroy our callbacks // now so that any references in closures are released predictable on // the dispatch thread.
mResolveRejectFunction.reset();
}
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.
RefPtr<PromiseType> result =
InvokeCallbackMethod<SupportChaining, PromiseType>(
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();
void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) override { // Note that promise-chaining is always supported here; this function can // only transform from MozPromise<A, B, k> to MozPromise<A2, B, k>. auto value = MaybeMove(aValue); typename PromiseType::ResolveOrRejectValue output;
if (value.IsResolve()) {
output.SetResolve((*mResolveFunction)(std::move(value.ResolveValue())));
} else {
output.SetReject(std::move(value.RejectValue()));
}
if (mCompletionPromise) {
mCompletionPromise->ResolveOrReject(std::move(output),
ThenValueBase::mCallSite);
}
}
void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) override { // Note that promise-chaining is always supported here; this function can // only transform from MozPromise<A, B, k> to MozPromise<A, B2, k>. auto value = MaybeMove(aValue); typename PromiseType::ResolveOrRejectValue output;
if (value.IsResolve()) {
output.SetResolve(std::move(value.ResolveValue()));
} else {
output.SetReject((*mRejectFunction)(std::move(value.RejectValue())));
}
if (mCompletionPromise) {
mCompletionPromise->ResolveOrReject(std::move(output),
ThenValueBase::mCallSite);
}
}
protected: /* * A command object to store all information needed to make a request to the * promise. This allows us to delay the request until further use is known * (whether it is ->Then() again for more promise chaining or ->Track() to * terminate chaining and issue the request). * * This allows a unified syntax for promise chaining and disconnection, and * feels more like its JS counterpart. * * Note that a ThenCommand is always exclusive, even if its source or result * promises are not. To attach multiple continuations, explicitly convert it * to a promise first.
*/ template <typename ThenValueType> class MOZ_TEMPORARY_CLASS ThenCommand { // Allow Promise1::ThenCommand to access the private constructor // Promise2::ThenCommand(ThenCommand&&). template <typename, typename, bool> friendclass MozPromise;
using PromiseType = typename ThenValueType::PromiseType; usingPrivate = typename PromiseType::Private;
public:
~ThenCommand() { // Issue the request now if the return value of Then() is not used. if (mThenValue) {
mReceiver->ThenInternal(mThenValue.forget(), mCallSite);
}
}
// Allow RefPtr<MozPromise> p = somePromise->Then(); // p->Then(thread1, ...); // p->Then(thread2, ...); operator RefPtr<PromiseType>() {
static_assert(
ThenValueType::SupportChaining, "The resolve/reject callback needs to return a RefPtr " "in order to do promise chaining.");
// mCompletionPromise must be created before ThenInternal() to avoid race.
RefPtr<Private> p = newPrivate("", true/* aIsCompletionPromise */);
mThenValue->mCompletionPromise = p; // Note ThenInternal() might nullify mCompletionPromise before return. // So we need to return p instead of mCompletionPromise.
mReceiver->ThenInternal(mThenValue.forget(), mCallSite); return p;
}
// Allow calling ->Then() again for more promise chaining or ->Track() to // end chaining and track the request for future disconnection.
ThenCommand* operator->() { returnthis; }
// Shorthand for a `Then` which simply forwards the reject-value, but performs // some additional work with the resolve-value. template <typename Function> auto Map(nsISerialEventTarget* aResponseTarget, StaticString aCallSite,
Function&& function) {
RefPtr<MapValue<Function>> thenValue = new MapValue<Function>(
aResponseTarget, std::forward<Function>(function), aCallSite); return ThenCommand<MapValue<Function>>(aCallSite, thenValue.forget(), this);
}
// Shorthand for a `Then` which simply forwards the resolve-value, but // performs some additional work with the reject-value. template <typename Function> auto MapErr(nsISerialEventTarget* aResponseTarget, StaticString aCallSite,
Function&& function) {
RefPtr<MapErrValue<Function>> thenValue = new MapErrValue<Function>(
aResponseTarget, std::forward<Function>(function), aCallSite); return ThenCommand<MapErrValue<Function>>(aCallSite, thenValue.forget(), this);
}
// We want to use the same type of dispatching method with the chained // promises.
// We need to ensure that the UseSynchronousTaskDispatch branch isn't taken // at compilation time to ensure we're not triggering the static_assert in // UseSynchronousTaskDispatch method. if constexpr (IsExclusive) ensures // that. if (mUseDirectTaskDispatch) {
chainedPromise->UseDirectTaskDispatch(aCallSite);
} elseif constexpr (IsExclusive) { if (mUseSynchronousTaskDispatch) {
chainedPromise->UseSynchronousTaskDispatch(aCallSite);
}
} else {
chainedPromise->SetTaskPriority(mPriority, aCallSite);
}
if (!IsPending()) {
ForwardTo(chainedPromise);
} else {
mChainedPromises.AppendElement(chainedPromise);
}
}
#ifdef MOZ_WIDGET_ANDROID // Creates a C++ MozPromise from its Java counterpart, GeckoResult.
[[nodiscard]] static RefPtr<MozPromise> FromGeckoResult(
java::GeckoResult::Param aGeckoResult) { using jni::GeckoResultCallback;
RefPtr<Private> p = newPrivate("GeckoResult Glue", false); auto resolve = GeckoResultCallback::CreateAndAttach<ResolveValueType>(
[p](ResolveValueType&& aArg) {
p->Resolve(MaybeMove(aArg), __func__);
}); auto reject = GeckoResultCallback::CreateAndAttach<RejectValueType>(
[p](RejectValueType&& aArg) { p->Reject(MaybeMove(aArg), __func__); });
aGeckoResult->NativeThen(resolve, reject); return p;
} #endif
// Note we expose the function AssertIsDead() instead of IsDead() since // checking IsDead() is a data race in the situation where the request is not // dead. Therefore we enforce the form |Assert(IsDead())| by exposing // AssertIsDead() only. void AssertIsDead() override {
PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic &&
mMagic3 == sMagic && mMagic4 == &mMutex);
MutexAutoLock lock(mMutex); for (auto&& then : mThenValues) {
then->AssertIsDead();
} for (auto&& chained : mChainedPromises) {
chained->AssertIsDead();
}
}
protected: // NOTE: Methods like `IsPending()` and `Value()` are intentionally marked as // `protected`, as they are not safe to call from outside of the MozPromise // code. The only way to inspect the state of a MozPromise is intentionally // to call `->Then` on the promise, and receive an async callback. // // MozPromise has somewhat complex locking and thread safety properties which // can't be extended outside of the code in this file. bool IsPending() const { return mValue.IsNothing(); }
ResolveOrRejectValue& Value() { // This method should only be called once the value has stabilized. As // such, we don't need to acquire the lock here.
MOZ_DIAGNOSTIC_ASSERT(!IsPending()); return mValue;
}
// If the caller and target are both on the same thread, run the the resolve // or reject callback synchronously. Otherwise, the task will be dispatched // via the target Dispatch method. void UseSynchronousTaskDispatch(constchar* aSite) {
static_assert(
IsExclusive, "Synchronous dispatch can only be used with exclusive promises");
PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic &&
mMagic3 == sMagic && mMagic4 == &mMutex);
MutexAutoLock lock(mMutex);
PROMISE_LOG("%s UseSynchronousTaskDispatch MozPromise (%p created at %s)",
aSite, this, mCreationSite.get());
MOZ_ASSERT(IsPending(), "A Promise must not have been already resolved or rejected to " "set dispatch state");
mUseSynchronousTaskDispatch = true;
}
// If the caller and target are both on the same thread, run the // resolve/reject callback off the direct task queue instead. This avoids a // full trip to the back of the event queue for each additional asynchronous // step when using MozPromise, and is similar (but not identical to) the // microtask semantics of JS promises. void UseDirectTaskDispatch(constchar* aSite) {
PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic &&
mMagic3 == sMagic && mMagic4 == &mMutex);
MutexAutoLock lock(mMutex);
PROMISE_LOG("%s UseDirectTaskDispatch MozPromise (%p created at %s)", aSite, this, mCreationSite.get());
MOZ_ASSERT(IsPending(), "A Promise must not have been already resolved or rejected to " "set dispatch state");
MOZ_ASSERT(!mUseSynchronousTaskDispatch, "Promise already set for synchronous dispatch");
mUseDirectTaskDispatch = true;
}
// If the resolve/reject will be handled on a thread supporting priorities, // one may want to tweak the priority of the task by passing a // nsIRunnablePriority::PRIORITY_* to SetTaskPriority. void SetTaskPriority(uint32_t aPriority, constchar* aSite) {
PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic &&
mMagic3 == sMagic && mMagic4 == &mMutex);
MutexAutoLock lock(mMutex);
PROMISE_LOG("%s TaskPriority MozPromise (%p created at %s)", aSite, this,
mCreationSite.get());
MOZ_ASSERT(IsPending(), "A Promise must not have been already resolved or rejected to " "set dispatch state");
MOZ_ASSERT(!mUseSynchronousTaskDispatch, "Promise already set for synchronous dispatch");
MOZ_ASSERT(!mUseDirectTaskDispatch, "Promise already set for direct dispatch");
mPriority = aPriority;
}
};
// A generic promise type that does the trick for simple use cases. // // Vaguely deprecated: prefer explicitly naming the resolve- and reject-type. // Additionally, prefer `mozilla::Ok` as the resolve-type if the boolean's value // is irrelevant. using GenericPromise = MozPromise<bool, nsresult, /* IsExclusive = */ true>;
// A generic, non-exclusive promise type that does the trick for simple use // cases. // // Vaguely deprecated, as above. using GenericNonExclusivePromise =
MozPromise<bool, nsresult, /* IsExclusive = */ false>;
/* * Class to encapsulate a promise for a particular role. Use this as the member * variable for a class whose method returns a promise.
*/ template <typename PromiseType, typename ImplType> class MozPromiseHolderBase { public:
MozPromiseHolderBase() = default;
template <typename PromiseType> class MozPromiseHolder
: public MozPromiseHolderBase<PromiseType, MozPromiseHolder<PromiseType>> { public: using MozPromiseHolderBase<
PromiseType, MozPromiseHolder<PromiseType>>::MozPromiseHolderBase; static constexpr void Check() {};
};
template <typename PromiseType> class MozMonitoredPromiseHolder
: public MozPromiseHolderBase<PromiseType,
MozMonitoredPromiseHolder<PromiseType>> { public: // Provide a Monitor that should always be held when accessing this instance. explicit MozMonitoredPromiseHolder(Monitor* const aMonitor)
: mMonitor(aMonitor) {
MOZ_ASSERT(aMonitor);
}
/* * Class to encapsulate a MozPromise::Request reference. Use this as the member * variable for a class waiting on a MozPromise.
*/ template <typename PromiseType> class MozPromiseRequestHolder { public:
MozPromiseRequestHolder() = default;
~MozPromiseRequestHolder() { MOZ_ASSERT(!mRequest); }
// Disconnects and forgets an outstanding promise. The resolve/reject methods // will never be called. void Disconnect() {
MOZ_ASSERT(Exists());
RefPtr request = std::move(mRequest);
request->Disconnect();
}
void DisconnectIfExists() { if (Exists()) {
Disconnect();
}
}
// 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/ // reject values are forwarded through.
namespace detail {
// Non-templated base class to allow us to use MOZ_COUNT_{C,D}TOR, which cause // assertions when used on templated types. class MethodCallBase { public:
MOZ_COUNTED_DEFAULT_CTOR(MethodCallBase)
MOZ_COUNTED_DTOR_VIRTUAL(MethodCallBase)
};
template <typename PromiseType, typename MethodType, typename ThisType, typename... Storages> class MethodCall : public MethodCallBase { public: template <typename... Args>
MethodCall(MethodType aMethod, ThisType* aThisVal, Args&&... aArgs)
: mMethod(aMethod),
mThisVal(aThisVal),
mArgs(std::forward<Args>(aArgs)...) {
static_assert(sizeof...(Storages) == sizeof...(Args), "Storages and Args should have equal sizes");
}
MethodCallType* methodCall = new MethodCallType(
aMethod, aThisVal, std::forward<ActualArgTypes>(aArgs)...);
RefPtr<typename PromiseType::Private> p = new (typename PromiseType::Private)(aCallerName);
RefPtr<ProxyRunnableType> r = new ProxyRunnableType(p, methodCall);
aTarget->Dispatch(r.forget()); return p;
}
constexpr bool Any() { returnfalse; }
template <typename T1>
constexpr bool Any(T1 a) { returnstatic_cast<bool>(a);
}
template <typename T1, typename... Ts>
constexpr bool Any(T1 a, Ts... aOthers) { return a || Any(aOthers...);
}
} // namespace detail
// InvokeAsync with explicitly-specified storages. // See ParameterStorage in nsThreadUtils.h for help. template <typename... Storages, typename PromiseType, typename ThisType, typename... ArgTypes, typename... ActualArgTypes,
std::enable_if_t<sizeof...(Storages) != 0, int> = 0> static RefPtr<PromiseType> InvokeAsync(
nsISerialEventTarget* aTarget, ThisType* aThisVal, StaticString aCallerName,
--> --------------------
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.