Bug 1667008 [wpt PR 25754] - [css-flex] Change some test expectations for image flex...
[gecko.git] / xpcom / threads / MozPromise.h
blobb50cc11f76fde53bbf05511cbb8e751a87c06859
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #if !defined(MozPromise_h_)
8 # define MozPromise_h_
10 # include <type_traits>
11 # include <utility>
13 # include "mozilla/Logging.h"
14 # include "mozilla/Maybe.h"
15 # include "mozilla/Monitor.h"
16 # include "mozilla/Mutex.h"
17 # include "mozilla/RefPtr.h"
18 # include "mozilla/Tuple.h"
19 # include "mozilla/UniquePtr.h"
20 # include "mozilla/Variant.h"
21 # include "nsIDirectTaskDispatcher.h"
22 # include "nsISerialEventTarget.h"
23 # include "nsTArray.h"
24 # include "nsThreadUtils.h"
26 # ifdef MOZ_WIDGET_ANDROID
27 # include "mozilla/jni/GeckoResultUtils.h"
28 # endif
30 # if MOZ_DIAGNOSTIC_ASSERT_ENABLED
31 # define PROMISE_DEBUG
32 # endif
34 # ifdef PROMISE_DEBUG
35 # define PROMISE_ASSERT MOZ_RELEASE_ASSERT
36 # else
37 # define PROMISE_ASSERT(...) \
38 do { \
39 } while (0)
40 # endif
42 # if DEBUG
43 # include "nsPrintfCString.h"
44 # endif
46 namespace mozilla {
48 namespace dom {
49 class Promise;
52 extern LazyLogModule gMozPromiseLog;
54 # define PROMISE_LOG(x, ...) \
55 MOZ_LOG(gMozPromiseLog, mozilla::LogLevel::Debug, (x, ##__VA_ARGS__))
57 namespace detail {
58 template <typename F>
59 struct MethodTraitsHelper : MethodTraitsHelper<decltype(&F::operator())> {};
60 template <typename ThisType, typename Ret, typename... ArgTypes>
61 struct MethodTraitsHelper<Ret (ThisType::*)(ArgTypes...)> {
62 using ReturnType = Ret;
63 static const size_t ArgSize = sizeof...(ArgTypes);
65 template <typename ThisType, typename Ret, typename... ArgTypes>
66 struct MethodTraitsHelper<Ret (ThisType::*)(ArgTypes...) const> {
67 using ReturnType = Ret;
68 static const size_t ArgSize = sizeof...(ArgTypes);
70 template <typename ThisType, typename Ret, typename... ArgTypes>
71 struct MethodTraitsHelper<Ret (ThisType::*)(ArgTypes...) volatile> {
72 using ReturnType = Ret;
73 static const size_t ArgSize = sizeof...(ArgTypes);
75 template <typename ThisType, typename Ret, typename... ArgTypes>
76 struct MethodTraitsHelper<Ret (ThisType::*)(ArgTypes...) const volatile> {
77 using ReturnType = Ret;
78 static const size_t ArgSize = sizeof...(ArgTypes);
80 template <typename T>
81 struct MethodTrait : MethodTraitsHelper<std::remove_reference_t<T>> {};
83 } // namespace detail
85 template <typename MethodType>
86 using TakesArgument =
87 std::integral_constant<bool, detail::MethodTrait<MethodType>::ArgSize != 0>;
89 template <typename MethodType, typename TargetType>
90 using ReturnTypeIs =
91 std::is_convertible<typename detail::MethodTrait<MethodType>::ReturnType,
92 TargetType>;
94 template <typename ResolveValueT, typename RejectValueT, bool IsExclusive>
95 class MozPromise;
97 template <typename Return>
98 struct IsMozPromise : std::false_type {};
100 template <typename ResolveValueT, typename RejectValueT, bool IsExclusive>
101 struct IsMozPromise<MozPromise<ResolveValueT, RejectValueT, IsExclusive>>
102 : std::true_type {};
105 * A promise manages an asynchronous request that may or may not be able to be
106 * fulfilled immediately. When an API returns a promise, the consumer may attach
107 * callbacks to be invoked (asynchronously, on a specified thread) when the
108 * request is either completed (resolved) or cannot be completed (rejected).
109 * Whereas JS promise callbacks are dispatched from Microtask checkpoints,
110 * MozPromises resolution/rejection make a normal round-trip through the event
111 * loop, which simplifies their ordering semantics relative to other native
112 * code.
114 * MozPromises attempt to mirror the spirit of JS Promises to the extent that
115 * is possible (and desirable) in C++. While the intent is that MozPromises
116 * feel familiar to programmers who are accustomed to their JS-implemented
117 * cousin, we don't shy away from imposing restrictions and adding features that
118 * make sense for the use cases we encounter.
120 * A MozPromise is ThreadSafe, and may be ->Then()ed on any thread. The Then()
121 * call accepts resolve and reject callbacks, and returns a magic object which
122 * will be implicitly converted to a MozPromise::Request or a MozPromise object
123 * depending on how the return value is used. The magic object serves several
124 * purposes for the consumer.
126 * (1) When converting to a MozPromise::Request, it allows the caller to
127 * cancel the delivery of the resolve/reject value if it has not already
128 * occurred, via Disconnect() (this must be done on the target thread to
129 * avoid racing).
131 * (2) When converting to a MozPromise (which is called a completion promise),
132 * it allows promise chaining so ->Then() can be called again to attach
133 * more resolve and reject callbacks. If the resolve/reject callback
134 * returns a new MozPromise, that promise is chained to the completion
135 * promise, such that its resolve/reject value will be forwarded along
136 * when it arrives. If the resolve/reject callback returns void, the
137 * completion promise is resolved/rejected with the same value that was
138 * passed to the callback.
140 * The MozPromise APIs skirt traditional XPCOM convention by returning nsRefPtrs
141 * (rather than already_AddRefed) from various methods. This is done to allow
142 * elegant chaining of calls without cluttering up the code with intermediate
143 * variables, and without introducing separate API variants for callers that
144 * want a return value (from, say, ->Then()) from those that don't.
146 * When IsExclusive is true, the MozPromise does a release-mode assertion that
147 * there is at most one call to either Then(...) or ChainTo(...).
150 class MozPromiseRefcountable {
151 public:
152 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MozPromiseRefcountable)
153 protected:
154 virtual ~MozPromiseRefcountable() = default;
157 class MozPromiseBase : public MozPromiseRefcountable {
158 public:
159 virtual void AssertIsDead() = 0;
162 template <typename T>
163 class MozPromiseHolder;
164 template <typename T>
165 class MozPromiseRequestHolder;
166 template <typename ResolveValueT, typename RejectValueT, bool IsExclusive>
167 class MozPromise : public MozPromiseBase {
168 static const uint32_t sMagic = 0xcecace11;
170 // Return a |T&&| to enable move when IsExclusive is true or
171 // a |const T&| to enforce copy otherwise.
172 template <typename T,
173 typename R = std::conditional_t<IsExclusive, T&&, const T&>>
174 static R MaybeMove(T& aX) {
175 return static_cast<R>(aX);
178 public:
179 typedef ResolveValueT ResolveValueType;
180 typedef RejectValueT RejectValueType;
181 class ResolveOrRejectValue {
182 public:
183 template <typename ResolveValueType_>
184 void SetResolve(ResolveValueType_&& aResolveValue) {
185 MOZ_ASSERT(IsNothing());
186 mValue = Storage(VariantIndex<ResolveIndex>{},
187 std::forward<ResolveValueType_>(aResolveValue));
190 template <typename RejectValueType_>
191 void SetReject(RejectValueType_&& aRejectValue) {
192 MOZ_ASSERT(IsNothing());
193 mValue = Storage(VariantIndex<RejectIndex>{},
194 std::forward<RejectValueType_>(aRejectValue));
197 template <typename ResolveValueType_>
198 static ResolveOrRejectValue MakeResolve(ResolveValueType_&& aResolveValue) {
199 ResolveOrRejectValue val;
200 val.SetResolve(std::forward<ResolveValueType_>(aResolveValue));
201 return val;
204 template <typename RejectValueType_>
205 static ResolveOrRejectValue MakeReject(RejectValueType_&& aRejectValue) {
206 ResolveOrRejectValue val;
207 val.SetReject(std::forward<RejectValueType_>(aRejectValue));
208 return val;
211 bool IsResolve() const { return mValue.template is<ResolveIndex>(); }
212 bool IsReject() const { return mValue.template is<RejectIndex>(); }
213 bool IsNothing() const { return mValue.template is<NothingIndex>(); }
215 const ResolveValueType& ResolveValue() const {
216 return mValue.template as<ResolveIndex>();
218 ResolveValueType& ResolveValue() {
219 return mValue.template as<ResolveIndex>();
221 const RejectValueType& RejectValue() const {
222 return mValue.template as<RejectIndex>();
224 RejectValueType& RejectValue() { return mValue.template as<RejectIndex>(); }
226 private:
227 enum { NothingIndex, ResolveIndex, RejectIndex };
228 using Storage = Variant<Nothing, ResolveValueType, RejectValueType>;
229 Storage mValue = Storage(VariantIndex<NothingIndex>{});
232 protected:
233 // MozPromise is the public type, and never constructed directly. Construct
234 // a MozPromise::Private, defined below.
235 MozPromise(const char* aCreationSite, bool aIsCompletionPromise)
236 : mCreationSite(aCreationSite),
237 mMutex("MozPromise Mutex"),
238 mHaveRequest(false),
239 mIsCompletionPromise(aIsCompletionPromise)
240 # ifdef PROMISE_DEBUG
242 mMagic4(&mMutex)
243 # endif
245 PROMISE_LOG("%s creating MozPromise (%p)", mCreationSite, this);
248 public:
249 // MozPromise::Private allows us to separate the public interface (upon which
250 // consumers of the promise may invoke methods like Then()) from the private
251 // interface (upon which the creator of the promise may invoke Resolve() or
252 // Reject()). APIs should create and store a MozPromise::Private (usually
253 // via a MozPromiseHolder), and return a MozPromise to consumers.
255 // NB: We can include the definition of this class inline once B2G ICS is
256 // gone.
257 class Private;
259 template <typename ResolveValueType_>
260 [[nodiscard]] static RefPtr<MozPromise> CreateAndResolve(
261 ResolveValueType_&& aResolveValue, const char* aResolveSite) {
262 static_assert(std::is_convertible_v<ResolveValueType_, ResolveValueT>,
263 "Resolve() argument must be implicitly convertible to "
264 "MozPromise's ResolveValueT");
265 RefPtr<typename MozPromise::Private> p =
266 new MozPromise::Private(aResolveSite);
267 p->Resolve(std::forward<ResolveValueType_>(aResolveValue), aResolveSite);
268 return p;
271 template <typename RejectValueType_>
272 [[nodiscard]] static RefPtr<MozPromise> CreateAndReject(
273 RejectValueType_&& aRejectValue, const char* aRejectSite) {
274 static_assert(std::is_convertible_v<RejectValueType_, RejectValueT>,
275 "Reject() argument must be implicitly convertible to "
276 "MozPromise's RejectValueT");
277 RefPtr<typename MozPromise::Private> p =
278 new MozPromise::Private(aRejectSite);
279 p->Reject(std::forward<RejectValueType_>(aRejectValue), aRejectSite);
280 return p;
283 template <typename ResolveOrRejectValueType_>
284 [[nodiscard]] static RefPtr<MozPromise> CreateAndResolveOrReject(
285 ResolveOrRejectValueType_&& aValue, const char* aSite) {
286 RefPtr<typename MozPromise::Private> p = new MozPromise::Private(aSite);
287 p->ResolveOrReject(std::forward<ResolveOrRejectValueType_>(aValue), aSite);
288 return p;
291 typedef MozPromise<CopyableTArray<ResolveValueType>, RejectValueType,
292 IsExclusive>
293 AllPromiseType;
295 private:
296 class AllPromiseHolder : public MozPromiseRefcountable {
297 public:
298 explicit AllPromiseHolder(size_t aDependentPromises)
299 : mPromise(new typename AllPromiseType::Private(__func__)),
300 mOutstandingPromises(aDependentPromises) {
301 MOZ_ASSERT(aDependentPromises > 0);
302 mResolveValues.SetLength(aDependentPromises);
305 void Resolve(size_t aIndex, ResolveValueType&& aResolveValue) {
306 if (!mPromise) {
307 // Already rejected.
308 return;
311 mResolveValues[aIndex].emplace(std::move(aResolveValue));
312 if (--mOutstandingPromises == 0) {
313 nsTArray<ResolveValueType> resolveValues;
314 resolveValues.SetCapacity(mResolveValues.Length());
315 for (auto&& resolveValue : mResolveValues) {
316 resolveValues.AppendElement(std::move(resolveValue.ref()));
319 mPromise->Resolve(std::move(resolveValues), __func__);
320 mPromise = nullptr;
321 mResolveValues.Clear();
325 void Reject(RejectValueType&& aRejectValue) {
326 if (!mPromise) {
327 // Already rejected.
328 return;
331 mPromise->Reject(std::move(aRejectValue), __func__);
332 mPromise = nullptr;
333 mResolveValues.Clear();
336 AllPromiseType* Promise() { return mPromise; }
338 private:
339 nsTArray<Maybe<ResolveValueType>> mResolveValues;
340 RefPtr<typename AllPromiseType::Private> mPromise;
341 size_t mOutstandingPromises;
344 public:
345 [[nodiscard]] static RefPtr<AllPromiseType> All(
346 nsISerialEventTarget* aProcessingTarget,
347 nsTArray<RefPtr<MozPromise>>& aPromises) {
348 if (aPromises.Length() == 0) {
349 return AllPromiseType::CreateAndResolve(
350 CopyableTArray<ResolveValueType>(), __func__);
353 RefPtr<AllPromiseHolder> holder = new AllPromiseHolder(aPromises.Length());
354 RefPtr<AllPromiseType> promise = holder->Promise();
355 for (size_t i = 0; i < aPromises.Length(); ++i) {
356 aPromises[i]->Then(
357 aProcessingTarget, __func__,
358 [holder, i](ResolveValueType aResolveValue) -> void {
359 holder->Resolve(i, std::move(aResolveValue));
361 [holder](RejectValueType aRejectValue) -> void {
362 holder->Reject(std::move(aRejectValue));
365 return promise;
368 class Request : public MozPromiseRefcountable {
369 public:
370 virtual void Disconnect() = 0;
372 protected:
373 Request() : mComplete(false), mDisconnected(false) {}
374 virtual ~Request() = default;
376 bool mComplete;
377 bool mDisconnected;
380 protected:
382 * A ThenValue tracks a single consumer waiting on the promise. When a
383 * consumer invokes promise->Then(...), a ThenValue is created. Once the
384 * Promise is resolved or rejected, a {Resolve,Reject}Runnable is dispatched,
385 * which invokes the resolve/reject method and then deletes the ThenValue.
387 class ThenValueBase : public Request {
388 friend class MozPromise;
389 static const uint32_t sMagic = 0xfadece11;
391 public:
392 class ResolveOrRejectRunnable : public CancelableRunnable {
393 public:
394 ResolveOrRejectRunnable(ThenValueBase* aThenValue, MozPromise* aPromise)
395 : CancelableRunnable(
396 "MozPromise::ThenValueBase::ResolveOrRejectRunnable"),
397 mThenValue(aThenValue),
398 mPromise(aPromise) {
399 MOZ_DIAGNOSTIC_ASSERT(!mPromise->IsPending());
402 ~ResolveOrRejectRunnable() {
403 if (mThenValue) {
404 mThenValue->AssertIsDead();
408 NS_IMETHOD Run() override {
409 PROMISE_LOG("ResolveOrRejectRunnable::Run() [this=%p]", this);
410 mThenValue->DoResolveOrReject(mPromise->Value());
411 mThenValue = nullptr;
412 mPromise = nullptr;
413 return NS_OK;
416 nsresult Cancel() override { return Run(); }
418 private:
419 RefPtr<ThenValueBase> mThenValue;
420 RefPtr<MozPromise> mPromise;
423 ThenValueBase(nsISerialEventTarget* aResponseTarget, const char* aCallSite)
424 : mResponseTarget(aResponseTarget), mCallSite(aCallSite) {
425 MOZ_ASSERT(aResponseTarget);
428 # ifdef PROMISE_DEBUG
429 ~ThenValueBase() {
430 mMagic1 = 0;
431 mMagic2 = 0;
433 # endif
435 void AssertIsDead() {
436 PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic);
437 // We want to assert that this ThenValues is dead - that is to say, that
438 // there are no consumers waiting for the result. In the case of a normal
439 // ThenValue, we check that it has been disconnected, which is the way
440 // that the consumer signals that it no longer wishes to hear about the
441 // result. If this ThenValue has a completion promise (which is mutually
442 // exclusive with being disconnectable), we recursively assert that every
443 // ThenValue associated with the completion promise is dead.
444 if (MozPromiseBase* p = CompletionPromise()) {
445 p->AssertIsDead();
446 } else {
447 MOZ_DIAGNOSTIC_ASSERT(Request::mDisconnected);
451 void Dispatch(MozPromise* aPromise) {
452 PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic);
453 aPromise->mMutex.AssertCurrentThreadOwns();
454 MOZ_ASSERT(!aPromise->IsPending());
456 nsCOMPtr<nsIRunnable> r = new ResolveOrRejectRunnable(this, aPromise);
457 PROMISE_LOG(
458 "%s Then() call made from %s [Runnable=%p, Promise=%p, ThenValue=%p] "
459 "%s dispatch",
460 aPromise->mValue.IsResolve() ? "Resolving" : "Rejecting", mCallSite,
461 r.get(), aPromise, this,
462 aPromise->mUseSynchronousTaskDispatch
463 ? "synchronous"
464 : aPromise->mUseDirectTaskDispatch ? "directtask" : "normal");
466 if (aPromise->mUseSynchronousTaskDispatch &&
467 mResponseTarget->IsOnCurrentThread()) {
468 PROMISE_LOG("ThenValue::Dispatch running task synchronously [this=%p]",
469 this);
470 r->Run();
471 return;
474 if (aPromise->mUseDirectTaskDispatch &&
475 mResponseTarget->IsOnCurrentThread()) {
476 PROMISE_LOG(
477 "ThenValue::Dispatch dispatch task via direct task queue [this=%p]",
478 this);
479 nsCOMPtr<nsIDirectTaskDispatcher> dispatcher =
480 do_QueryInterface(mResponseTarget);
481 if (dispatcher) {
482 dispatcher->DispatchDirectTask(r.forget());
483 return;
485 NS_WARNING(
486 nsPrintfCString(
487 "Direct Task dispatching not available for thread \"%s\"",
488 PR_GetThreadName(PR_GetCurrentThread()))
489 .get());
490 MOZ_DIAGNOSTIC_ASSERT(
491 false,
492 "mResponseTarget must implement nsIDirectTaskDispatcher for direct "
493 "task dispatching");
496 // Promise consumers are allowed to disconnect the Request object and
497 // then shut down the thread or task queue that the promise result would
498 // be dispatched on. So we unfortunately can't assert that promise
499 // dispatch succeeds. :-(
500 mResponseTarget->Dispatch(r.forget());
503 void Disconnect() override {
504 MOZ_DIAGNOSTIC_ASSERT(mResponseTarget->IsOnCurrentThread());
505 MOZ_DIAGNOSTIC_ASSERT(!Request::mComplete);
506 Request::mDisconnected = true;
508 // We could support rejecting the completion promise on disconnection, but
509 // then we'd need to have some sort of default reject value. The use cases
510 // of disconnection and completion promise chaining seem pretty
511 // orthogonal, so let's use assert against it.
512 MOZ_DIAGNOSTIC_ASSERT(!CompletionPromise());
515 protected:
516 virtual MozPromiseBase* CompletionPromise() const = 0;
517 virtual void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) = 0;
519 void DoResolveOrReject(ResolveOrRejectValue& aValue) {
520 PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic);
521 MOZ_DIAGNOSTIC_ASSERT(mResponseTarget->IsOnCurrentThread());
522 Request::mComplete = true;
523 if (Request::mDisconnected) {
524 PROMISE_LOG(
525 "ThenValue::DoResolveOrReject disconnected - bailing out [this=%p]",
526 this);
527 return;
530 // Invoke the resolve or reject method.
531 DoResolveOrRejectInternal(aValue);
534 nsCOMPtr<nsISerialEventTarget>
535 mResponseTarget; // May be released on any thread.
536 # ifdef PROMISE_DEBUG
537 uint32_t mMagic1 = sMagic;
538 # endif
539 const char* mCallSite;
540 # ifdef PROMISE_DEBUG
541 uint32_t mMagic2 = sMagic;
542 # endif
546 * We create two overloads for invoking Resolve/Reject Methods so as to
547 * make the resolve/reject value argument "optional".
549 template <typename ThisType, typename MethodType, typename ValueType>
550 static std::enable_if_t<TakesArgument<MethodType>::value,
551 typename detail::MethodTrait<MethodType>::ReturnType>
552 InvokeMethod(ThisType* aThisVal, MethodType aMethod, ValueType&& aValue) {
553 return (aThisVal->*aMethod)(std::forward<ValueType>(aValue));
556 template <typename ThisType, typename MethodType, typename ValueType>
557 static std::enable_if_t<!TakesArgument<MethodType>::value,
558 typename detail::MethodTrait<MethodType>::ReturnType>
559 InvokeMethod(ThisType* aThisVal, MethodType aMethod, ValueType&& aValue) {
560 return (aThisVal->*aMethod)();
563 // Called when promise chaining is supported.
564 template <bool SupportChaining, typename ThisType, typename MethodType,
565 typename ValueType, typename CompletionPromiseType>
566 static std::enable_if_t<SupportChaining, void> InvokeCallbackMethod(
567 ThisType* aThisVal, MethodType aMethod, ValueType&& aValue,
568 CompletionPromiseType&& aCompletionPromise) {
569 auto p = InvokeMethod(aThisVal, aMethod, std::forward<ValueType>(aValue));
570 if (aCompletionPromise) {
571 p->ChainTo(aCompletionPromise.forget(), "<chained completion promise>");
575 // Called when promise chaining is not supported.
576 template <bool SupportChaining, typename ThisType, typename MethodType,
577 typename ValueType, typename CompletionPromiseType>
578 static std::enable_if_t<!SupportChaining, void> InvokeCallbackMethod(
579 ThisType* aThisVal, MethodType aMethod, ValueType&& aValue,
580 CompletionPromiseType&& aCompletionPromise) {
581 MOZ_DIAGNOSTIC_ASSERT(
582 !aCompletionPromise,
583 "Can't do promise chaining for a non-promise-returning method.");
584 InvokeMethod(aThisVal, aMethod, std::forward<ValueType>(aValue));
587 template <typename>
588 class ThenCommand;
590 template <typename...>
591 class ThenValue;
593 template <typename ThisType, typename ResolveMethodType,
594 typename RejectMethodType>
595 class ThenValue<ThisType*, ResolveMethodType, RejectMethodType>
596 : public ThenValueBase {
597 friend class ThenCommand<ThenValue>;
599 using R1 = typename RemoveSmartPointer<
600 typename detail::MethodTrait<ResolveMethodType>::ReturnType>::Type;
601 using R2 = typename RemoveSmartPointer<
602 typename detail::MethodTrait<RejectMethodType>::ReturnType>::Type;
603 using SupportChaining =
604 std::integral_constant<bool, IsMozPromise<R1>::value &&
605 std::is_same_v<R1, R2>>;
607 // Fall back to MozPromise when promise chaining is not supported to make
608 // code compile.
609 using PromiseType =
610 std::conditional_t<SupportChaining::value, R1, MozPromise>;
612 public:
613 ThenValue(nsISerialEventTarget* aResponseTarget, ThisType* aThisVal,
614 ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod,
615 const char* aCallSite)
616 : ThenValueBase(aResponseTarget, aCallSite),
617 mThisVal(aThisVal),
618 mResolveMethod(aResolveMethod),
619 mRejectMethod(aRejectMethod) {}
621 void Disconnect() override {
622 ThenValueBase::Disconnect();
624 // If a Request has been disconnected, we don't guarantee that the
625 // resolve/reject runnable will be dispatched. Null out our refcounted
626 // this-value now so that it's released predictably on the dispatch
627 // thread.
628 mThisVal = nullptr;
631 protected:
632 MozPromiseBase* CompletionPromise() const override {
633 return mCompletionPromise;
636 void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) override {
637 if (aValue.IsResolve()) {
638 InvokeCallbackMethod<SupportChaining::value>(
639 mThisVal.get(), mResolveMethod, MaybeMove(aValue.ResolveValue()),
640 std::move(mCompletionPromise));
641 } else {
642 InvokeCallbackMethod<SupportChaining::value>(
643 mThisVal.get(), mRejectMethod, MaybeMove(aValue.RejectValue()),
644 std::move(mCompletionPromise));
647 // Null out mThisVal after invoking the callback so that any references
648 // are released predictably on the dispatch thread. Otherwise, it would be
649 // released on whatever thread last drops its reference to the ThenValue,
650 // which may or may not be ok.
651 mThisVal = nullptr;
654 private:
655 RefPtr<ThisType>
656 mThisVal; // Only accessed and refcounted on dispatch thread.
657 ResolveMethodType mResolveMethod;
658 RejectMethodType mRejectMethod;
659 RefPtr<typename PromiseType::Private> mCompletionPromise;
662 template <typename ThisType, typename ResolveRejectMethodType>
663 class ThenValue<ThisType*, ResolveRejectMethodType> : public ThenValueBase {
664 friend class ThenCommand<ThenValue>;
666 using R1 = typename RemoveSmartPointer<typename detail::MethodTrait<
667 ResolveRejectMethodType>::ReturnType>::Type;
668 using SupportChaining =
669 std::integral_constant<bool, IsMozPromise<R1>::value>;
671 // Fall back to MozPromise when promise chaining is not supported to make
672 // code compile.
673 using PromiseType =
674 std::conditional_t<SupportChaining::value, R1, MozPromise>;
676 public:
677 ThenValue(nsISerialEventTarget* aResponseTarget, ThisType* aThisVal,
678 ResolveRejectMethodType aResolveRejectMethod,
679 const char* aCallSite)
680 : ThenValueBase(aResponseTarget, aCallSite),
681 mThisVal(aThisVal),
682 mResolveRejectMethod(aResolveRejectMethod) {}
684 void Disconnect() override {
685 ThenValueBase::Disconnect();
687 // If a Request has been disconnected, we don't guarantee that the
688 // resolve/reject runnable will be dispatched. Null out our refcounted
689 // this-value now so that it's released predictably on the dispatch
690 // thread.
691 mThisVal = nullptr;
694 protected:
695 MozPromiseBase* CompletionPromise() const override {
696 return mCompletionPromise;
699 void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) override {
700 InvokeCallbackMethod<SupportChaining::value>(
701 mThisVal.get(), mResolveRejectMethod, MaybeMove(aValue),
702 std::move(mCompletionPromise));
704 // Null out mThisVal after invoking the callback so that any references
705 // are released predictably on the dispatch thread. Otherwise, it would be
706 // released on whatever thread last drops its reference to the ThenValue,
707 // which may or may not be ok.
708 mThisVal = nullptr;
711 private:
712 RefPtr<ThisType>
713 mThisVal; // Only accessed and refcounted on dispatch thread.
714 ResolveRejectMethodType mResolveRejectMethod;
715 RefPtr<typename PromiseType::Private> mCompletionPromise;
718 // NB: We could use std::function here instead of a template if it were
719 // supported. :-(
720 template <typename ResolveFunction, typename RejectFunction>
721 class ThenValue<ResolveFunction, RejectFunction> : public ThenValueBase {
722 friend class ThenCommand<ThenValue>;
724 using R1 = typename RemoveSmartPointer<
725 typename detail::MethodTrait<ResolveFunction>::ReturnType>::Type;
726 using R2 = typename RemoveSmartPointer<
727 typename detail::MethodTrait<RejectFunction>::ReturnType>::Type;
728 using SupportChaining =
729 std::integral_constant<bool, IsMozPromise<R1>::value &&
730 std::is_same_v<R1, R2>>;
732 // Fall back to MozPromise when promise chaining is not supported to make
733 // code compile.
734 using PromiseType =
735 std::conditional_t<SupportChaining::value, R1, MozPromise>;
737 public:
738 ThenValue(nsISerialEventTarget* aResponseTarget,
739 ResolveFunction&& aResolveFunction,
740 RejectFunction&& aRejectFunction, const char* aCallSite)
741 : ThenValueBase(aResponseTarget, aCallSite) {
742 mResolveFunction.emplace(std::move(aResolveFunction));
743 mRejectFunction.emplace(std::move(aRejectFunction));
746 void Disconnect() override {
747 ThenValueBase::Disconnect();
749 // If a Request has been disconnected, we don't guarantee that the
750 // resolve/reject runnable will be dispatched. Destroy our callbacks
751 // now so that any references in closures are released predictable on
752 // the dispatch thread.
753 mResolveFunction.reset();
754 mRejectFunction.reset();
757 protected:
758 MozPromiseBase* CompletionPromise() const override {
759 return mCompletionPromise;
762 void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) override {
763 // Note: The usage of InvokeCallbackMethod here requires that
764 // ResolveFunction/RejectFunction are capture-lambdas (i.e. anonymous
765 // classes with ::operator()), since it allows us to share code more
766 // easily. We could fix this if need be, though it's quite easy to work
767 // around by just capturing something.
768 if (aValue.IsResolve()) {
769 InvokeCallbackMethod<SupportChaining::value>(
770 mResolveFunction.ptr(), &ResolveFunction::operator(),
771 MaybeMove(aValue.ResolveValue()), std::move(mCompletionPromise));
772 } else {
773 InvokeCallbackMethod<SupportChaining::value>(
774 mRejectFunction.ptr(), &RejectFunction::operator(),
775 MaybeMove(aValue.RejectValue()), std::move(mCompletionPromise));
778 // Destroy callbacks after invocation so that any references in closures
779 // are released predictably on the dispatch thread. Otherwise, they would
780 // be released on whatever thread last drops its reference to the
781 // ThenValue, which may or may not be ok.
782 mResolveFunction.reset();
783 mRejectFunction.reset();
786 private:
787 Maybe<ResolveFunction>
788 mResolveFunction; // Only accessed and deleted on dispatch thread.
789 Maybe<RejectFunction>
790 mRejectFunction; // Only accessed and deleted on dispatch thread.
791 RefPtr<typename PromiseType::Private> mCompletionPromise;
794 template <typename ResolveRejectFunction>
795 class ThenValue<ResolveRejectFunction> : public ThenValueBase {
796 friend class ThenCommand<ThenValue>;
798 using R1 = typename RemoveSmartPointer<
799 typename detail::MethodTrait<ResolveRejectFunction>::ReturnType>::Type;
800 using SupportChaining =
801 std::integral_constant<bool, IsMozPromise<R1>::value>;
803 // Fall back to MozPromise when promise chaining is not supported to make
804 // code compile.
805 using PromiseType =
806 std::conditional_t<SupportChaining::value, R1, MozPromise>;
808 public:
809 ThenValue(nsISerialEventTarget* aResponseTarget,
810 ResolveRejectFunction&& aResolveRejectFunction,
811 const char* aCallSite)
812 : ThenValueBase(aResponseTarget, aCallSite) {
813 mResolveRejectFunction.emplace(std::move(aResolveRejectFunction));
816 void Disconnect() override {
817 ThenValueBase::Disconnect();
819 // If a Request has been disconnected, we don't guarantee that the
820 // resolve/reject runnable will be dispatched. Destroy our callbacks
821 // now so that any references in closures are released predictable on
822 // the dispatch thread.
823 mResolveRejectFunction.reset();
826 protected:
827 MozPromiseBase* CompletionPromise() const override {
828 return mCompletionPromise;
831 void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) override {
832 // Note: The usage of InvokeCallbackMethod here requires that
833 // ResolveRejectFunction is capture-lambdas (i.e. anonymous
834 // classes with ::operator()), since it allows us to share code more
835 // easily. We could fix this if need be, though it's quite easy to work
836 // around by just capturing something.
837 InvokeCallbackMethod<SupportChaining::value>(
838 mResolveRejectFunction.ptr(), &ResolveRejectFunction::operator(),
839 MaybeMove(aValue), std::move(mCompletionPromise));
841 // Destroy callbacks after invocation so that any references in closures
842 // are released predictably on the dispatch thread. Otherwise, they would
843 // be released on whatever thread last drops its reference to the
844 // ThenValue, which may or may not be ok.
845 mResolveRejectFunction.reset();
848 private:
849 Maybe<ResolveRejectFunction>
850 mResolveRejectFunction; // Only accessed and deleted on dispatch
851 // thread.
852 RefPtr<typename PromiseType::Private> mCompletionPromise;
855 public:
856 void ThenInternal(already_AddRefed<ThenValueBase> aThenValue,
857 const char* aCallSite) {
858 PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic &&
859 mMagic3 == sMagic && mMagic4 == &mMutex);
860 RefPtr<ThenValueBase> thenValue = aThenValue;
861 MutexAutoLock lock(mMutex);
862 MOZ_DIAGNOSTIC_ASSERT(
863 !IsExclusive || !mHaveRequest,
864 "Using an exclusive promise in a non-exclusive fashion");
865 mHaveRequest = true;
866 PROMISE_LOG("%s invoking Then() [this=%p, aThenValue=%p, isPending=%d]",
867 aCallSite, this, thenValue.get(), (int)IsPending());
868 if (!IsPending()) {
869 thenValue->Dispatch(this);
870 } else {
871 mThenValues.AppendElement(thenValue.forget());
875 protected:
877 * A command object to store all information needed to make a request to
878 * the promise. This allows us to delay the request until further use is
879 * known (whether it is ->Then() again for more promise chaining or ->Track()
880 * to terminate chaining and issue the request).
882 * This allows a unified syntax for promise chaining and disconnection
883 * and feels more like its JS counterpart.
885 template <typename ThenValueType>
886 class ThenCommand {
887 // Allow Promise1::ThenCommand to access the private constructor,
888 // Promise2::ThenCommand(ThenCommand&&).
889 template <typename, typename, bool>
890 friend class MozPromise;
892 using PromiseType = typename ThenValueType::PromiseType;
893 using Private = typename PromiseType::Private;
895 ThenCommand(const char* aCallSite,
896 already_AddRefed<ThenValueType> aThenValue,
897 MozPromise* aReceiver)
898 : mCallSite(aCallSite), mThenValue(aThenValue), mReceiver(aReceiver) {}
900 ThenCommand(ThenCommand&& aOther) = default;
902 public:
903 ~ThenCommand() {
904 // Issue the request now if the return value of Then() is not used.
905 if (mThenValue) {
906 mReceiver->ThenInternal(mThenValue.forget(), mCallSite);
910 // Allow RefPtr<MozPromise> p = somePromise->Then();
911 // p->Then(thread1, ...);
912 // p->Then(thread2, ...);
913 operator RefPtr<PromiseType>() {
914 static_assert(
915 ThenValueType::SupportChaining::value,
916 "The resolve/reject callback needs to return a RefPtr<MozPromise> "
917 "in order to do promise chaining.");
919 // mCompletionPromise must be created before ThenInternal() to avoid race.
920 RefPtr<Private> p =
921 new Private("<completion promise>", true /* aIsCompletionPromise */);
922 mThenValue->mCompletionPromise = p;
923 // Note ThenInternal() might nullify mCompletionPromise before return.
924 // So we need to return p instead of mCompletionPromise.
925 mReceiver->ThenInternal(mThenValue.forget(), mCallSite);
926 return p;
929 template <typename... Ts>
930 auto Then(Ts&&... aArgs) -> decltype(
931 std::declval<PromiseType>().Then(std::forward<Ts>(aArgs)...)) {
932 return static_cast<RefPtr<PromiseType>>(*this)->Then(
933 std::forward<Ts>(aArgs)...);
936 void Track(MozPromiseRequestHolder<MozPromise>& aRequestHolder) {
937 aRequestHolder.Track(do_AddRef(mThenValue));
938 mReceiver->ThenInternal(mThenValue.forget(), mCallSite);
941 // Allow calling ->Then() again for more promise chaining or ->Track() to
942 // end chaining and track the request for future disconnection.
943 ThenCommand* operator->() { return this; }
945 private:
946 const char* mCallSite;
947 RefPtr<ThenValueType> mThenValue;
948 RefPtr<MozPromise> mReceiver;
951 public:
952 template <typename ThisType, typename... Methods,
953 typename ThenValueType = ThenValue<ThisType*, Methods...>,
954 typename ReturnType = ThenCommand<ThenValueType>>
955 ReturnType Then(nsISerialEventTarget* aResponseTarget, const char* aCallSite,
956 ThisType* aThisVal, Methods... aMethods) {
957 RefPtr<ThenValueType> thenValue =
958 new ThenValueType(aResponseTarget, aThisVal, aMethods..., aCallSite);
959 return ReturnType(aCallSite, thenValue.forget(), this);
962 template <typename... Functions,
963 typename ThenValueType = ThenValue<Functions...>,
964 typename ReturnType = ThenCommand<ThenValueType>>
965 ReturnType Then(nsISerialEventTarget* aResponseTarget, const char* aCallSite,
966 Functions&&... aFunctions) {
967 RefPtr<ThenValueType> thenValue =
968 new ThenValueType(aResponseTarget, std::move(aFunctions)..., aCallSite);
969 return ReturnType(aCallSite, thenValue.forget(), this);
972 void ChainTo(already_AddRefed<Private> aChainedPromise,
973 const char* aCallSite) {
974 MutexAutoLock lock(mMutex);
975 MOZ_DIAGNOSTIC_ASSERT(
976 !IsExclusive || !mHaveRequest,
977 "Using an exclusive promise in a non-exclusive fashion");
978 mHaveRequest = true;
979 RefPtr<Private> chainedPromise = aChainedPromise;
980 PROMISE_LOG(
981 "%s invoking Chain() [this=%p, chainedPromise=%p, isPending=%d]",
982 aCallSite, this, chainedPromise.get(), (int)IsPending());
984 // We want to use the same type of dispatching method with the chained
985 // promises.
987 // We need to ensure that the UseSynchronousTaskDispatch branch isn't taken
988 // at compilation time to ensure we're not triggering the static_assert in
989 // UseSynchronousTaskDispatch method. if constexpr (IsExclusive) ensures
990 // that.
991 if (mUseDirectTaskDispatch) {
992 chainedPromise->UseDirectTaskDispatch(aCallSite);
993 } else if constexpr (IsExclusive) {
994 if (mUseSynchronousTaskDispatch) {
995 chainedPromise->UseSynchronousTaskDispatch(aCallSite);
999 if (!IsPending()) {
1000 ForwardTo(chainedPromise);
1001 } else {
1002 mChainedPromises.AppendElement(chainedPromise);
1006 # ifdef MOZ_WIDGET_ANDROID
1007 // Creates a C++ MozPromise from its Java counterpart, GeckoResult.
1008 [[nodiscard]] static RefPtr<MozPromise> FromGeckoResult(
1009 java::GeckoResult::Param aGeckoResult) {
1010 using jni::GeckoResultCallback;
1011 RefPtr<Private> p = new Private("GeckoResult Glue", false);
1012 auto resolve = GeckoResultCallback::CreateAndAttach<ResolveValueType>(
1013 [p](ResolveValueType aArg) { p->Resolve(aArg, __func__); });
1014 auto reject = GeckoResultCallback::CreateAndAttach<RejectValueType>(
1015 [p](RejectValueType aArg) { p->Reject(aArg, __func__); });
1016 aGeckoResult->NativeThen(resolve, reject);
1017 return p;
1019 # endif
1021 // Creates a C++ MozPromise from its JS counterpart, dom::Promise.
1022 // FromDomPromise currently only supports primitive types (int8/16/32, float,
1023 // double) And the reject value type must be a nsresult.
1024 // To use, please include MozPromiseInlines.h
1025 static RefPtr<MozPromise> FromDomPromise(dom::Promise* aDOMPromise);
1027 // Note we expose the function AssertIsDead() instead of IsDead() since
1028 // checking IsDead() is a data race in the situation where the request is not
1029 // dead. Therefore we enforce the form |Assert(IsDead())| by exposing
1030 // AssertIsDead() only.
1031 void AssertIsDead() override {
1032 PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic &&
1033 mMagic3 == sMagic && mMagic4 == &mMutex);
1034 MutexAutoLock lock(mMutex);
1035 for (auto&& then : mThenValues) {
1036 then->AssertIsDead();
1038 for (auto&& chained : mChainedPromises) {
1039 chained->AssertIsDead();
1043 protected:
1044 bool IsPending() const { return mValue.IsNothing(); }
1046 ResolveOrRejectValue& Value() {
1047 // This method should only be called once the value has stabilized. As
1048 // such, we don't need to acquire the lock here.
1049 MOZ_DIAGNOSTIC_ASSERT(!IsPending());
1050 return mValue;
1053 void DispatchAll() {
1054 mMutex.AssertCurrentThreadOwns();
1055 for (auto&& thenValue : mThenValues) {
1056 thenValue->Dispatch(this);
1058 mThenValues.Clear();
1060 for (auto&& chainedPromise : mChainedPromises) {
1061 ForwardTo(chainedPromise);
1063 mChainedPromises.Clear();
1066 void ForwardTo(Private* aOther) {
1067 MOZ_ASSERT(!IsPending());
1068 if (mValue.IsResolve()) {
1069 aOther->Resolve(MaybeMove(mValue.ResolveValue()), "<chained promise>");
1070 } else {
1071 aOther->Reject(MaybeMove(mValue.RejectValue()), "<chained promise>");
1075 virtual ~MozPromise() {
1076 PROMISE_LOG("MozPromise::~MozPromise [this=%p]", this);
1077 AssertIsDead();
1078 // We can't guarantee a completion promise will always be revolved or
1079 // rejected since ResolveOrRejectRunnable might not run when dispatch fails.
1080 if (!mIsCompletionPromise) {
1081 MOZ_ASSERT(!IsPending());
1082 MOZ_ASSERT(mThenValues.IsEmpty());
1083 MOZ_ASSERT(mChainedPromises.IsEmpty());
1085 # ifdef PROMISE_DEBUG
1086 mMagic1 = 0;
1087 mMagic2 = 0;
1088 mMagic3 = 0;
1089 mMagic4 = nullptr;
1090 # endif
1093 const char* mCreationSite; // For logging
1094 Mutex mMutex;
1095 ResolveOrRejectValue mValue;
1096 bool mUseSynchronousTaskDispatch = false;
1097 bool mUseDirectTaskDispatch = false;
1098 # ifdef PROMISE_DEBUG
1099 uint32_t mMagic1 = sMagic;
1100 # endif
1101 // Try shows we never have more than 3 elements when IsExclusive is false.
1102 // So '3' is a good value to avoid heap allocation in most cases.
1103 AutoTArray<RefPtr<ThenValueBase>, IsExclusive ? 1 : 3> mThenValues;
1104 # ifdef PROMISE_DEBUG
1105 uint32_t mMagic2 = sMagic;
1106 # endif
1107 nsTArray<RefPtr<Private>> mChainedPromises;
1108 # ifdef PROMISE_DEBUG
1109 uint32_t mMagic3 = sMagic;
1110 # endif
1111 bool mHaveRequest;
1112 const bool mIsCompletionPromise;
1113 # ifdef PROMISE_DEBUG
1114 void* mMagic4;
1115 # endif
1118 template <typename ResolveValueT, typename RejectValueT, bool IsExclusive>
1119 class MozPromise<ResolveValueT, RejectValueT, IsExclusive>::Private
1120 : public MozPromise<ResolveValueT, RejectValueT, IsExclusive> {
1121 public:
1122 explicit Private(const char* aCreationSite, bool aIsCompletionPromise = false)
1123 : MozPromise(aCreationSite, aIsCompletionPromise) {}
1125 template <typename ResolveValueT_>
1126 void Resolve(ResolveValueT_&& aResolveValue, const char* aResolveSite) {
1127 PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic &&
1128 mMagic3 == sMagic && mMagic4 == &mMutex);
1129 MutexAutoLock lock(mMutex);
1130 PROMISE_LOG("%s resolving MozPromise (%p created at %s)", aResolveSite,
1131 this, mCreationSite);
1132 if (!IsPending()) {
1133 PROMISE_LOG(
1134 "%s ignored already resolved or rejected MozPromise (%p created at "
1135 "%s)",
1136 aResolveSite, this, mCreationSite);
1137 return;
1139 mValue.SetResolve(std::forward<ResolveValueT_>(aResolveValue));
1140 DispatchAll();
1143 template <typename RejectValueT_>
1144 void Reject(RejectValueT_&& aRejectValue, const char* aRejectSite) {
1145 PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic &&
1146 mMagic3 == sMagic && mMagic4 == &mMutex);
1147 MutexAutoLock lock(mMutex);
1148 PROMISE_LOG("%s rejecting MozPromise (%p created at %s)", aRejectSite, this,
1149 mCreationSite);
1150 if (!IsPending()) {
1151 PROMISE_LOG(
1152 "%s ignored already resolved or rejected MozPromise (%p created at "
1153 "%s)",
1154 aRejectSite, this, mCreationSite);
1155 return;
1157 mValue.SetReject(std::forward<RejectValueT_>(aRejectValue));
1158 DispatchAll();
1161 template <typename ResolveOrRejectValue_>
1162 void ResolveOrReject(ResolveOrRejectValue_&& aValue, const char* aSite) {
1163 PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic &&
1164 mMagic3 == sMagic && mMagic4 == &mMutex);
1165 MutexAutoLock lock(mMutex);
1166 PROMISE_LOG("%s resolveOrRejecting MozPromise (%p created at %s)", aSite,
1167 this, mCreationSite);
1168 if (!IsPending()) {
1169 PROMISE_LOG(
1170 "%s ignored already resolved or rejected MozPromise (%p created at "
1171 "%s)",
1172 aSite, this, mCreationSite);
1173 return;
1175 mValue = std::forward<ResolveOrRejectValue_>(aValue);
1176 DispatchAll();
1179 // If the caller and target are both on the same thread, run the the resolve
1180 // or reject callback synchronously. Otherwise, the task will be dispatched
1181 // via the target Dispatch method.
1182 void UseSynchronousTaskDispatch(const char* aSite) {
1183 static_assert(
1184 IsExclusive,
1185 "Synchronous dispatch can only be used with exclusive promises");
1186 PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic &&
1187 mMagic3 == sMagic && mMagic4 == &mMutex);
1188 MutexAutoLock lock(mMutex);
1189 PROMISE_LOG("%s UseSynchronousTaskDispatch MozPromise (%p created at %s)",
1190 aSite, this, mCreationSite);
1191 MOZ_ASSERT(IsPending(),
1192 "A Promise must not have been already resolved or rejected to "
1193 "set dispatch state");
1194 mUseSynchronousTaskDispatch = true;
1197 // If the caller and target are both on the same thread, run the
1198 // resolve/reject callback off the direct task queue instead. This avoids a
1199 // full trip to the back of the event queue for each additional asynchronous
1200 // step when using MozPromise, and is similar (but not identical to) the
1201 // microtask semantics of JS promises.
1202 void UseDirectTaskDispatch(const char* aSite) {
1203 PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic &&
1204 mMagic3 == sMagic && mMagic4 == &mMutex);
1205 MutexAutoLock lock(mMutex);
1206 PROMISE_LOG("%s UseDirectTaskDispatch MozPromise (%p created at %s)", aSite,
1207 this, mCreationSite);
1208 MOZ_ASSERT(IsPending(),
1209 "A Promise must not have been already resolved or rejected to "
1210 "set dispatch state");
1211 MOZ_ASSERT(!mUseSynchronousTaskDispatch,
1212 "Promise already set for synchronous dispatch");
1213 mUseDirectTaskDispatch = true;
1217 // A generic promise type that does the trick for simple use cases.
1218 typedef MozPromise<bool, nsresult, /* IsExclusive = */ true> GenericPromise;
1220 // A generic, non-exclusive promise type that does the trick for simple use
1221 // cases.
1222 typedef MozPromise<bool, nsresult, /* IsExclusive = */ false>
1223 GenericNonExclusivePromise;
1226 * Class to encapsulate a promise for a particular role. Use this as the member
1227 * variable for a class whose method returns a promise.
1229 template <typename PromiseType, typename ImplType>
1230 class MozPromiseHolderBase {
1231 public:
1232 MozPromiseHolderBase() = default;
1234 MozPromiseHolderBase(MozPromiseHolderBase&& aOther) = default;
1235 MozPromiseHolderBase& operator=(MozPromiseHolderBase&& aOther) = default;
1237 ~MozPromiseHolderBase() { MOZ_ASSERT(!mPromise); }
1239 already_AddRefed<PromiseType> Ensure(const char* aMethodName) {
1240 static_cast<ImplType*>(this)->Check();
1241 if (!mPromise) {
1242 mPromise = new (typename PromiseType::Private)(aMethodName);
1244 RefPtr<PromiseType> p = mPromise.get();
1245 return p.forget();
1248 bool IsEmpty() const {
1249 static_cast<const ImplType*>(this)->Check();
1250 return !mPromise;
1253 already_AddRefed<typename PromiseType::Private> Steal() {
1254 static_cast<ImplType*>(this)->Check();
1255 return mPromise.forget();
1258 template <typename ResolveValueType_>
1259 void Resolve(ResolveValueType_&& aResolveValue, const char* aMethodName) {
1260 static_assert(std::is_convertible_v<ResolveValueType_,
1261 typename PromiseType::ResolveValueType>,
1262 "Resolve() argument must be implicitly convertible to "
1263 "MozPromise's ResolveValueT");
1265 static_cast<ImplType*>(this)->Check();
1266 MOZ_ASSERT(mPromise);
1267 mPromise->Resolve(std::forward<ResolveValueType_>(aResolveValue),
1268 aMethodName);
1269 mPromise = nullptr;
1272 template <typename ResolveValueType_>
1273 void ResolveIfExists(ResolveValueType_&& aResolveValue,
1274 const char* aMethodName) {
1275 if (!IsEmpty()) {
1276 Resolve(std::forward<ResolveValueType_>(aResolveValue), aMethodName);
1280 template <typename RejectValueType_>
1281 void Reject(RejectValueType_&& aRejectValue, const char* aMethodName) {
1282 static_assert(std::is_convertible_v<RejectValueType_,
1283 typename PromiseType::RejectValueType>,
1284 "Reject() argument must be implicitly convertible to "
1285 "MozPromise's RejectValueT");
1287 static_cast<ImplType*>(this)->Check();
1288 MOZ_ASSERT(mPromise);
1289 mPromise->Reject(std::forward<RejectValueType_>(aRejectValue), aMethodName);
1290 mPromise = nullptr;
1293 template <typename RejectValueType_>
1294 void RejectIfExists(RejectValueType_&& aRejectValue,
1295 const char* aMethodName) {
1296 if (!IsEmpty()) {
1297 Reject(std::forward<RejectValueType_>(aRejectValue), aMethodName);
1301 template <typename ResolveOrRejectValueType_>
1302 void ResolveOrReject(ResolveOrRejectValueType_&& aValue,
1303 const char* aMethodName) {
1304 static_cast<ImplType*>(this)->Check();
1305 MOZ_ASSERT(mPromise);
1306 mPromise->ResolveOrReject(std::forward<ResolveOrRejectValueType_>(aValue),
1307 aMethodName);
1308 mPromise = nullptr;
1311 template <typename ResolveOrRejectValueType_>
1312 void ResolveOrRejectIfExists(ResolveOrRejectValueType_&& aValue,
1313 const char* aMethodName) {
1314 if (!IsEmpty()) {
1315 ResolveOrReject(std::forward<ResolveOrRejectValueType_>(aValue),
1316 aMethodName);
1320 void UseSynchronousTaskDispatch(const char* aSite) {
1321 MOZ_ASSERT(mPromise);
1322 mPromise->UseSynchronousTaskDispatch(aSite);
1325 void UseDirectTaskDispatch(const char* aSite) {
1326 MOZ_ASSERT(mPromise);
1327 mPromise->UseDirectTaskDispatch(aSite);
1330 private:
1331 RefPtr<typename PromiseType::Private> mPromise;
1334 template <typename PromiseType>
1335 class MozPromiseHolder
1336 : public MozPromiseHolderBase<PromiseType, MozPromiseHolder<PromiseType>> {
1337 public:
1338 using MozPromiseHolderBase<
1339 PromiseType, MozPromiseHolder<PromiseType>>::MozPromiseHolderBase;
1340 static constexpr void Check(){};
1343 template <typename PromiseType>
1344 class MozMonitoredPromiseHolder
1345 : public MozPromiseHolderBase<PromiseType,
1346 MozMonitoredPromiseHolder<PromiseType>> {
1347 public:
1348 // Provide a Monitor that should always be held when accessing this instance.
1349 explicit MozMonitoredPromiseHolder(Monitor* const aMonitor)
1350 : mMonitor(aMonitor) {
1351 MOZ_ASSERT(aMonitor);
1354 MozMonitoredPromiseHolder(MozMonitoredPromiseHolder&& aOther) = delete;
1355 MozMonitoredPromiseHolder& operator=(MozMonitoredPromiseHolder&& aOther) =
1356 delete;
1358 void Check() const { mMonitor->AssertCurrentThreadOwns(); }
1360 private:
1361 Monitor* const mMonitor;
1365 * Class to encapsulate a MozPromise::Request reference. Use this as the member
1366 * variable for a class waiting on a MozPromise.
1368 template <typename PromiseType>
1369 class MozPromiseRequestHolder {
1370 public:
1371 MozPromiseRequestHolder() = default;
1372 ~MozPromiseRequestHolder() { MOZ_ASSERT(!mRequest); }
1374 void Track(already_AddRefed<typename PromiseType::Request> aRequest) {
1375 MOZ_DIAGNOSTIC_ASSERT(!Exists());
1376 mRequest = aRequest;
1379 void Complete() {
1380 MOZ_DIAGNOSTIC_ASSERT(Exists());
1381 mRequest = nullptr;
1384 // Disconnects and forgets an outstanding promise. The resolve/reject methods
1385 // will never be called.
1386 void Disconnect() {
1387 MOZ_ASSERT(Exists());
1388 mRequest->Disconnect();
1389 mRequest = nullptr;
1392 void DisconnectIfExists() {
1393 if (Exists()) {
1394 Disconnect();
1398 bool Exists() const { return !!mRequest; }
1400 private:
1401 RefPtr<typename PromiseType::Request> mRequest;
1404 // Asynchronous Potentially-Cross-Thread Method Calls.
1406 // This machinery allows callers to schedule a promise-returning function
1407 // (a method and object, or a function object like a lambda) to be invoked
1408 // asynchronously on a given thread, while at the same time receiving a
1409 // promise upon which to invoke Then() immediately. InvokeAsync dispatches a
1410 // task to invoke the function on the proper thread and also chain the
1411 // resulting promise to the one that the caller received, so that resolve/
1412 // reject values are forwarded through.
1414 namespace detail {
1416 // Non-templated base class to allow us to use MOZ_COUNT_{C,D}TOR, which cause
1417 // assertions when used on templated types.
1418 class MethodCallBase {
1419 public:
1420 MOZ_COUNTED_DEFAULT_CTOR(MethodCallBase)
1421 MOZ_COUNTED_DTOR_VIRTUAL(MethodCallBase)
1424 template <typename PromiseType, typename MethodType, typename ThisType,
1425 typename... Storages>
1426 class MethodCall : public MethodCallBase {
1427 public:
1428 template <typename... Args>
1429 MethodCall(MethodType aMethod, ThisType* aThisVal, Args&&... aArgs)
1430 : mMethod(aMethod),
1431 mThisVal(aThisVal),
1432 mArgs(std::forward<Args>(aArgs)...) {
1433 static_assert(sizeof...(Storages) == sizeof...(Args),
1434 "Storages and Args should have equal sizes");
1437 RefPtr<PromiseType> Invoke() { return mArgs.apply(mThisVal.get(), mMethod); }
1439 private:
1440 MethodType mMethod;
1441 RefPtr<ThisType> mThisVal;
1442 RunnableMethodArguments<Storages...> mArgs;
1445 template <typename PromiseType, typename MethodType, typename ThisType,
1446 typename... Storages>
1447 class ProxyRunnable : public CancelableRunnable {
1448 public:
1449 ProxyRunnable(
1450 typename PromiseType::Private* aProxyPromise,
1451 MethodCall<PromiseType, MethodType, ThisType, Storages...>* aMethodCall)
1452 : CancelableRunnable("detail::ProxyRunnable"),
1453 mProxyPromise(aProxyPromise),
1454 mMethodCall(aMethodCall) {}
1456 NS_IMETHOD Run() override {
1457 RefPtr<PromiseType> p = mMethodCall->Invoke();
1458 mMethodCall = nullptr;
1459 p->ChainTo(mProxyPromise.forget(), "<Proxy Promise>");
1460 return NS_OK;
1463 nsresult Cancel() override { return Run(); }
1465 private:
1466 RefPtr<typename PromiseType::Private> mProxyPromise;
1467 UniquePtr<MethodCall<PromiseType, MethodType, ThisType, Storages...>>
1468 mMethodCall;
1471 template <typename... Storages, typename PromiseType, typename ThisType,
1472 typename... ArgTypes, typename... ActualArgTypes>
1473 static RefPtr<PromiseType> InvokeAsyncImpl(
1474 nsISerialEventTarget* aTarget, ThisType* aThisVal, const char* aCallerName,
1475 RefPtr<PromiseType> (ThisType::*aMethod)(ArgTypes...),
1476 ActualArgTypes&&... aArgs) {
1477 MOZ_ASSERT(aTarget);
1479 typedef RefPtr<PromiseType> (ThisType::*MethodType)(ArgTypes...);
1480 typedef detail::MethodCall<PromiseType, MethodType, ThisType, Storages...>
1481 MethodCallType;
1482 typedef detail::ProxyRunnable<PromiseType, MethodType, ThisType, Storages...>
1483 ProxyRunnableType;
1485 MethodCallType* methodCall = new MethodCallType(
1486 aMethod, aThisVal, std::forward<ActualArgTypes>(aArgs)...);
1487 RefPtr<typename PromiseType::Private> p =
1488 new (typename PromiseType::Private)(aCallerName);
1489 RefPtr<ProxyRunnableType> r = new ProxyRunnableType(p, methodCall);
1490 aTarget->Dispatch(r.forget());
1491 return p;
1494 constexpr bool Any() { return false; }
1496 template <typename T1>
1497 constexpr bool Any(T1 a) {
1498 return static_cast<bool>(a);
1501 template <typename T1, typename... Ts>
1502 constexpr bool Any(T1 a, Ts... aOthers) {
1503 return a || Any(aOthers...);
1506 } // namespace detail
1508 // InvokeAsync with explicitly-specified storages.
1509 // See ParameterStorage in nsThreadUtils.h for help.
1510 template <typename... Storages, typename PromiseType, typename ThisType,
1511 typename... ArgTypes, typename... ActualArgTypes,
1512 std::enable_if_t<sizeof...(Storages) != 0, int> = 0>
1513 static RefPtr<PromiseType> InvokeAsync(
1514 nsISerialEventTarget* aTarget, ThisType* aThisVal, const char* aCallerName,
1515 RefPtr<PromiseType> (ThisType::*aMethod)(ArgTypes...),
1516 ActualArgTypes&&... aArgs) {
1517 static_assert(
1518 sizeof...(Storages) == sizeof...(ArgTypes),
1519 "Provided Storages and method's ArgTypes should have equal sizes");
1520 static_assert(sizeof...(Storages) == sizeof...(ActualArgTypes),
1521 "Provided Storages and ActualArgTypes should have equal sizes");
1522 return detail::InvokeAsyncImpl<Storages...>(
1523 aTarget, aThisVal, aCallerName, aMethod,
1524 std::forward<ActualArgTypes>(aArgs)...);
1527 // InvokeAsync with no explicitly-specified storages, will copy arguments and
1528 // then move them out of the runnable into the target method parameters.
1529 template <typename... Storages, typename PromiseType, typename ThisType,
1530 typename... ArgTypes, typename... ActualArgTypes,
1531 std::enable_if_t<sizeof...(Storages) == 0, int> = 0>
1532 static RefPtr<PromiseType> InvokeAsync(
1533 nsISerialEventTarget* aTarget, ThisType* aThisVal, const char* aCallerName,
1534 RefPtr<PromiseType> (ThisType::*aMethod)(ArgTypes...),
1535 ActualArgTypes&&... aArgs) {
1536 static_assert(
1537 !detail::Any(
1538 std::is_pointer_v<std::remove_reference_t<ActualArgTypes>>...),
1539 "Cannot pass pointer types through InvokeAsync, Storages must be "
1540 "provided");
1541 static_assert(sizeof...(ArgTypes) == sizeof...(ActualArgTypes),
1542 "Method's ArgTypes and ActualArgTypes should have equal sizes");
1543 return detail::InvokeAsyncImpl<
1544 StoreCopyPassByRRef<std::decay_t<ActualArgTypes>>...>(
1545 aTarget, aThisVal, aCallerName, aMethod,
1546 std::forward<ActualArgTypes>(aArgs)...);
1549 namespace detail {
1551 template <typename Function, typename PromiseType>
1552 class ProxyFunctionRunnable : public CancelableRunnable {
1553 using FunctionStorage = std::decay_t<Function>;
1555 public:
1556 template <typename F>
1557 ProxyFunctionRunnable(typename PromiseType::Private* aProxyPromise,
1558 F&& aFunction)
1559 : CancelableRunnable("detail::ProxyFunctionRunnable"),
1560 mProxyPromise(aProxyPromise),
1561 mFunction(new FunctionStorage(std::forward<F>(aFunction))) {}
1563 NS_IMETHOD Run() override {
1564 RefPtr<PromiseType> p = (*mFunction)();
1565 mFunction = nullptr;
1566 p->ChainTo(mProxyPromise.forget(), "<Proxy Promise>");
1567 return NS_OK;
1570 nsresult Cancel() override { return Run(); }
1572 private:
1573 RefPtr<typename PromiseType::Private> mProxyPromise;
1574 UniquePtr<FunctionStorage> mFunction;
1577 // Note: The following struct and function are not for public consumption (yet?)
1578 // as we would prefer all calls to pass on-the-spot lambdas (or at least moved
1579 // function objects). They could be moved outside of detail if really needed.
1581 // We prefer getting function objects by non-lvalue-ref (to avoid copying them
1582 // and their captures). This struct is a tag that allows the use of objects
1583 // through lvalue-refs where necessary.
1584 struct AllowInvokeAsyncFunctionLVRef {};
1586 // Invoke a function object (e.g., lambda or std/mozilla::function)
1587 // asynchronously; note that the object will be copied if provided by
1588 // lvalue-ref. Return a promise that the function should eventually resolve or
1589 // reject.
1590 template <typename Function>
1591 static auto InvokeAsync(nsISerialEventTarget* aTarget, const char* aCallerName,
1592 AllowInvokeAsyncFunctionLVRef, Function&& aFunction)
1593 -> decltype(aFunction()) {
1594 static_assert(
1595 IsRefcountedSmartPointer<decltype(aFunction())>::value &&
1596 IsMozPromise<
1597 typename RemoveSmartPointer<decltype(aFunction())>::Type>::value,
1598 "Function object must return RefPtr<MozPromise>");
1599 MOZ_ASSERT(aTarget);
1600 typedef typename RemoveSmartPointer<decltype(aFunction())>::Type PromiseType;
1601 typedef detail::ProxyFunctionRunnable<Function, PromiseType>
1602 ProxyRunnableType;
1604 auto p = MakeRefPtr<typename PromiseType::Private>(aCallerName);
1605 auto r = MakeRefPtr<ProxyRunnableType>(p, std::forward<Function>(aFunction));
1606 aTarget->Dispatch(r.forget());
1607 return p;
1610 } // namespace detail
1612 // Invoke a function object (e.g., lambda) asynchronously.
1613 // Return a promise that the function should eventually resolve or reject.
1614 template <typename Function>
1615 static auto InvokeAsync(nsISerialEventTarget* aTarget, const char* aCallerName,
1616 Function&& aFunction) -> decltype(aFunction()) {
1617 static_assert(!std::is_lvalue_reference_v<Function>,
1618 "Function object must not be passed by lvalue-ref (to avoid "
1619 "unplanned copies); Consider move()ing the object.");
1620 return detail::InvokeAsync(aTarget, aCallerName,
1621 detail::AllowInvokeAsyncFunctionLVRef(),
1622 std::forward<Function>(aFunction));
1625 # undef PROMISE_LOG
1626 # undef PROMISE_ASSERT
1627 # undef PROMISE_DEBUG
1629 } // namespace mozilla
1631 #endif