no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / xpcom / threads / MozPromise.h
blobf17e085c2fd3535daddd4673ab668406e87947f8
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/ErrorNames.h"
14 # include "mozilla/Logging.h"
15 # include "mozilla/Maybe.h"
16 # include "mozilla/Monitor.h"
17 # include "mozilla/Mutex.h"
18 # include "mozilla/RefPtr.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 typedef MozPromise<CopyableTArray<ResolveOrRejectValue>, bool, IsExclusive>
296 AllSettledPromiseType;
298 private:
299 class AllPromiseHolder : public MozPromiseRefcountable {
300 public:
301 explicit AllPromiseHolder(size_t aDependentPromises)
302 : mPromise(new typename AllPromiseType::Private(__func__)),
303 mOutstandingPromises(aDependentPromises) {
304 MOZ_ASSERT(aDependentPromises > 0);
305 mResolveValues.SetLength(aDependentPromises);
308 template <typename ResolveValueType_>
309 void Resolve(size_t aIndex, ResolveValueType_&& aResolveValue) {
310 if (!mPromise) {
311 // Already rejected.
312 return;
315 mResolveValues[aIndex].emplace(
316 std::forward<ResolveValueType_>(aResolveValue));
317 if (--mOutstandingPromises == 0) {
318 nsTArray<ResolveValueType> resolveValues;
319 resolveValues.SetCapacity(mResolveValues.Length());
320 for (auto&& resolveValue : mResolveValues) {
321 resolveValues.AppendElement(std::move(resolveValue.ref()));
324 mPromise->Resolve(std::move(resolveValues), __func__);
325 mPromise = nullptr;
326 mResolveValues.Clear();
330 template <typename RejectValueType_>
331 void Reject(RejectValueType_&& aRejectValue) {
332 if (!mPromise) {
333 // Already rejected.
334 return;
337 mPromise->Reject(std::forward<RejectValueType_>(aRejectValue), __func__);
338 mPromise = nullptr;
339 mResolveValues.Clear();
342 AllPromiseType* Promise() { return mPromise; }
344 private:
345 nsTArray<Maybe<ResolveValueType>> mResolveValues;
346 RefPtr<typename AllPromiseType::Private> mPromise;
347 size_t mOutstandingPromises;
350 // Trying to pass ResolveOrRejectValue by value fails static analysis checks,
351 // so we need to use either a const& or an rvalue reference, depending on
352 // whether IsExclusive is true or not.
353 typedef std::conditional_t<IsExclusive, ResolveOrRejectValue&&,
354 const ResolveOrRejectValue&>
355 ResolveOrRejectValueParam;
357 typedef std::conditional_t<IsExclusive, ResolveValueType&&,
358 const ResolveValueType&>
359 ResolveValueTypeParam;
361 typedef std::conditional_t<IsExclusive, RejectValueType&&,
362 const RejectValueType&>
363 RejectValueTypeParam;
365 class AllSettledPromiseHolder : public MozPromiseRefcountable {
366 public:
367 explicit AllSettledPromiseHolder(size_t aDependentPromises)
368 : mPromise(new typename AllSettledPromiseType::Private(__func__)),
369 mOutstandingPromises(aDependentPromises) {
370 MOZ_ASSERT(aDependentPromises > 0);
371 mValues.SetLength(aDependentPromises);
374 void Settle(size_t aIndex, ResolveOrRejectValueParam aValue) {
375 if (!mPromise) {
376 // Already rejected.
377 return;
380 mValues[aIndex].emplace(MaybeMove(aValue));
381 if (--mOutstandingPromises == 0) {
382 nsTArray<ResolveOrRejectValue> values;
383 values.SetCapacity(mValues.Length());
384 for (auto&& value : mValues) {
385 values.AppendElement(std::move(value.ref()));
388 mPromise->Resolve(std::move(values), __func__);
389 mPromise = nullptr;
390 mValues.Clear();
394 AllSettledPromiseType* Promise() { return mPromise; }
396 private:
397 nsTArray<Maybe<ResolveOrRejectValue>> mValues;
398 RefPtr<typename AllSettledPromiseType::Private> mPromise;
399 size_t mOutstandingPromises;
402 public:
403 [[nodiscard]] static RefPtr<AllPromiseType> All(
404 nsISerialEventTarget* aProcessingTarget,
405 nsTArray<RefPtr<MozPromise>>& aPromises) {
406 if (aPromises.Length() == 0) {
407 return AllPromiseType::CreateAndResolve(
408 CopyableTArray<ResolveValueType>(), __func__);
411 RefPtr<AllPromiseHolder> holder = new AllPromiseHolder(aPromises.Length());
412 RefPtr<AllPromiseType> promise = holder->Promise();
413 for (size_t i = 0; i < aPromises.Length(); ++i) {
414 aPromises[i]->Then(
415 aProcessingTarget, __func__,
416 [holder, i](ResolveValueTypeParam aResolveValue) -> void {
417 holder->Resolve(i, MaybeMove(aResolveValue));
419 [holder](RejectValueTypeParam aRejectValue) -> void {
420 holder->Reject(MaybeMove(aRejectValue));
423 return promise;
426 [[nodiscard]] static RefPtr<AllSettledPromiseType> AllSettled(
427 nsISerialEventTarget* aProcessingTarget,
428 nsTArray<RefPtr<MozPromise>>& aPromises) {
429 if (aPromises.Length() == 0) {
430 return AllSettledPromiseType::CreateAndResolve(
431 CopyableTArray<ResolveOrRejectValue>(), __func__);
434 RefPtr<AllSettledPromiseHolder> holder =
435 new AllSettledPromiseHolder(aPromises.Length());
436 RefPtr<AllSettledPromiseType> promise = holder->Promise();
437 for (size_t i = 0; i < aPromises.Length(); ++i) {
438 aPromises[i]->Then(aProcessingTarget, __func__,
439 [holder, i](ResolveOrRejectValueParam aValue) -> void {
440 holder->Settle(i, MaybeMove(aValue));
443 return promise;
446 class Request : public MozPromiseRefcountable {
447 public:
448 virtual void Disconnect() = 0;
450 protected:
451 Request() : mComplete(false), mDisconnected(false) {}
452 virtual ~Request() = default;
454 bool mComplete;
455 bool mDisconnected;
458 protected:
460 * A ThenValue tracks a single consumer waiting on the promise. When a
461 * consumer invokes promise->Then(...), a ThenValue is created. Once the
462 * Promise is resolved or rejected, a {Resolve,Reject}Runnable is dispatched,
463 * which invokes the resolve/reject method and then deletes the ThenValue.
465 class ThenValueBase : public Request {
466 friend class MozPromise;
467 static const uint32_t sMagic = 0xfadece11;
469 public:
470 class ResolveOrRejectRunnable final
471 : public PrioritizableCancelableRunnable {
472 public:
473 ResolveOrRejectRunnable(ThenValueBase* aThenValue, MozPromise* aPromise)
474 : PrioritizableCancelableRunnable(
475 aPromise->mPriority,
476 "MozPromise::ThenValueBase::ResolveOrRejectRunnable"),
477 mThenValue(aThenValue),
478 mPromise(aPromise) {
479 MOZ_DIAGNOSTIC_ASSERT(!mPromise->IsPending());
482 ~ResolveOrRejectRunnable() {
483 if (mThenValue) {
484 mThenValue->AssertIsDead();
488 NS_IMETHOD Run() override {
489 PROMISE_LOG("ResolveOrRejectRunnable::Run() [this=%p]", this);
490 mThenValue->DoResolveOrReject(mPromise->Value());
491 mThenValue = nullptr;
492 mPromise = nullptr;
493 return NS_OK;
496 nsresult Cancel() override { return Run(); }
498 private:
499 RefPtr<ThenValueBase> mThenValue;
500 RefPtr<MozPromise> mPromise;
503 ThenValueBase(nsISerialEventTarget* aResponseTarget, const char* aCallSite)
504 : mResponseTarget(aResponseTarget), mCallSite(aCallSite) {
505 MOZ_ASSERT(aResponseTarget);
508 # ifdef PROMISE_DEBUG
509 ~ThenValueBase() {
510 mMagic1 = 0;
511 mMagic2 = 0;
513 # endif
515 void AssertIsDead() {
516 PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic);
517 // We want to assert that this ThenValues is dead - that is to say, that
518 // there are no consumers waiting for the result. In the case of a normal
519 // ThenValue, we check that it has been disconnected, which is the way
520 // that the consumer signals that it no longer wishes to hear about the
521 // result. If this ThenValue has a completion promise (which is mutually
522 // exclusive with being disconnectable), we recursively assert that every
523 // ThenValue associated with the completion promise is dead.
524 if (MozPromiseBase* p = CompletionPromise()) {
525 p->AssertIsDead();
526 } else {
527 # ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
528 if (MOZ_UNLIKELY(!Request::mDisconnected)) {
529 MOZ_CRASH_UNSAFE_PRINTF(
530 "MozPromise::ThenValue created from '%s' destroyed without being "
531 "either disconnected, resolved, or rejected (dispatchRv: %s)",
532 mCallSite,
533 mDispatchRv ? GetStaticErrorName(*mDispatchRv)
534 : "not dispatched");
536 # endif
540 void Dispatch(MozPromise* aPromise) {
541 PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic);
542 aPromise->mMutex.AssertCurrentThreadOwns();
543 MOZ_ASSERT(!aPromise->IsPending());
545 nsCOMPtr<nsIRunnable> r = new ResolveOrRejectRunnable(this, aPromise);
546 PROMISE_LOG(
547 "%s Then() call made from %s [Runnable=%p, Promise=%p, ThenValue=%p] "
548 "%s dispatch",
549 aPromise->mValue.IsResolve() ? "Resolving" : "Rejecting", mCallSite,
550 r.get(), aPromise, this,
551 aPromise->mUseSynchronousTaskDispatch ? "synchronous"
552 : aPromise->mUseDirectTaskDispatch ? "directtask"
553 : "normal");
555 if (aPromise->mUseSynchronousTaskDispatch &&
556 mResponseTarget->IsOnCurrentThread()) {
557 PROMISE_LOG("ThenValue::Dispatch running task synchronously [this=%p]",
558 this);
559 r->Run();
560 return;
563 if (aPromise->mUseDirectTaskDispatch &&
564 mResponseTarget->IsOnCurrentThread()) {
565 PROMISE_LOG(
566 "ThenValue::Dispatch dispatch task via direct task queue [this=%p]",
567 this);
568 nsCOMPtr<nsIDirectTaskDispatcher> dispatcher =
569 do_QueryInterface(mResponseTarget);
570 if (dispatcher) {
571 SetDispatchRv(dispatcher->DispatchDirectTask(r.forget()));
572 return;
574 NS_WARNING(
575 nsPrintfCString(
576 "Direct Task dispatching not available for thread \"%s\"",
577 PR_GetThreadName(PR_GetCurrentThread()))
578 .get());
579 MOZ_DIAGNOSTIC_ASSERT(
580 false,
581 "mResponseTarget must implement nsIDirectTaskDispatcher for direct "
582 "task dispatching");
585 // Promise consumers are allowed to disconnect the Request object and
586 // then shut down the thread or task queue that the promise result would
587 // be dispatched on. So we unfortunately can't assert that promise
588 // dispatch succeeds. :-(
589 // We do record whether or not it succeeds so that if the ThenValueBase is
590 // then destroyed and it was not disconnected, we can include that
591 // information in the assertion message.
592 SetDispatchRv(mResponseTarget->Dispatch(r.forget()));
595 void Disconnect() override {
596 MOZ_DIAGNOSTIC_ASSERT(mResponseTarget->IsOnCurrentThread());
597 MOZ_DIAGNOSTIC_ASSERT(!Request::mComplete);
598 Request::mDisconnected = true;
600 // We could support rejecting the completion promise on disconnection, but
601 // then we'd need to have some sort of default reject value. The use cases
602 // of disconnection and completion promise chaining seem pretty
603 // orthogonal, so let's use assert against it.
604 MOZ_DIAGNOSTIC_ASSERT(!CompletionPromise());
607 protected:
608 virtual MozPromiseBase* CompletionPromise() const = 0;
609 virtual void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) = 0;
611 void DoResolveOrReject(ResolveOrRejectValue& aValue) {
612 PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic);
613 MOZ_DIAGNOSTIC_ASSERT(mResponseTarget->IsOnCurrentThread());
614 Request::mComplete = true;
615 if (Request::mDisconnected) {
616 PROMISE_LOG(
617 "ThenValue::DoResolveOrReject disconnected - bailing out [this=%p]",
618 this);
619 return;
622 // Invoke the resolve or reject method.
623 DoResolveOrRejectInternal(aValue);
626 void SetDispatchRv(nsresult aRv) {
627 # ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
628 mDispatchRv = Some(aRv);
629 # endif
632 nsCOMPtr<nsISerialEventTarget>
633 mResponseTarget; // May be released on any thread.
634 # ifdef PROMISE_DEBUG
635 uint32_t mMagic1 = sMagic;
636 # endif
637 const char* mCallSite;
638 # ifdef PROMISE_DEBUG
639 uint32_t mMagic2 = sMagic;
640 # endif
641 # ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
642 Maybe<nsresult> mDispatchRv;
643 # endif
647 * We create two overloads for invoking Resolve/Reject Methods so as to
648 * make the resolve/reject value argument "optional".
650 template <typename ThisType, typename MethodType, typename ValueType>
651 static std::enable_if_t<TakesArgument<MethodType>::value,
652 typename detail::MethodTrait<MethodType>::ReturnType>
653 InvokeMethod(ThisType* aThisVal, MethodType aMethod, ValueType&& aValue) {
654 return (aThisVal->*aMethod)(std::forward<ValueType>(aValue));
657 template <typename ThisType, typename MethodType, typename ValueType>
658 static std::enable_if_t<!TakesArgument<MethodType>::value,
659 typename detail::MethodTrait<MethodType>::ReturnType>
660 InvokeMethod(ThisType* aThisVal, MethodType aMethod, ValueType&& aValue) {
661 return (aThisVal->*aMethod)();
664 // Called when promise chaining is supported.
665 template <bool SupportChaining, typename ThisType, typename MethodType,
666 typename ValueType, typename CompletionPromiseType>
667 static std::enable_if_t<SupportChaining, void> InvokeCallbackMethod(
668 ThisType* aThisVal, MethodType aMethod, ValueType&& aValue,
669 CompletionPromiseType&& aCompletionPromise) {
670 auto p = InvokeMethod(aThisVal, aMethod, std::forward<ValueType>(aValue));
671 if (aCompletionPromise) {
672 p->ChainTo(aCompletionPromise.forget(), "<chained completion promise>");
676 // Called when promise chaining is not supported.
677 template <bool SupportChaining, typename ThisType, typename MethodType,
678 typename ValueType, typename CompletionPromiseType>
679 static std::enable_if_t<!SupportChaining, void> InvokeCallbackMethod(
680 ThisType* aThisVal, MethodType aMethod, ValueType&& aValue,
681 CompletionPromiseType&& aCompletionPromise) {
682 MOZ_DIAGNOSTIC_ASSERT(
683 !aCompletionPromise,
684 "Can't do promise chaining for a non-promise-returning method.");
685 InvokeMethod(aThisVal, aMethod, std::forward<ValueType>(aValue));
688 template <typename>
689 class ThenCommand;
691 template <typename...>
692 class ThenValue;
694 template <typename ThisType, typename ResolveMethodType,
695 typename RejectMethodType>
696 class ThenValue<ThisType*, ResolveMethodType, RejectMethodType>
697 : public ThenValueBase {
698 friend class ThenCommand<ThenValue>;
700 using R1 = typename RemoveSmartPointer<
701 typename detail::MethodTrait<ResolveMethodType>::ReturnType>::Type;
702 using R2 = typename RemoveSmartPointer<
703 typename detail::MethodTrait<RejectMethodType>::ReturnType>::Type;
704 using SupportChaining =
705 std::integral_constant<bool, IsMozPromise<R1>::value &&
706 std::is_same_v<R1, R2>>;
708 // Fall back to MozPromise when promise chaining is not supported to make
709 // code compile.
710 using PromiseType =
711 std::conditional_t<SupportChaining::value, R1, MozPromise>;
713 public:
714 ThenValue(nsISerialEventTarget* aResponseTarget, ThisType* aThisVal,
715 ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod,
716 const char* aCallSite)
717 : ThenValueBase(aResponseTarget, aCallSite),
718 mThisVal(aThisVal),
719 mResolveMethod(aResolveMethod),
720 mRejectMethod(aRejectMethod) {}
722 void Disconnect() override {
723 ThenValueBase::Disconnect();
725 // If a Request has been disconnected, we don't guarantee that the
726 // resolve/reject runnable will be dispatched. Null out our refcounted
727 // this-value now so that it's released predictably on the dispatch
728 // thread.
729 mThisVal = nullptr;
732 protected:
733 MozPromiseBase* CompletionPromise() const override {
734 return mCompletionPromise;
737 void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) override {
738 if (aValue.IsResolve()) {
739 InvokeCallbackMethod<SupportChaining::value>(
740 mThisVal.get(), mResolveMethod, MaybeMove(aValue.ResolveValue()),
741 std::move(mCompletionPromise));
742 } else {
743 InvokeCallbackMethod<SupportChaining::value>(
744 mThisVal.get(), mRejectMethod, MaybeMove(aValue.RejectValue()),
745 std::move(mCompletionPromise));
748 // Null out mThisVal after invoking the callback so that any references
749 // are released predictably on the dispatch thread. Otherwise, it would be
750 // released on whatever thread last drops its reference to the ThenValue,
751 // which may or may not be ok.
752 mThisVal = nullptr;
755 private:
756 RefPtr<ThisType>
757 mThisVal; // Only accessed and refcounted on dispatch thread.
758 ResolveMethodType mResolveMethod;
759 RejectMethodType mRejectMethod;
760 RefPtr<typename PromiseType::Private> mCompletionPromise;
763 template <typename ThisType, typename ResolveRejectMethodType>
764 class ThenValue<ThisType*, ResolveRejectMethodType> : public ThenValueBase {
765 friend class ThenCommand<ThenValue>;
767 using R1 = typename RemoveSmartPointer<typename detail::MethodTrait<
768 ResolveRejectMethodType>::ReturnType>::Type;
769 using SupportChaining =
770 std::integral_constant<bool, IsMozPromise<R1>::value>;
772 // Fall back to MozPromise when promise chaining is not supported to make
773 // code compile.
774 using PromiseType =
775 std::conditional_t<SupportChaining::value, R1, MozPromise>;
777 public:
778 ThenValue(nsISerialEventTarget* aResponseTarget, ThisType* aThisVal,
779 ResolveRejectMethodType aResolveRejectMethod,
780 const char* aCallSite)
781 : ThenValueBase(aResponseTarget, aCallSite),
782 mThisVal(aThisVal),
783 mResolveRejectMethod(aResolveRejectMethod) {}
785 void Disconnect() override {
786 ThenValueBase::Disconnect();
788 // If a Request has been disconnected, we don't guarantee that the
789 // resolve/reject runnable will be dispatched. Null out our refcounted
790 // this-value now so that it's released predictably on the dispatch
791 // thread.
792 mThisVal = nullptr;
795 protected:
796 MozPromiseBase* CompletionPromise() const override {
797 return mCompletionPromise;
800 void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) override {
801 InvokeCallbackMethod<SupportChaining::value>(
802 mThisVal.get(), mResolveRejectMethod, MaybeMove(aValue),
803 std::move(mCompletionPromise));
805 // Null out mThisVal after invoking the callback so that any references
806 // are released predictably on the dispatch thread. Otherwise, it would be
807 // released on whatever thread last drops its reference to the ThenValue,
808 // which may or may not be ok.
809 mThisVal = nullptr;
812 private:
813 RefPtr<ThisType>
814 mThisVal; // Only accessed and refcounted on dispatch thread.
815 ResolveRejectMethodType mResolveRejectMethod;
816 RefPtr<typename PromiseType::Private> mCompletionPromise;
819 // NB: We could use std::function here instead of a template if it were
820 // supported. :-(
821 template <typename ResolveFunction, typename RejectFunction>
822 class ThenValue<ResolveFunction, RejectFunction> : public ThenValueBase {
823 friend class ThenCommand<ThenValue>;
825 using R1 = typename RemoveSmartPointer<
826 typename detail::MethodTrait<ResolveFunction>::ReturnType>::Type;
827 using R2 = typename RemoveSmartPointer<
828 typename detail::MethodTrait<RejectFunction>::ReturnType>::Type;
829 using SupportChaining =
830 std::integral_constant<bool, IsMozPromise<R1>::value &&
831 std::is_same_v<R1, R2>>;
833 // Fall back to MozPromise when promise chaining is not supported to make
834 // code compile.
835 using PromiseType =
836 std::conditional_t<SupportChaining::value, R1, MozPromise>;
838 public:
839 ThenValue(nsISerialEventTarget* aResponseTarget,
840 ResolveFunction&& aResolveFunction,
841 RejectFunction&& aRejectFunction, const char* aCallSite)
842 : ThenValueBase(aResponseTarget, aCallSite) {
843 mResolveFunction.emplace(std::move(aResolveFunction));
844 mRejectFunction.emplace(std::move(aRejectFunction));
847 void Disconnect() override {
848 ThenValueBase::Disconnect();
850 // If a Request has been disconnected, we don't guarantee that the
851 // resolve/reject runnable will be dispatched. Destroy our callbacks
852 // now so that any references in closures are released predictable on
853 // the dispatch thread.
854 mResolveFunction.reset();
855 mRejectFunction.reset();
858 protected:
859 MozPromiseBase* CompletionPromise() const override {
860 return mCompletionPromise;
863 void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) override {
864 // Note: The usage of InvokeCallbackMethod here requires that
865 // ResolveFunction/RejectFunction are capture-lambdas (i.e. anonymous
866 // classes with ::operator()), since it allows us to share code more
867 // easily. We could fix this if need be, though it's quite easy to work
868 // around by just capturing something.
869 if (aValue.IsResolve()) {
870 InvokeCallbackMethod<SupportChaining::value>(
871 mResolveFunction.ptr(), &ResolveFunction::operator(),
872 MaybeMove(aValue.ResolveValue()), std::move(mCompletionPromise));
873 } else {
874 InvokeCallbackMethod<SupportChaining::value>(
875 mRejectFunction.ptr(), &RejectFunction::operator(),
876 MaybeMove(aValue.RejectValue()), std::move(mCompletionPromise));
879 // Destroy callbacks after invocation so that any references in closures
880 // are released predictably on the dispatch thread. Otherwise, they would
881 // be released on whatever thread last drops its reference to the
882 // ThenValue, which may or may not be ok.
883 mResolveFunction.reset();
884 mRejectFunction.reset();
887 private:
888 Maybe<ResolveFunction>
889 mResolveFunction; // Only accessed and deleted on dispatch thread.
890 Maybe<RejectFunction>
891 mRejectFunction; // Only accessed and deleted on dispatch thread.
892 RefPtr<typename PromiseType::Private> mCompletionPromise;
895 template <typename ResolveRejectFunction>
896 class ThenValue<ResolveRejectFunction> : public ThenValueBase {
897 friend class ThenCommand<ThenValue>;
899 using R1 = typename RemoveSmartPointer<
900 typename detail::MethodTrait<ResolveRejectFunction>::ReturnType>::Type;
901 using SupportChaining =
902 std::integral_constant<bool, IsMozPromise<R1>::value>;
904 // Fall back to MozPromise when promise chaining is not supported to make
905 // code compile.
906 using PromiseType =
907 std::conditional_t<SupportChaining::value, R1, MozPromise>;
909 public:
910 ThenValue(nsISerialEventTarget* aResponseTarget,
911 ResolveRejectFunction&& aResolveRejectFunction,
912 const char* aCallSite)
913 : ThenValueBase(aResponseTarget, aCallSite) {
914 mResolveRejectFunction.emplace(std::move(aResolveRejectFunction));
917 void Disconnect() override {
918 ThenValueBase::Disconnect();
920 // If a Request has been disconnected, we don't guarantee that the
921 // resolve/reject runnable will be dispatched. Destroy our callbacks
922 // now so that any references in closures are released predictable on
923 // the dispatch thread.
924 mResolveRejectFunction.reset();
927 protected:
928 MozPromiseBase* CompletionPromise() const override {
929 return mCompletionPromise;
932 void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) override {
933 // Note: The usage of InvokeCallbackMethod here requires that
934 // ResolveRejectFunction is capture-lambdas (i.e. anonymous
935 // classes with ::operator()), since it allows us to share code more
936 // easily. We could fix this if need be, though it's quite easy to work
937 // around by just capturing something.
938 InvokeCallbackMethod<SupportChaining::value>(
939 mResolveRejectFunction.ptr(), &ResolveRejectFunction::operator(),
940 MaybeMove(aValue), std::move(mCompletionPromise));
942 // Destroy callbacks after invocation so that any references in closures
943 // are released predictably on the dispatch thread. Otherwise, they would
944 // be released on whatever thread last drops its reference to the
945 // ThenValue, which may or may not be ok.
946 mResolveRejectFunction.reset();
949 private:
950 Maybe<ResolveRejectFunction>
951 mResolveRejectFunction; // Only accessed and deleted on dispatch
952 // thread.
953 RefPtr<typename PromiseType::Private> mCompletionPromise;
956 public:
957 void ThenInternal(already_AddRefed<ThenValueBase> aThenValue,
958 const char* aCallSite) {
959 PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic &&
960 mMagic3 == sMagic && mMagic4 == &mMutex);
961 RefPtr<ThenValueBase> thenValue = aThenValue;
962 MutexAutoLock lock(mMutex);
963 MOZ_DIAGNOSTIC_ASSERT(
964 !IsExclusive || !mHaveRequest,
965 "Using an exclusive promise in a non-exclusive fashion");
966 mHaveRequest = true;
967 PROMISE_LOG("%s invoking Then() [this=%p, aThenValue=%p, isPending=%d]",
968 aCallSite, this, thenValue.get(), (int)IsPending());
969 if (!IsPending()) {
970 thenValue->Dispatch(this);
971 } else {
972 mThenValues.AppendElement(thenValue.forget());
976 protected:
978 * A command object to store all information needed to make a request to
979 * the promise. This allows us to delay the request until further use is
980 * known (whether it is ->Then() again for more promise chaining or ->Track()
981 * to terminate chaining and issue the request).
983 * This allows a unified syntax for promise chaining and disconnection
984 * and feels more like its JS counterpart.
986 template <typename ThenValueType>
987 class ThenCommand {
988 // Allow Promise1::ThenCommand to access the private constructor,
989 // Promise2::ThenCommand(ThenCommand&&).
990 template <typename, typename, bool>
991 friend class MozPromise;
993 using PromiseType = typename ThenValueType::PromiseType;
994 using Private = typename PromiseType::Private;
996 ThenCommand(const char* aCallSite,
997 already_AddRefed<ThenValueType> aThenValue,
998 MozPromise* aReceiver)
999 : mCallSite(aCallSite), mThenValue(aThenValue), mReceiver(aReceiver) {}
1001 ThenCommand(ThenCommand&& aOther) = default;
1003 public:
1004 ~ThenCommand() {
1005 // Issue the request now if the return value of Then() is not used.
1006 if (mThenValue) {
1007 mReceiver->ThenInternal(mThenValue.forget(), mCallSite);
1011 // Allow RefPtr<MozPromise> p = somePromise->Then();
1012 // p->Then(thread1, ...);
1013 // p->Then(thread2, ...);
1014 operator RefPtr<PromiseType>() {
1015 static_assert(
1016 ThenValueType::SupportChaining::value,
1017 "The resolve/reject callback needs to return a RefPtr<MozPromise> "
1018 "in order to do promise chaining.");
1020 // mCompletionPromise must be created before ThenInternal() to avoid race.
1021 RefPtr<Private> p =
1022 new Private("<completion promise>", true /* aIsCompletionPromise */);
1023 mThenValue->mCompletionPromise = p;
1024 // Note ThenInternal() might nullify mCompletionPromise before return.
1025 // So we need to return p instead of mCompletionPromise.
1026 mReceiver->ThenInternal(mThenValue.forget(), mCallSite);
1027 return p;
1030 template <typename... Ts>
1031 auto Then(Ts&&... aArgs) -> decltype(std::declval<PromiseType>().Then(
1032 std::forward<Ts>(aArgs)...)) {
1033 return static_cast<RefPtr<PromiseType>>(*this)->Then(
1034 std::forward<Ts>(aArgs)...);
1037 void Track(MozPromiseRequestHolder<MozPromise>& aRequestHolder) {
1038 aRequestHolder.Track(do_AddRef(mThenValue));
1039 mReceiver->ThenInternal(mThenValue.forget(), mCallSite);
1042 // Allow calling ->Then() again for more promise chaining or ->Track() to
1043 // end chaining and track the request for future disconnection.
1044 ThenCommand* operator->() { return this; }
1046 private:
1047 const char* mCallSite;
1048 RefPtr<ThenValueType> mThenValue;
1049 RefPtr<MozPromise> mReceiver;
1052 public:
1053 template <typename ThisType, typename... Methods,
1054 typename ThenValueType = ThenValue<ThisType*, Methods...>,
1055 typename ReturnType = ThenCommand<ThenValueType>>
1056 ReturnType Then(nsISerialEventTarget* aResponseTarget, const char* aCallSite,
1057 ThisType* aThisVal, Methods... aMethods) {
1058 RefPtr<ThenValueType> thenValue =
1059 new ThenValueType(aResponseTarget, aThisVal, aMethods..., aCallSite);
1060 return ReturnType(aCallSite, thenValue.forget(), this);
1063 template <typename... Functions,
1064 typename ThenValueType = ThenValue<Functions...>,
1065 typename ReturnType = ThenCommand<ThenValueType>>
1066 ReturnType Then(nsISerialEventTarget* aResponseTarget, const char* aCallSite,
1067 Functions&&... aFunctions) {
1068 RefPtr<ThenValueType> thenValue =
1069 new ThenValueType(aResponseTarget, std::move(aFunctions)..., aCallSite);
1070 return ReturnType(aCallSite, thenValue.forget(), this);
1073 void ChainTo(already_AddRefed<Private> aChainedPromise,
1074 const char* aCallSite) {
1075 MutexAutoLock lock(mMutex);
1076 MOZ_DIAGNOSTIC_ASSERT(
1077 !IsExclusive || !mHaveRequest,
1078 "Using an exclusive promise in a non-exclusive fashion");
1079 mHaveRequest = true;
1080 RefPtr<Private> chainedPromise = aChainedPromise;
1081 PROMISE_LOG(
1082 "%s invoking Chain() [this=%p, chainedPromise=%p, isPending=%d]",
1083 aCallSite, this, chainedPromise.get(), (int)IsPending());
1085 // We want to use the same type of dispatching method with the chained
1086 // promises.
1088 // We need to ensure that the UseSynchronousTaskDispatch branch isn't taken
1089 // at compilation time to ensure we're not triggering the static_assert in
1090 // UseSynchronousTaskDispatch method. if constexpr (IsExclusive) ensures
1091 // that.
1092 if (mUseDirectTaskDispatch) {
1093 chainedPromise->UseDirectTaskDispatch(aCallSite);
1094 } else if constexpr (IsExclusive) {
1095 if (mUseSynchronousTaskDispatch) {
1096 chainedPromise->UseSynchronousTaskDispatch(aCallSite);
1098 } else {
1099 chainedPromise->SetTaskPriority(mPriority, aCallSite);
1102 if (!IsPending()) {
1103 ForwardTo(chainedPromise);
1104 } else {
1105 mChainedPromises.AppendElement(chainedPromise);
1109 # ifdef MOZ_WIDGET_ANDROID
1110 // Creates a C++ MozPromise from its Java counterpart, GeckoResult.
1111 [[nodiscard]] static RefPtr<MozPromise> FromGeckoResult(
1112 java::GeckoResult::Param aGeckoResult) {
1113 using jni::GeckoResultCallback;
1114 RefPtr<Private> p = new Private("GeckoResult Glue", false);
1115 auto resolve = GeckoResultCallback::CreateAndAttach<ResolveValueType>(
1116 [p](ResolveValueType&& aArg) {
1117 p->Resolve(MaybeMove(aArg), __func__);
1119 auto reject = GeckoResultCallback::CreateAndAttach<RejectValueType>(
1120 [p](RejectValueType&& aArg) { p->Reject(MaybeMove(aArg), __func__); });
1121 aGeckoResult->NativeThen(resolve, reject);
1122 return p;
1124 # endif
1126 // Note we expose the function AssertIsDead() instead of IsDead() since
1127 // checking IsDead() is a data race in the situation where the request is not
1128 // dead. Therefore we enforce the form |Assert(IsDead())| by exposing
1129 // AssertIsDead() only.
1130 void AssertIsDead() override {
1131 PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic &&
1132 mMagic3 == sMagic && mMagic4 == &mMutex);
1133 MutexAutoLock lock(mMutex);
1134 for (auto&& then : mThenValues) {
1135 then->AssertIsDead();
1137 for (auto&& chained : mChainedPromises) {
1138 chained->AssertIsDead();
1142 bool IsResolved() const { return mValue.IsResolve(); }
1144 protected:
1145 bool IsPending() const { return mValue.IsNothing(); }
1147 ResolveOrRejectValue& Value() {
1148 // This method should only be called once the value has stabilized. As
1149 // such, we don't need to acquire the lock here.
1150 MOZ_DIAGNOSTIC_ASSERT(!IsPending());
1151 return mValue;
1154 void DispatchAll() {
1155 mMutex.AssertCurrentThreadOwns();
1156 for (auto&& thenValue : mThenValues) {
1157 thenValue->Dispatch(this);
1159 mThenValues.Clear();
1161 for (auto&& chainedPromise : mChainedPromises) {
1162 ForwardTo(chainedPromise);
1164 mChainedPromises.Clear();
1167 void ForwardTo(Private* aOther) {
1168 MOZ_ASSERT(!IsPending());
1169 if (mValue.IsResolve()) {
1170 aOther->Resolve(MaybeMove(mValue.ResolveValue()), "<chained promise>");
1171 } else {
1172 aOther->Reject(MaybeMove(mValue.RejectValue()), "<chained promise>");
1176 virtual ~MozPromise() {
1177 PROMISE_LOG("MozPromise::~MozPromise [this=%p]", this);
1178 AssertIsDead();
1179 // We can't guarantee a completion promise will always be revolved or
1180 // rejected since ResolveOrRejectRunnable might not run when dispatch fails.
1181 if (!mIsCompletionPromise) {
1182 MOZ_ASSERT(!IsPending());
1183 MOZ_ASSERT(mThenValues.IsEmpty());
1184 MOZ_ASSERT(mChainedPromises.IsEmpty());
1186 # ifdef PROMISE_DEBUG
1187 mMagic1 = 0;
1188 mMagic2 = 0;
1189 mMagic3 = 0;
1190 mMagic4 = nullptr;
1191 # endif
1194 const char* mCreationSite; // For logging
1195 Mutex mMutex MOZ_UNANNOTATED;
1196 ResolveOrRejectValue mValue;
1197 bool mUseSynchronousTaskDispatch = false;
1198 bool mUseDirectTaskDispatch = false;
1199 uint32_t mPriority = nsIRunnablePriority::PRIORITY_NORMAL;
1200 # ifdef PROMISE_DEBUG
1201 uint32_t mMagic1 = sMagic;
1202 # endif
1203 // Try shows we never have more than 3 elements when IsExclusive is false.
1204 // So '3' is a good value to avoid heap allocation in most cases.
1205 AutoTArray<RefPtr<ThenValueBase>, IsExclusive ? 1 : 3> mThenValues;
1206 # ifdef PROMISE_DEBUG
1207 uint32_t mMagic2 = sMagic;
1208 # endif
1209 nsTArray<RefPtr<Private>> mChainedPromises;
1210 # ifdef PROMISE_DEBUG
1211 uint32_t mMagic3 = sMagic;
1212 # endif
1213 bool mHaveRequest;
1214 const bool mIsCompletionPromise;
1215 # ifdef PROMISE_DEBUG
1216 void* mMagic4;
1217 # endif
1220 template <typename ResolveValueT, typename RejectValueT, bool IsExclusive>
1221 class MozPromise<ResolveValueT, RejectValueT, IsExclusive>::Private
1222 : public MozPromise<ResolveValueT, RejectValueT, IsExclusive> {
1223 public:
1224 explicit Private(const char* aCreationSite, bool aIsCompletionPromise = false)
1225 : MozPromise(aCreationSite, aIsCompletionPromise) {}
1227 template <typename ResolveValueT_>
1228 void Resolve(ResolveValueT_&& aResolveValue, const char* aResolveSite) {
1229 PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic &&
1230 mMagic3 == sMagic && mMagic4 == &mMutex);
1231 MutexAutoLock lock(mMutex);
1232 PROMISE_LOG("%s resolving MozPromise (%p created at %s)", aResolveSite,
1233 this, mCreationSite);
1234 if (!IsPending()) {
1235 PROMISE_LOG(
1236 "%s ignored already resolved or rejected MozPromise (%p created at "
1237 "%s)",
1238 aResolveSite, this, mCreationSite);
1239 return;
1241 mValue.SetResolve(std::forward<ResolveValueT_>(aResolveValue));
1242 DispatchAll();
1245 template <typename RejectValueT_>
1246 void Reject(RejectValueT_&& aRejectValue, const char* aRejectSite) {
1247 PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic &&
1248 mMagic3 == sMagic && mMagic4 == &mMutex);
1249 MutexAutoLock lock(mMutex);
1250 PROMISE_LOG("%s rejecting MozPromise (%p created at %s)", aRejectSite, this,
1251 mCreationSite);
1252 if (!IsPending()) {
1253 PROMISE_LOG(
1254 "%s ignored already resolved or rejected MozPromise (%p created at "
1255 "%s)",
1256 aRejectSite, this, mCreationSite);
1257 return;
1259 mValue.SetReject(std::forward<RejectValueT_>(aRejectValue));
1260 DispatchAll();
1263 template <typename ResolveOrRejectValue_>
1264 void ResolveOrReject(ResolveOrRejectValue_&& aValue, const char* aSite) {
1265 PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic &&
1266 mMagic3 == sMagic && mMagic4 == &mMutex);
1267 MutexAutoLock lock(mMutex);
1268 PROMISE_LOG("%s resolveOrRejecting MozPromise (%p created at %s)", aSite,
1269 this, mCreationSite);
1270 if (!IsPending()) {
1271 PROMISE_LOG(
1272 "%s ignored already resolved or rejected MozPromise (%p created at "
1273 "%s)",
1274 aSite, this, mCreationSite);
1275 return;
1277 mValue = std::forward<ResolveOrRejectValue_>(aValue);
1278 DispatchAll();
1281 // If the caller and target are both on the same thread, run the the resolve
1282 // or reject callback synchronously. Otherwise, the task will be dispatched
1283 // via the target Dispatch method.
1284 void UseSynchronousTaskDispatch(const char* aSite) {
1285 static_assert(
1286 IsExclusive,
1287 "Synchronous dispatch can only be used with exclusive promises");
1288 PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic &&
1289 mMagic3 == sMagic && mMagic4 == &mMutex);
1290 MutexAutoLock lock(mMutex);
1291 PROMISE_LOG("%s UseSynchronousTaskDispatch MozPromise (%p created at %s)",
1292 aSite, this, mCreationSite);
1293 MOZ_ASSERT(IsPending(),
1294 "A Promise must not have been already resolved or rejected to "
1295 "set dispatch state");
1296 mUseSynchronousTaskDispatch = true;
1299 // If the caller and target are both on the same thread, run the
1300 // resolve/reject callback off the direct task queue instead. This avoids a
1301 // full trip to the back of the event queue for each additional asynchronous
1302 // step when using MozPromise, and is similar (but not identical to) the
1303 // microtask semantics of JS promises.
1304 void UseDirectTaskDispatch(const char* aSite) {
1305 PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic &&
1306 mMagic3 == sMagic && mMagic4 == &mMutex);
1307 MutexAutoLock lock(mMutex);
1308 PROMISE_LOG("%s UseDirectTaskDispatch MozPromise (%p created at %s)", aSite,
1309 this, mCreationSite);
1310 MOZ_ASSERT(IsPending(),
1311 "A Promise must not have been already resolved or rejected to "
1312 "set dispatch state");
1313 MOZ_ASSERT(!mUseSynchronousTaskDispatch,
1314 "Promise already set for synchronous dispatch");
1315 mUseDirectTaskDispatch = true;
1318 // If the resolve/reject will be handled on a thread supporting priorities,
1319 // one may want to tweak the priority of the task by passing a
1320 // nsIRunnablePriority::PRIORITY_* to SetTaskPriority.
1321 void SetTaskPriority(uint32_t aPriority, const char* aSite) {
1322 PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic &&
1323 mMagic3 == sMagic && mMagic4 == &mMutex);
1324 MutexAutoLock lock(mMutex);
1325 PROMISE_LOG("%s TaskPriority MozPromise (%p created at %s)", aSite, this,
1326 mCreationSite);
1327 MOZ_ASSERT(IsPending(),
1328 "A Promise must not have been already resolved or rejected to "
1329 "set dispatch state");
1330 MOZ_ASSERT(!mUseSynchronousTaskDispatch,
1331 "Promise already set for synchronous dispatch");
1332 MOZ_ASSERT(!mUseDirectTaskDispatch,
1333 "Promise already set for direct dispatch");
1334 mPriority = aPriority;
1338 // A generic promise type that does the trick for simple use cases.
1339 typedef MozPromise<bool, nsresult, /* IsExclusive = */ true> GenericPromise;
1341 // A generic, non-exclusive promise type that does the trick for simple use
1342 // cases.
1343 typedef MozPromise<bool, nsresult, /* IsExclusive = */ false>
1344 GenericNonExclusivePromise;
1347 * Class to encapsulate a promise for a particular role. Use this as the member
1348 * variable for a class whose method returns a promise.
1350 template <typename PromiseType, typename ImplType>
1351 class MozPromiseHolderBase {
1352 public:
1353 MozPromiseHolderBase() = default;
1355 MozPromiseHolderBase(MozPromiseHolderBase&& aOther) = default;
1356 MozPromiseHolderBase& operator=(MozPromiseHolderBase&& aOther) = default;
1358 ~MozPromiseHolderBase() { MOZ_ASSERT(!mPromise); }
1360 already_AddRefed<PromiseType> Ensure(const char* aMethodName) {
1361 static_cast<ImplType*>(this)->Check();
1362 if (!mPromise) {
1363 mPromise = new (typename PromiseType::Private)(aMethodName);
1365 RefPtr<PromiseType> p = mPromise.get();
1366 return p.forget();
1369 bool IsEmpty() const {
1370 static_cast<const ImplType*>(this)->Check();
1371 return !mPromise;
1374 already_AddRefed<typename PromiseType::Private> Steal() {
1375 static_cast<ImplType*>(this)->Check();
1376 return mPromise.forget();
1379 template <typename ResolveValueType_>
1380 void Resolve(ResolveValueType_&& aResolveValue, const char* aMethodName) {
1381 static_assert(std::is_convertible_v<ResolveValueType_,
1382 typename PromiseType::ResolveValueType>,
1383 "Resolve() argument must be implicitly convertible to "
1384 "MozPromise's ResolveValueT");
1386 static_cast<ImplType*>(this)->Check();
1387 MOZ_ASSERT(mPromise);
1388 mPromise->Resolve(std::forward<ResolveValueType_>(aResolveValue),
1389 aMethodName);
1390 mPromise = nullptr;
1393 template <typename ResolveValueType_>
1394 void ResolveIfExists(ResolveValueType_&& aResolveValue,
1395 const char* aMethodName) {
1396 if (!IsEmpty()) {
1397 Resolve(std::forward<ResolveValueType_>(aResolveValue), aMethodName);
1401 template <typename RejectValueType_>
1402 void Reject(RejectValueType_&& aRejectValue, const char* aMethodName) {
1403 static_assert(std::is_convertible_v<RejectValueType_,
1404 typename PromiseType::RejectValueType>,
1405 "Reject() argument must be implicitly convertible to "
1406 "MozPromise's RejectValueT");
1408 static_cast<ImplType*>(this)->Check();
1409 MOZ_ASSERT(mPromise);
1410 mPromise->Reject(std::forward<RejectValueType_>(aRejectValue), aMethodName);
1411 mPromise = nullptr;
1414 template <typename RejectValueType_>
1415 void RejectIfExists(RejectValueType_&& aRejectValue,
1416 const char* aMethodName) {
1417 if (!IsEmpty()) {
1418 Reject(std::forward<RejectValueType_>(aRejectValue), aMethodName);
1422 template <typename ResolveOrRejectValueType_>
1423 void ResolveOrReject(ResolveOrRejectValueType_&& aValue,
1424 const char* aMethodName) {
1425 static_cast<ImplType*>(this)->Check();
1426 MOZ_ASSERT(mPromise);
1427 mPromise->ResolveOrReject(std::forward<ResolveOrRejectValueType_>(aValue),
1428 aMethodName);
1429 mPromise = nullptr;
1432 template <typename ResolveOrRejectValueType_>
1433 void ResolveOrRejectIfExists(ResolveOrRejectValueType_&& aValue,
1434 const char* aMethodName) {
1435 if (!IsEmpty()) {
1436 ResolveOrReject(std::forward<ResolveOrRejectValueType_>(aValue),
1437 aMethodName);
1441 void UseSynchronousTaskDispatch(const char* aSite) {
1442 MOZ_ASSERT(mPromise);
1443 mPromise->UseSynchronousTaskDispatch(aSite);
1446 void UseDirectTaskDispatch(const char* aSite) {
1447 MOZ_ASSERT(mPromise);
1448 mPromise->UseDirectTaskDispatch(aSite);
1451 void SetTaskPriority(uint32_t aPriority, const char* aSite) {
1452 MOZ_ASSERT(mPromise);
1453 mPromise->SetTaskPriority(aPriority, aSite);
1456 private:
1457 RefPtr<typename PromiseType::Private> mPromise;
1460 template <typename PromiseType>
1461 class MozPromiseHolder
1462 : public MozPromiseHolderBase<PromiseType, MozPromiseHolder<PromiseType>> {
1463 public:
1464 using MozPromiseHolderBase<
1465 PromiseType, MozPromiseHolder<PromiseType>>::MozPromiseHolderBase;
1466 static constexpr void Check(){};
1469 template <typename PromiseType>
1470 class MozMonitoredPromiseHolder
1471 : public MozPromiseHolderBase<PromiseType,
1472 MozMonitoredPromiseHolder<PromiseType>> {
1473 public:
1474 // Provide a Monitor that should always be held when accessing this instance.
1475 explicit MozMonitoredPromiseHolder(Monitor* const aMonitor)
1476 : mMonitor(aMonitor) {
1477 MOZ_ASSERT(aMonitor);
1480 MozMonitoredPromiseHolder(MozMonitoredPromiseHolder&& aOther) = delete;
1481 MozMonitoredPromiseHolder& operator=(MozMonitoredPromiseHolder&& aOther) =
1482 delete;
1484 void Check() const { mMonitor->AssertCurrentThreadOwns(); }
1486 private:
1487 Monitor* const mMonitor;
1491 * Class to encapsulate a MozPromise::Request reference. Use this as the member
1492 * variable for a class waiting on a MozPromise.
1494 template <typename PromiseType>
1495 class MozPromiseRequestHolder {
1496 public:
1497 MozPromiseRequestHolder() = default;
1498 ~MozPromiseRequestHolder() { MOZ_ASSERT(!mRequest); }
1500 void Track(already_AddRefed<typename PromiseType::Request> aRequest) {
1501 MOZ_DIAGNOSTIC_ASSERT(!Exists());
1502 mRequest = aRequest;
1505 void Complete() {
1506 MOZ_DIAGNOSTIC_ASSERT(Exists());
1507 mRequest = nullptr;
1510 // Disconnects and forgets an outstanding promise. The resolve/reject methods
1511 // will never be called.
1512 void Disconnect() {
1513 MOZ_ASSERT(Exists());
1514 mRequest->Disconnect();
1515 mRequest = nullptr;
1518 void DisconnectIfExists() {
1519 if (Exists()) {
1520 Disconnect();
1524 bool Exists() const { return !!mRequest; }
1526 private:
1527 RefPtr<typename PromiseType::Request> mRequest;
1530 // Asynchronous Potentially-Cross-Thread Method Calls.
1532 // This machinery allows callers to schedule a promise-returning function
1533 // (a method and object, or a function object like a lambda) to be invoked
1534 // asynchronously on a given thread, while at the same time receiving a
1535 // promise upon which to invoke Then() immediately. InvokeAsync dispatches a
1536 // task to invoke the function on the proper thread and also chain the
1537 // resulting promise to the one that the caller received, so that resolve/
1538 // reject values are forwarded through.
1540 namespace detail {
1542 // Non-templated base class to allow us to use MOZ_COUNT_{C,D}TOR, which cause
1543 // assertions when used on templated types.
1544 class MethodCallBase {
1545 public:
1546 MOZ_COUNTED_DEFAULT_CTOR(MethodCallBase)
1547 MOZ_COUNTED_DTOR_VIRTUAL(MethodCallBase)
1550 template <typename PromiseType, typename MethodType, typename ThisType,
1551 typename... Storages>
1552 class MethodCall : public MethodCallBase {
1553 public:
1554 template <typename... Args>
1555 MethodCall(MethodType aMethod, ThisType* aThisVal, Args&&... aArgs)
1556 : mMethod(aMethod),
1557 mThisVal(aThisVal),
1558 mArgs(std::forward<Args>(aArgs)...) {
1559 static_assert(sizeof...(Storages) == sizeof...(Args),
1560 "Storages and Args should have equal sizes");
1563 RefPtr<PromiseType> Invoke() { return mArgs.apply(mThisVal.get(), mMethod); }
1565 private:
1566 MethodType mMethod;
1567 RefPtr<ThisType> mThisVal;
1568 RunnableMethodArguments<Storages...> mArgs;
1571 template <typename PromiseType, typename MethodType, typename ThisType,
1572 typename... Storages>
1573 class ProxyRunnable : public CancelableRunnable {
1574 public:
1575 ProxyRunnable(
1576 typename PromiseType::Private* aProxyPromise,
1577 MethodCall<PromiseType, MethodType, ThisType, Storages...>* aMethodCall)
1578 : CancelableRunnable("detail::ProxyRunnable"),
1579 mProxyPromise(aProxyPromise),
1580 mMethodCall(aMethodCall) {}
1582 NS_IMETHOD Run() override {
1583 RefPtr<PromiseType> p = mMethodCall->Invoke();
1584 mMethodCall = nullptr;
1585 p->ChainTo(mProxyPromise.forget(), "<Proxy Promise>");
1586 return NS_OK;
1589 nsresult Cancel() override { return Run(); }
1591 private:
1592 RefPtr<typename PromiseType::Private> mProxyPromise;
1593 UniquePtr<MethodCall<PromiseType, MethodType, ThisType, Storages...>>
1594 mMethodCall;
1597 template <typename... Storages, typename PromiseType, typename ThisType,
1598 typename... ArgTypes, typename... ActualArgTypes>
1599 static RefPtr<PromiseType> InvokeAsyncImpl(
1600 nsISerialEventTarget* aTarget, ThisType* aThisVal, const char* aCallerName,
1601 RefPtr<PromiseType> (ThisType::*aMethod)(ArgTypes...),
1602 ActualArgTypes&&... aArgs) {
1603 MOZ_ASSERT(aTarget);
1605 typedef RefPtr<PromiseType> (ThisType::*MethodType)(ArgTypes...);
1606 typedef detail::MethodCall<PromiseType, MethodType, ThisType, Storages...>
1607 MethodCallType;
1608 typedef detail::ProxyRunnable<PromiseType, MethodType, ThisType, Storages...>
1609 ProxyRunnableType;
1611 MethodCallType* methodCall = new MethodCallType(
1612 aMethod, aThisVal, std::forward<ActualArgTypes>(aArgs)...);
1613 RefPtr<typename PromiseType::Private> p =
1614 new (typename PromiseType::Private)(aCallerName);
1615 RefPtr<ProxyRunnableType> r = new ProxyRunnableType(p, methodCall);
1616 aTarget->Dispatch(r.forget());
1617 return p;
1620 constexpr bool Any() { return false; }
1622 template <typename T1>
1623 constexpr bool Any(T1 a) {
1624 return static_cast<bool>(a);
1627 template <typename T1, typename... Ts>
1628 constexpr bool Any(T1 a, Ts... aOthers) {
1629 return a || Any(aOthers...);
1632 } // namespace detail
1634 // InvokeAsync with explicitly-specified storages.
1635 // See ParameterStorage in nsThreadUtils.h for help.
1636 template <typename... Storages, typename PromiseType, typename ThisType,
1637 typename... ArgTypes, typename... ActualArgTypes,
1638 std::enable_if_t<sizeof...(Storages) != 0, int> = 0>
1639 static RefPtr<PromiseType> InvokeAsync(
1640 nsISerialEventTarget* aTarget, ThisType* aThisVal, const char* aCallerName,
1641 RefPtr<PromiseType> (ThisType::*aMethod)(ArgTypes...),
1642 ActualArgTypes&&... aArgs) {
1643 static_assert(
1644 sizeof...(Storages) == sizeof...(ArgTypes),
1645 "Provided Storages and method's ArgTypes should have equal sizes");
1646 static_assert(sizeof...(Storages) == sizeof...(ActualArgTypes),
1647 "Provided Storages and ActualArgTypes should have equal sizes");
1648 return detail::InvokeAsyncImpl<Storages...>(
1649 aTarget, aThisVal, aCallerName, aMethod,
1650 std::forward<ActualArgTypes>(aArgs)...);
1653 // InvokeAsync with no explicitly-specified storages, will copy arguments and
1654 // then move them out of the runnable into the target method parameters.
1655 template <typename... Storages, typename PromiseType, typename ThisType,
1656 typename... ArgTypes, typename... ActualArgTypes,
1657 std::enable_if_t<sizeof...(Storages) == 0, int> = 0>
1658 static RefPtr<PromiseType> InvokeAsync(
1659 nsISerialEventTarget* aTarget, ThisType* aThisVal, const char* aCallerName,
1660 RefPtr<PromiseType> (ThisType::*aMethod)(ArgTypes...),
1661 ActualArgTypes&&... aArgs) {
1662 static_assert(
1663 !detail::Any(
1664 std::is_pointer_v<std::remove_reference_t<ActualArgTypes>>...),
1665 "Cannot pass pointer types through InvokeAsync, Storages must be "
1666 "provided");
1667 static_assert(sizeof...(ArgTypes) == sizeof...(ActualArgTypes),
1668 "Method's ArgTypes and ActualArgTypes should have equal sizes");
1669 return detail::InvokeAsyncImpl<
1670 StoreCopyPassByRRef<std::decay_t<ActualArgTypes>>...>(
1671 aTarget, aThisVal, aCallerName, aMethod,
1672 std::forward<ActualArgTypes>(aArgs)...);
1675 namespace detail {
1677 template <typename Function, typename PromiseType>
1678 class ProxyFunctionRunnable : public CancelableRunnable {
1679 using FunctionStorage = std::decay_t<Function>;
1681 public:
1682 template <typename F>
1683 ProxyFunctionRunnable(typename PromiseType::Private* aProxyPromise,
1684 F&& aFunction)
1685 : CancelableRunnable("detail::ProxyFunctionRunnable"),
1686 mProxyPromise(aProxyPromise),
1687 mFunction(new FunctionStorage(std::forward<F>(aFunction))) {}
1689 NS_IMETHOD Run() override {
1690 RefPtr<PromiseType> p = (*mFunction)();
1691 mFunction = nullptr;
1692 p->ChainTo(mProxyPromise.forget(), "<Proxy Promise>");
1693 return NS_OK;
1696 nsresult Cancel() override { return Run(); }
1698 private:
1699 RefPtr<typename PromiseType::Private> mProxyPromise;
1700 UniquePtr<FunctionStorage> mFunction;
1703 // Note: The following struct and function are not for public consumption (yet?)
1704 // as we would prefer all calls to pass on-the-spot lambdas (or at least moved
1705 // function objects). They could be moved outside of detail if really needed.
1707 // We prefer getting function objects by non-lvalue-ref (to avoid copying them
1708 // and their captures). This struct is a tag that allows the use of objects
1709 // through lvalue-refs where necessary.
1710 struct AllowInvokeAsyncFunctionLVRef {};
1712 // Invoke a function object (e.g., lambda or std/mozilla::function)
1713 // asynchronously; note that the object will be copied if provided by
1714 // lvalue-ref. Return a promise that the function should eventually resolve or
1715 // reject.
1716 template <typename Function>
1717 static auto InvokeAsync(nsISerialEventTarget* aTarget, const char* aCallerName,
1718 AllowInvokeAsyncFunctionLVRef, Function&& aFunction)
1719 -> decltype(aFunction()) {
1720 static_assert(
1721 IsRefcountedSmartPointer<decltype(aFunction())>::value &&
1722 IsMozPromise<
1723 typename RemoveSmartPointer<decltype(aFunction())>::Type>::value,
1724 "Function object must return RefPtr<MozPromise>");
1725 MOZ_ASSERT(aTarget);
1726 typedef typename RemoveSmartPointer<decltype(aFunction())>::Type PromiseType;
1727 typedef detail::ProxyFunctionRunnable<Function, PromiseType>
1728 ProxyRunnableType;
1730 auto p = MakeRefPtr<typename PromiseType::Private>(aCallerName);
1731 auto r = MakeRefPtr<ProxyRunnableType>(p, std::forward<Function>(aFunction));
1732 aTarget->Dispatch(r.forget());
1733 return p;
1736 } // namespace detail
1738 // Invoke a function object (e.g., lambda) asynchronously.
1739 // Return a promise that the function should eventually resolve or reject.
1740 template <typename Function>
1741 static auto InvokeAsync(nsISerialEventTarget* aTarget, const char* aCallerName,
1742 Function&& aFunction) -> decltype(aFunction()) {
1743 static_assert(!std::is_lvalue_reference_v<Function>,
1744 "Function object must not be passed by lvalue-ref (to avoid "
1745 "unplanned copies); Consider move()ing the object.");
1746 return detail::InvokeAsync(aTarget, aCallerName,
1747 detail::AllowInvokeAsyncFunctionLVRef(),
1748 std::forward<Function>(aFunction));
1751 # undef PROMISE_LOG
1752 # undef PROMISE_ASSERT
1753 # undef PROMISE_DEBUG
1755 } // namespace mozilla
1757 #endif