Line data Source code
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/. */
6 :
7 : #if !defined(MozPromise_h_)
8 : #define MozPromise_h_
9 :
10 : #include "mozilla/IndexSequence.h"
11 : #include "mozilla/Logging.h"
12 : #include "mozilla/Maybe.h"
13 : #include "mozilla/Mutex.h"
14 : #include "mozilla/Monitor.h"
15 : #include "mozilla/Tuple.h"
16 : #include "mozilla/TypeTraits.h"
17 : #include "mozilla/Variant.h"
18 :
19 : #include "nsISerialEventTarget.h"
20 : #include "nsTArray.h"
21 : #include "nsThreadUtils.h"
22 :
23 : #if MOZ_DIAGNOSTIC_ASSERT_ENABLED
24 : #define PROMISE_DEBUG
25 : #endif
26 :
27 : #ifdef PROMISE_DEBUG
28 : #define PROMISE_ASSERT MOZ_RELEASE_ASSERT
29 : #else
30 : #define PROMISE_ASSERT(...) do { } while (0)
31 : #endif
32 :
33 : namespace mozilla {
34 :
35 : extern LazyLogModule gMozPromiseLog;
36 :
37 : #define PROMISE_LOG(x, ...) \
38 : MOZ_LOG(gMozPromiseLog, mozilla::LogLevel::Debug, (x, ##__VA_ARGS__))
39 :
40 : namespace detail {
41 : template <typename F>
42 : struct MethodTraitsHelper : MethodTraitsHelper<decltype(&F::operator())>
43 : {
44 : };
45 : template <typename ThisType, typename Ret, typename... ArgTypes>
46 : struct MethodTraitsHelper<Ret(ThisType::*)(ArgTypes...)>
47 : {
48 : using ReturnType = Ret;
49 : static const size_t ArgSize = sizeof...(ArgTypes);
50 : };
51 : template <typename ThisType, typename Ret, typename... ArgTypes>
52 : struct MethodTraitsHelper<Ret(ThisType::*)(ArgTypes...) const>
53 : {
54 : using ReturnType = Ret;
55 : static const size_t ArgSize = sizeof...(ArgTypes);
56 : };
57 : template <typename ThisType, typename Ret, typename... ArgTypes>
58 : struct MethodTraitsHelper<Ret(ThisType::*)(ArgTypes...) volatile>
59 : {
60 : using ReturnType = Ret;
61 : static const size_t ArgSize = sizeof...(ArgTypes);
62 : };
63 : template <typename ThisType, typename Ret, typename... ArgTypes>
64 : struct MethodTraitsHelper<Ret(ThisType::*)(ArgTypes...) const volatile>
65 : {
66 : using ReturnType = Ret;
67 : static const size_t ArgSize = sizeof...(ArgTypes);
68 : };
69 : template <typename T>
70 : struct MethodTrait : MethodTraitsHelper<typename RemoveReference<T>::Type>
71 : {
72 : };
73 :
74 : } // namespace detail
75 :
76 : template<typename MethodType>
77 : using TakesArgument =
78 : IntegralConstant<bool, detail::MethodTrait<MethodType>::ArgSize != 0>;
79 :
80 : template<typename MethodType, typename TargetType>
81 : using ReturnTypeIs =
82 : IsConvertible<typename detail::MethodTrait<MethodType>::ReturnType, TargetType>;
83 :
84 : template<typename ResolveValueT, typename RejectValueT, bool IsExclusive>
85 : class MozPromise;
86 :
87 : template<typename Return>
88 : struct IsMozPromise : FalseType
89 : {
90 : };
91 :
92 : template<typename ResolveValueT, typename RejectValueT, bool IsExclusive>
93 : struct IsMozPromise<MozPromise<ResolveValueT, RejectValueT, IsExclusive>>
94 : : TrueType
95 : {
96 : };
97 :
98 : /*
99 : * A promise manages an asynchronous request that may or may not be able to be
100 : * fulfilled immediately. When an API returns a promise, the consumer may attach
101 : * callbacks to be invoked (asynchronously, on a specified thread) when the
102 : * request is either completed (resolved) or cannot be completed (rejected).
103 : * Whereas JS promise callbacks are dispatched from Microtask checkpoints,
104 : * MozPromises resolution/rejection make a normal round-trip through the event
105 : * loop, which simplifies their ordering semantics relative to other native code.
106 : *
107 : * MozPromises attempt to mirror the spirit of JS Promises to the extent that
108 : * is possible (and desirable) in C++. While the intent is that MozPromises
109 : * feel familiar to programmers who are accustomed to their JS-implemented cousin,
110 : * we don't shy away from imposing restrictions and adding features that make
111 : * sense for the use cases we encounter.
112 : *
113 : * A MozPromise is ThreadSafe, and may be ->Then()ed on any thread. The Then()
114 : * call accepts resolve and reject callbacks, and returns a magic object which
115 : * will be implicitly converted to a MozPromise::Request or a MozPromise object
116 : * depending on how the return value is used. The magic object serves several
117 : * purposes for the consumer.
118 : *
119 : * (1) When converting to a MozPromise::Request, it allows the caller to
120 : * cancel the delivery of the resolve/reject value if it has not already
121 : * occurred, via Disconnect() (this must be done on the target thread to
122 : * avoid racing).
123 : *
124 : * (2) When converting to a MozPromise (which is called a completion promise),
125 : * it allows promise chaining so ->Then() can be called again to attach
126 : * more resolve and reject callbacks. If the resolve/reject callback
127 : * returns a new MozPromise, that promise is chained to the completion
128 : * promise, such that its resolve/reject value will be forwarded along
129 : * when it arrives. If the resolve/reject callback returns void, the
130 : * completion promise is resolved/rejected with the same value that was
131 : * passed to the callback.
132 : *
133 : * The MozPromise APIs skirt traditional XPCOM convention by returning nsRefPtrs
134 : * (rather than already_AddRefed) from various methods. This is done to allow elegant
135 : * chaining of calls without cluttering up the code with intermediate variables, and
136 : * without introducing separate API variants for callers that want a return value
137 : * (from, say, ->Then()) from those that don't.
138 : *
139 : * When IsExclusive is true, the MozPromise does a release-mode assertion that
140 : * there is at most one call to either Then(...) or ChainTo(...).
141 : */
142 :
143 19 : class MozPromiseRefcountable
144 : {
145 : public:
146 100 : NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MozPromiseRefcountable)
147 : protected:
148 19 : virtual ~MozPromiseRefcountable() {}
149 : };
150 :
151 26 : class MozPromiseBase : public MozPromiseRefcountable
152 : {
153 : public:
154 : virtual void AssertIsDead() = 0;
155 : };
156 :
157 : template<typename T> class MozPromiseHolder;
158 : template<typename T> class MozPromiseRequestHolder;
159 : template<typename ResolveValueT, typename RejectValueT, bool IsExclusive>
160 : class MozPromise : public MozPromiseBase
161 : {
162 : static const uint32_t sMagic = 0xcecace11;
163 :
164 : // Return a |T&&| to enable move when IsExclusive is true or
165 : // a |const T&| to enforce copy otherwise.
166 : template <typename T,
167 : typename R = typename Conditional<IsExclusive, T&&, const T&>::Type>
168 11 : static R MaybeMove(T& aX)
169 : {
170 11 : return static_cast<R>(aX);
171 : }
172 :
173 : public:
174 : typedef ResolveValueT ResolveValueType;
175 : typedef RejectValueT RejectValueType;
176 26 : class ResolveOrRejectValue
177 : {
178 : public:
179 : template<typename ResolveValueType_>
180 13 : void SetResolve(ResolveValueType_&& aResolveValue)
181 : {
182 13 : MOZ_ASSERT(IsNothing());
183 13 : mValue = Storage(VariantIndex<ResolveIndex>{},
184 : Forward<ResolveValueType_>(aResolveValue));
185 13 : }
186 :
187 : template<typename RejectValueType_>
188 0 : void SetReject(RejectValueType_&& aRejectValue)
189 : {
190 0 : MOZ_ASSERT(IsNothing());
191 0 : mValue = Storage(VariantIndex<RejectIndex>{},
192 : Forward<RejectValueType_>(aRejectValue));
193 0 : }
194 :
195 : template<typename ResolveValueType_>
196 : static ResolveOrRejectValue MakeResolve(ResolveValueType_&& aResolveValue)
197 : {
198 : ResolveOrRejectValue val;
199 : val.SetResolve(Forward<ResolveValueType_>(aResolveValue));
200 : return val;
201 : }
202 :
203 : template<typename RejectValueType_>
204 : static ResolveOrRejectValue MakeReject(RejectValueType_&& aRejectValue)
205 : {
206 : ResolveOrRejectValue val;
207 : val.SetReject(Forward<RejectValueType_>(aRejectValue));
208 : return val;
209 : }
210 :
211 11 : bool IsResolve() const { return mValue.template is<ResolveIndex>(); }
212 : bool IsReject() const { return mValue.template is<RejectIndex>(); }
213 71 : bool IsNothing() const { return mValue.template is<NothingIndex>(); }
214 :
215 : const ResolveValueType& ResolveValue() const
216 : {
217 : return mValue.template as<ResolveIndex>();
218 : }
219 11 : ResolveValueType& ResolveValue()
220 : {
221 11 : return mValue.template as<ResolveIndex>();
222 : }
223 : const RejectValueType& RejectValue() const
224 : {
225 : return mValue.template as<RejectIndex>();
226 : }
227 0 : RejectValueType& RejectValue()
228 : {
229 0 : return mValue.template as<RejectIndex>();
230 : }
231 :
232 : private:
233 : enum { NothingIndex, ResolveIndex, RejectIndex };
234 : using Storage = Variant<Nothing, ResolveValueType, RejectValueType>;
235 : Storage mValue = Storage(VariantIndex<NothingIndex>{});
236 : };
237 :
238 : protected:
239 : // MozPromise is the public type, and never constructed directly. Construct
240 : // a MozPromise::Private, defined below.
241 13 : MozPromise(const char* aCreationSite, bool aIsCompletionPromise)
242 : : mCreationSite(aCreationSite)
243 : , mMutex("MozPromise Mutex")
244 : , mHaveRequest(false)
245 : , mIsCompletionPromise(aIsCompletionPromise)
246 : #ifdef PROMISE_DEBUG
247 13 : , mMagic4(&mMutex)
248 : #endif
249 : {
250 13 : PROMISE_LOG("%s creating MozPromise (%p)", mCreationSite, this);
251 13 : }
252 :
253 : public:
254 : // MozPromise::Private allows us to separate the public interface (upon which
255 : // consumers of the promise may invoke methods like Then()) from the private
256 : // interface (upon which the creator of the promise may invoke Resolve() or
257 : // Reject()). APIs should create and store a MozPromise::Private (usually
258 : // via a MozPromiseHolder), and return a MozPromise to consumers.
259 : //
260 : // NB: We can include the definition of this class inline once B2G ICS is gone.
261 : class Private;
262 :
263 : template<typename ResolveValueType_>
264 : static RefPtr<MozPromise>
265 4 : CreateAndResolve(ResolveValueType_&& aResolveValue, const char* aResolveSite)
266 : {
267 8 : RefPtr<typename MozPromise::Private> p = new MozPromise::Private(aResolveSite);
268 4 : p->Resolve(Forward<ResolveValueType_>(aResolveValue), aResolveSite);
269 8 : return p.forget();
270 : }
271 :
272 : template<typename RejectValueType_>
273 : static RefPtr<MozPromise>
274 0 : CreateAndReject(RejectValueType_&& aRejectValue, const char* aRejectSite)
275 : {
276 0 : RefPtr<typename MozPromise::Private> p = new MozPromise::Private(aRejectSite);
277 0 : p->Reject(Forward<RejectValueType_>(aRejectValue), aRejectSite);
278 0 : return p.forget();
279 : }
280 :
281 : typedef MozPromise<nsTArray<ResolveValueType>, RejectValueType, IsExclusive> AllPromiseType;
282 : private:
283 0 : class AllPromiseHolder : public MozPromiseRefcountable
284 : {
285 : public:
286 0 : explicit AllPromiseHolder(size_t aDependentPromises)
287 0 : : mPromise(new typename AllPromiseType::Private(__func__))
288 0 : , mOutstandingPromises(aDependentPromises)
289 : {
290 0 : MOZ_ASSERT(aDependentPromises > 0);
291 0 : mResolveValues.SetLength(aDependentPromises);
292 0 : }
293 :
294 0 : void Resolve(size_t aIndex, ResolveValueType&& aResolveValue)
295 : {
296 0 : if (!mPromise) {
297 : // Already rejected.
298 0 : return;
299 : }
300 :
301 0 : mResolveValues[aIndex].emplace(Move(aResolveValue));
302 0 : if (--mOutstandingPromises == 0) {
303 0 : nsTArray<ResolveValueType> resolveValues;
304 0 : resolveValues.SetCapacity(mResolveValues.Length());
305 0 : for (size_t i = 0; i < mResolveValues.Length(); ++i) {
306 0 : resolveValues.AppendElement(Move(mResolveValues[i].ref()));
307 : }
308 :
309 0 : mPromise->Resolve(Move(resolveValues), __func__);
310 0 : mPromise = nullptr;
311 0 : mResolveValues.Clear();
312 : }
313 : }
314 :
315 0 : void Reject(RejectValueType&& aRejectValue)
316 : {
317 0 : if (!mPromise) {
318 : // Already rejected.
319 0 : return;
320 : }
321 :
322 0 : mPromise->Reject(Move(aRejectValue), __func__);
323 0 : mPromise = nullptr;
324 0 : mResolveValues.Clear();
325 : }
326 :
327 0 : AllPromiseType* Promise() { return mPromise; }
328 :
329 : private:
330 : nsTArray<Maybe<ResolveValueType>> mResolveValues;
331 : RefPtr<typename AllPromiseType::Private> mPromise;
332 : size_t mOutstandingPromises;
333 : };
334 : public:
335 :
336 0 : static RefPtr<AllPromiseType> All(nsISerialEventTarget* aProcessingTarget, nsTArray<RefPtr<MozPromise>>& aPromises)
337 : {
338 0 : if (aPromises.Length() == 0) {
339 0 : return AllPromiseType::CreateAndResolve(nsTArray<ResolveValueType>(), __func__);
340 : }
341 :
342 0 : RefPtr<AllPromiseHolder> holder = new AllPromiseHolder(aPromises.Length());
343 0 : for (size_t i = 0; i < aPromises.Length(); ++i) {
344 0 : aPromises[i]->Then(aProcessingTarget, __func__,
345 0 : [holder, i] (ResolveValueType aResolveValue) -> void { holder->Resolve(i, Move(aResolveValue)); },
346 0 : [holder] (RejectValueType aRejectValue) -> void { holder->Reject(Move(aRejectValue)); }
347 : );
348 : }
349 0 : return holder->Promise();
350 : }
351 :
352 : class Request : public MozPromiseRefcountable
353 : {
354 : public:
355 : virtual void Disconnect() = 0;
356 :
357 : protected:
358 6 : Request() : mComplete(false), mDisconnected(false) {}
359 6 : virtual ~Request() {}
360 :
361 : bool mComplete;
362 : bool mDisconnected;
363 : };
364 :
365 : protected:
366 :
367 : /*
368 : * A ThenValue tracks a single consumer waiting on the promise. When a consumer
369 : * invokes promise->Then(...), a ThenValue is created. Once the Promise is
370 : * resolved or rejected, a {Resolve,Reject}Runnable is dispatched, which
371 : * invokes the resolve/reject method and then deletes the ThenValue.
372 : */
373 : class ThenValueBase : public Request
374 : {
375 : friend class MozPromise;
376 : static const uint32_t sMagic = 0xfadece11;
377 :
378 : public:
379 : class ResolveOrRejectRunnable : public CancelableRunnable
380 : {
381 : public:
382 6 : ResolveOrRejectRunnable(ThenValueBase* aThenValue, MozPromise* aPromise)
383 : : CancelableRunnable(
384 : "MozPromise::ThenValueBase::ResolveOrRejectRunnable")
385 : , mThenValue(aThenValue)
386 6 : , mPromise(aPromise)
387 : {
388 6 : MOZ_DIAGNOSTIC_ASSERT(!mPromise->IsPending());
389 6 : }
390 :
391 12 : ~ResolveOrRejectRunnable()
392 : {
393 6 : if (mThenValue) {
394 0 : mThenValue->AssertIsDead();
395 : }
396 24 : }
397 :
398 6 : NS_IMETHOD Run() override
399 : {
400 6 : PROMISE_LOG("ResolveOrRejectRunnable::Run() [this=%p]", this);
401 6 : mThenValue->DoResolveOrReject(mPromise->Value());
402 6 : mThenValue = nullptr;
403 6 : mPromise = nullptr;
404 6 : return NS_OK;
405 : }
406 :
407 0 : nsresult Cancel() override
408 : {
409 0 : return Run();
410 : }
411 :
412 : private:
413 : RefPtr<ThenValueBase> mThenValue;
414 : RefPtr<MozPromise> mPromise;
415 : };
416 :
417 6 : ThenValueBase(nsISerialEventTarget* aResponseTarget,
418 : const char* aCallSite)
419 : : mResponseTarget(aResponseTarget)
420 6 : , mCallSite(aCallSite)
421 : {
422 6 : MOZ_ASSERT(aResponseTarget);
423 6 : }
424 :
425 : #ifdef PROMISE_DEBUG
426 6 : ~ThenValueBase()
427 : {
428 6 : mMagic1 = 0;
429 6 : mMagic2 = 0;
430 12 : }
431 : #endif
432 :
433 0 : void AssertIsDead()
434 : {
435 0 : PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic);
436 : // We want to assert that this ThenValues is dead - that is to say, that
437 : // there are no consumers waiting for the result. In the case of a normal
438 : // ThenValue, we check that it has been disconnected, which is the way
439 : // that the consumer signals that it no longer wishes to hear about the
440 : // result. If this ThenValue has a completion promise (which is mutually
441 : // exclusive with being disconnectable), we recursively assert that every
442 : // ThenValue associated with the completion promise is dead.
443 0 : if (MozPromiseBase* p = CompletionPromise()) {
444 0 : p->AssertIsDead();
445 : } else {
446 0 : MOZ_DIAGNOSTIC_ASSERT(Request::mDisconnected);
447 : }
448 0 : }
449 :
450 6 : void Dispatch(MozPromise *aPromise)
451 : {
452 6 : PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic);
453 6 : aPromise->mMutex.AssertCurrentThreadOwns();
454 6 : MOZ_ASSERT(!aPromise->IsPending());
455 :
456 12 : nsCOMPtr<nsIRunnable> r = new ResolveOrRejectRunnable(this, aPromise);
457 6 : PROMISE_LOG("%s Then() call made from %s [Runnable=%p, Promise=%p, ThenValue=%p]",
458 : aPromise->mValue.IsResolve() ? "Resolving" : "Rejecting", mCallSite,
459 : r.get(), aPromise, this);
460 :
461 : // Promise consumers are allowed to disconnect the Request object and
462 : // then shut down the thread or task queue that the promise result would
463 : // be dispatched on. So we unfortunately can't assert that promise
464 : // dispatch succeeds. :-(
465 6 : mResponseTarget->Dispatch(r.forget());
466 6 : }
467 :
468 0 : void Disconnect() override
469 : {
470 0 : MOZ_DIAGNOSTIC_ASSERT(mResponseTarget->IsOnCurrentThread());
471 0 : MOZ_DIAGNOSTIC_ASSERT(!Request::mComplete);
472 0 : Request::mDisconnected = true;
473 :
474 : // We could support rejecting the completion promise on disconnection, but
475 : // then we'd need to have some sort of default reject value. The use cases
476 : // of disconnection and completion promise chaining seem pretty orthogonal,
477 : // so let's use assert against it.
478 0 : MOZ_DIAGNOSTIC_ASSERT(!CompletionPromise());
479 0 : }
480 :
481 : protected:
482 : virtual MozPromiseBase* CompletionPromise() const = 0;
483 : virtual void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) = 0;
484 :
485 6 : void DoResolveOrReject(ResolveOrRejectValue& aValue)
486 : {
487 6 : PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic);
488 6 : MOZ_DIAGNOSTIC_ASSERT(mResponseTarget->IsOnCurrentThread());
489 6 : Request::mComplete = true;
490 6 : if (Request::mDisconnected) {
491 0 : PROMISE_LOG("ThenValue::DoResolveOrReject disconnected - bailing out [this=%p]", this);
492 0 : return;
493 : }
494 :
495 : // Invoke the resolve or reject method.
496 6 : DoResolveOrRejectInternal(aValue);
497 : }
498 :
499 : nsCOMPtr<nsISerialEventTarget> mResponseTarget; // May be released on any thread.
500 : #ifdef PROMISE_DEBUG
501 : uint32_t mMagic1 = sMagic;
502 : #endif
503 : const char* mCallSite;
504 : #ifdef PROMISE_DEBUG
505 : uint32_t mMagic2 = sMagic;
506 : #endif
507 : };
508 :
509 : /*
510 : * We create two overloads for invoking Resolve/Reject Methods so as to
511 : * make the resolve/reject value argument "optional".
512 : */
513 : template<typename ThisType, typename MethodType, typename ValueType>
514 : static typename EnableIf<
515 : TakesArgument<MethodType>::value,
516 : typename detail::MethodTrait<MethodType>::ReturnType>::Type
517 2 : InvokeMethod(ThisType* aThisVal, MethodType aMethod, ValueType&& aValue)
518 : {
519 2 : return (aThisVal->*aMethod)(Forward<ValueType>(aValue));
520 : }
521 :
522 : template<typename ThisType, typename MethodType, typename ValueType>
523 : static typename EnableIf<
524 : !TakesArgument<MethodType>::value,
525 : typename detail::MethodTrait<MethodType>::ReturnType>::Type
526 4 : InvokeMethod(ThisType* aThisVal, MethodType aMethod, ValueType&& aValue)
527 : {
528 4 : return (aThisVal->*aMethod)();
529 : }
530 :
531 : // Called when promise chaining is supported.
532 : template<bool SupportChaining,
533 : typename ThisType,
534 : typename MethodType,
535 : typename ValueType,
536 : typename CompletionPromiseType>
537 2 : static typename EnableIf<SupportChaining, void>::Type InvokeCallbackMethod(
538 : ThisType* aThisVal,
539 : MethodType aMethod,
540 : ValueType&& aValue,
541 : CompletionPromiseType&& aCompletionPromise)
542 : {
543 4 : auto p = InvokeMethod(aThisVal, aMethod, Forward<ValueType>(aValue));
544 2 : if (aCompletionPromise) {
545 2 : p->ChainTo(aCompletionPromise.forget(), "<chained completion promise>");
546 : }
547 2 : }
548 :
549 : // Called when promise chaining is not supported.
550 : template<bool SupportChaining,
551 : typename ThisType,
552 : typename MethodType,
553 : typename ValueType,
554 : typename CompletionPromiseType>
555 4 : static typename EnableIf<!SupportChaining, void>::Type InvokeCallbackMethod(
556 : ThisType* aThisVal,
557 : MethodType aMethod,
558 : ValueType&& aValue,
559 : CompletionPromiseType&& aCompletionPromise)
560 : {
561 4 : MOZ_DIAGNOSTIC_ASSERT(
562 : !aCompletionPromise,
563 : "Can't do promise chaining for a non-promise-returning method.");
564 4 : InvokeMethod(aThisVal, aMethod, Forward<ValueType>(aValue));
565 4 : }
566 :
567 : template<typename>
568 : class ThenCommand;
569 :
570 : template<typename...>
571 : class ThenValue;
572 :
573 : template<typename ThisType,
574 : typename ResolveMethodType,
575 : typename RejectMethodType>
576 0 : class ThenValue<ThisType*, ResolveMethodType, RejectMethodType>
577 : : public ThenValueBase
578 : {
579 : friend class ThenCommand<ThenValue>;
580 :
581 : using R1 = typename RemoveSmartPointer<
582 : typename detail::MethodTrait<ResolveMethodType>::ReturnType>::Type;
583 : using R2 = typename RemoveSmartPointer<
584 : typename detail::MethodTrait<RejectMethodType>::ReturnType>::Type;
585 : using SupportChaining =
586 : IntegralConstant<bool, IsMozPromise<R1>::value && IsSame<R1, R2>::value>;
587 :
588 : // Fall back to MozPromise when promise chaining is not supported to make code compile.
589 : using PromiseType =
590 : typename Conditional<SupportChaining::value, R1, MozPromise>::Type;
591 :
592 : public:
593 0 : ThenValue(nsISerialEventTarget* aResponseTarget,
594 : ThisType* aThisVal,
595 : ResolveMethodType aResolveMethod,
596 : RejectMethodType aRejectMethod,
597 : const char* aCallSite)
598 : : ThenValueBase(aResponseTarget, aCallSite)
599 : , mThisVal(aThisVal)
600 : , mResolveMethod(aResolveMethod)
601 0 : , mRejectMethod(aRejectMethod)
602 : {
603 0 : }
604 :
605 0 : void Disconnect() override
606 : {
607 0 : ThenValueBase::Disconnect();
608 :
609 : // If a Request has been disconnected, we don't guarantee that the
610 : // resolve/reject runnable will be dispatched. Null out our refcounted
611 : // this-value now so that it's released predictably on the dispatch thread.
612 0 : mThisVal = nullptr;
613 0 : }
614 :
615 : protected:
616 0 : MozPromiseBase* CompletionPromise() const override
617 : {
618 0 : return mCompletionPromise;
619 : }
620 :
621 0 : void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) override
622 : {
623 0 : if (aValue.IsResolve()) {
624 0 : InvokeCallbackMethod<SupportChaining::value>(
625 : mThisVal.get(),
626 : mResolveMethod,
627 0 : MaybeMove(aValue.ResolveValue()),
628 0 : Move(mCompletionPromise));
629 : } else {
630 0 : InvokeCallbackMethod<SupportChaining::value>(
631 : mThisVal.get(),
632 : mRejectMethod,
633 0 : MaybeMove(aValue.RejectValue()),
634 0 : Move(mCompletionPromise));
635 : }
636 :
637 : // Null out mThisVal after invoking the callback so that any references are
638 : // released predictably on the dispatch thread. Otherwise, it would be
639 : // released on whatever thread last drops its reference to the ThenValue,
640 : // which may or may not be ok.
641 0 : mThisVal = nullptr;
642 0 : }
643 :
644 : private:
645 : RefPtr<ThisType> mThisVal; // Only accessed and refcounted on dispatch thread.
646 : ResolveMethodType mResolveMethod;
647 : RejectMethodType mRejectMethod;
648 : RefPtr<typename PromiseType::Private> mCompletionPromise;
649 : };
650 :
651 : template<typename ThisType, typename ResolveRejectMethodType>
652 : class ThenValue<ThisType*, ResolveRejectMethodType> : public ThenValueBase
653 : {
654 : friend class ThenCommand<ThenValue>;
655 :
656 : using R1 = typename RemoveSmartPointer<
657 : typename detail::MethodTrait<ResolveRejectMethodType>::ReturnType>::Type;
658 : using SupportChaining = IntegralConstant<bool, IsMozPromise<R1>::value>;
659 :
660 : // Fall back to MozPromise when promise chaining is not supported to make code compile.
661 : using PromiseType =
662 : typename Conditional<SupportChaining::value, R1, MozPromise>::Type;
663 :
664 : public:
665 : ThenValue(nsISerialEventTarget* aResponseTarget,
666 : ThisType* aThisVal,
667 : ResolveRejectMethodType aResolveRejectMethod,
668 : const char* aCallSite)
669 : : ThenValueBase(aResponseTarget, aCallSite)
670 : , mThisVal(aThisVal)
671 : , mResolveRejectMethod(aResolveRejectMethod)
672 : {}
673 :
674 : void Disconnect() override
675 : {
676 : ThenValueBase::Disconnect();
677 :
678 : // If a Request has been disconnected, we don't guarantee that the
679 : // resolve/reject runnable will be dispatched. Null out our refcounted
680 : // this-value now so that it's released predictably on the dispatch thread.
681 : mThisVal = nullptr;
682 : }
683 :
684 : protected:
685 : MozPromiseBase* CompletionPromise() const override
686 : {
687 : return mCompletionPromise;
688 : }
689 :
690 : void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) override
691 : {
692 : InvokeCallbackMethod<SupportChaining::value>(mThisVal.get(),
693 : mResolveRejectMethod,
694 : MaybeMove(aValue),
695 : Move(mCompletionPromise));
696 :
697 : // Null out mThisVal after invoking the callback so that any references are
698 : // released predictably on the dispatch thread. Otherwise, it would be
699 : // released on whatever thread last drops its reference to the ThenValue,
700 : // which may or may not be ok.
701 : mThisVal = nullptr;
702 : }
703 :
704 : private:
705 : RefPtr<ThisType> mThisVal; // Only accessed and refcounted on dispatch thread.
706 : ResolveRejectMethodType mResolveRejectMethod;
707 : RefPtr<typename PromiseType::Private> mCompletionPromise;
708 : };
709 :
710 : // NB: We could use std::function here instead of a template if it were supported. :-(
711 : template<typename ResolveFunction, typename RejectFunction>
712 18 : class ThenValue<ResolveFunction, RejectFunction> : public ThenValueBase
713 : {
714 : friend class ThenCommand<ThenValue>;
715 :
716 : using R1 = typename RemoveSmartPointer<
717 : typename detail::MethodTrait<ResolveFunction>::ReturnType>::Type;
718 : using R2 = typename RemoveSmartPointer<
719 : typename detail::MethodTrait<RejectFunction>::ReturnType>::Type;
720 : using SupportChaining =
721 : IntegralConstant<bool, IsMozPromise<R1>::value && IsSame<R1, R2>::value>;
722 :
723 : // Fall back to MozPromise when promise chaining is not supported to make code compile.
724 : using PromiseType =
725 : typename Conditional<SupportChaining::value, R1, MozPromise>::Type;
726 :
727 : public:
728 6 : ThenValue(nsISerialEventTarget* aResponseTarget,
729 : ResolveFunction&& aResolveFunction,
730 : RejectFunction&& aRejectFunction,
731 : const char* aCallSite)
732 6 : : ThenValueBase(aResponseTarget, aCallSite)
733 : {
734 6 : mResolveFunction.emplace(Move(aResolveFunction));
735 6 : mRejectFunction.emplace(Move(aRejectFunction));
736 6 : }
737 :
738 0 : void Disconnect() override
739 : {
740 0 : ThenValueBase::Disconnect();
741 :
742 : // If a Request has been disconnected, we don't guarantee that the
743 : // resolve/reject runnable will be dispatched. Destroy our callbacks
744 : // now so that any references in closures are released predictable on
745 : // the dispatch thread.
746 0 : mResolveFunction.reset();
747 0 : mRejectFunction.reset();
748 0 : }
749 :
750 : protected:
751 0 : MozPromiseBase* CompletionPromise() const override
752 : {
753 0 : return mCompletionPromise;
754 : }
755 :
756 6 : void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) override
757 : {
758 : // Note: The usage of InvokeCallbackMethod here requires that
759 : // ResolveFunction/RejectFunction are capture-lambdas (i.e. anonymous
760 : // classes with ::operator()), since it allows us to share code more easily.
761 : // We could fix this if need be, though it's quite easy to work around by
762 : // just capturing something.
763 6 : if (aValue.IsResolve()) {
764 6 : InvokeCallbackMethod<SupportChaining::value>(
765 : mResolveFunction.ptr(),
766 : &ResolveFunction::operator(),
767 6 : MaybeMove(aValue.ResolveValue()),
768 6 : Move(mCompletionPromise));
769 : } else {
770 0 : InvokeCallbackMethod<SupportChaining::value>(
771 : mRejectFunction.ptr(),
772 : &RejectFunction::operator(),
773 0 : MaybeMove(aValue.RejectValue()),
774 0 : Move(mCompletionPromise));
775 : }
776 :
777 : // Destroy callbacks after invocation so that any references in closures are
778 : // released predictably on the dispatch thread. Otherwise, they would be
779 : // released on whatever thread last drops its reference to the ThenValue,
780 : // which may or may not be ok.
781 6 : mResolveFunction.reset();
782 6 : mRejectFunction.reset();
783 6 : }
784 :
785 : private:
786 : Maybe<ResolveFunction> mResolveFunction; // Only accessed and deleted on dispatch thread.
787 : Maybe<RejectFunction> mRejectFunction; // Only accessed and deleted on dispatch thread.
788 : RefPtr<typename PromiseType::Private> mCompletionPromise;
789 : };
790 :
791 : template<typename ResolveRejectFunction>
792 0 : class ThenValue<ResolveRejectFunction> : public ThenValueBase
793 : {
794 : friend class ThenCommand<ThenValue>;
795 :
796 : using R1 = typename RemoveSmartPointer<
797 : typename detail::MethodTrait<ResolveRejectFunction>::ReturnType>::Type;
798 : using SupportChaining = IntegralConstant<bool, IsMozPromise<R1>::value>;
799 :
800 : // Fall back to MozPromise when promise chaining is not supported to make code compile.
801 : using PromiseType =
802 : typename Conditional<SupportChaining::value, R1, MozPromise>::Type;
803 :
804 : public:
805 0 : ThenValue(nsISerialEventTarget* aResponseTarget,
806 : ResolveRejectFunction&& aResolveRejectFunction,
807 : const char* aCallSite)
808 0 : : ThenValueBase(aResponseTarget, aCallSite)
809 : {
810 0 : mResolveRejectFunction.emplace(Move(aResolveRejectFunction));
811 0 : }
812 :
813 0 : void Disconnect() override
814 : {
815 0 : ThenValueBase::Disconnect();
816 :
817 : // If a Request has been disconnected, we don't guarantee that the
818 : // resolve/reject runnable will be dispatched. Destroy our callbacks
819 : // now so that any references in closures are released predictable on
820 : // the dispatch thread.
821 0 : mResolveRejectFunction.reset();
822 0 : }
823 :
824 : protected:
825 0 : MozPromiseBase* CompletionPromise() const override
826 : {
827 0 : return mCompletionPromise;
828 : }
829 :
830 0 : void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) override
831 : {
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 easily.
835 : // We could fix this if need be, though it's quite easy to work around by
836 : // just capturing something.
837 0 : InvokeCallbackMethod<SupportChaining::value>(
838 : mResolveRejectFunction.ptr(),
839 : &ResolveRejectFunction::operator(),
840 : MaybeMove(aValue),
841 0 : Move(mCompletionPromise));
842 :
843 : // Destroy callbacks after invocation so that any references in closures are
844 : // released predictably on the dispatch thread. Otherwise, they would be
845 : // released on whatever thread last drops its reference to the ThenValue,
846 : // which may or may not be ok.
847 0 : mResolveRejectFunction.reset();
848 0 : }
849 :
850 : private:
851 : Maybe<ResolveRejectFunction> mResolveRejectFunction; // Only accessed and deleted on dispatch thread.
852 : RefPtr<typename PromiseType::Private> mCompletionPromise;
853 : };
854 :
855 : public:
856 6 : void ThenInternal(already_AddRefed<ThenValueBase> aThenValue,
857 : const char* aCallSite)
858 : {
859 6 : PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic && mMagic3 == sMagic && mMagic4 == &mMutex);
860 12 : RefPtr<ThenValueBase> thenValue = aThenValue;
861 12 : MutexAutoLock lock(mMutex);
862 0 : MOZ_DIAGNOSTIC_ASSERT(!IsExclusive || !mHaveRequest);
863 6 : mHaveRequest = true;
864 6 : PROMISE_LOG("%s invoking Then() [this=%p, aThenValue=%p, isPending=%d]",
865 : aCallSite,
866 : this,
867 : thenValue.get(),
868 : (int)IsPending());
869 6 : if (!IsPending()) {
870 0 : thenValue->Dispatch(this);
871 : } else {
872 6 : mThenValues.AppendElement(thenValue.forget());
873 : }
874 6 : }
875 :
876 : protected:
877 : /*
878 : * A command object to store all information needed to make a request to
879 : * the promise. This allows us to delay the request until further use is
880 : * known (whether it is ->Then() again for more promise chaining or ->Track()
881 : * to terminate chaining and issue the request).
882 : *
883 : * This allows a unified syntax for promise chaining and disconnection
884 : * and feels more like its JS counterpart.
885 : */
886 : template<typename ThenValueType>
887 : class ThenCommand
888 : {
889 : // Allow Promise1::ThenCommand to access the private constructor,
890 : // Promise2::ThenCommand(ThenCommand&&).
891 : template<typename, typename, bool>
892 : friend class MozPromise;
893 :
894 : using PromiseType = typename ThenValueType::PromiseType;
895 : using Private = typename PromiseType::Private;
896 :
897 6 : ThenCommand(const char* aCallSite,
898 : already_AddRefed<ThenValueType> aThenValue,
899 : MozPromise* aReceiver)
900 : : mCallSite(aCallSite)
901 : , mThenValue(aThenValue)
902 6 : , mReceiver(aReceiver)
903 : {
904 6 : }
905 :
906 : ThenCommand(ThenCommand&& aOther) = default;
907 :
908 : public:
909 6 : ~ThenCommand()
910 : {
911 : // Issue the request now if the return value of Then() is not used.
912 6 : if (mThenValue) {
913 1 : mReceiver->ThenInternal(mThenValue.forget(), mCallSite);
914 : }
915 6 : }
916 :
917 : // Allow RefPtr<MozPromise> p = somePromise->Then();
918 : // p->Then(thread1, ...);
919 : // p->Then(thread2, ...);
920 2 : operator RefPtr<PromiseType>()
921 : {
922 : static_assert(
923 : ThenValueType::SupportChaining::value,
924 : "The resolve/reject callback needs to return a RefPtr<MozPromise> "
925 : "in order to do promise chaining.");
926 :
927 : // mCompletionPromise must be created before ThenInternal() to avoid race.
928 : RefPtr<Private> p =
929 4 : new Private("<completion promise>", true /* aIsCompletionPromise */);
930 2 : mThenValue->mCompletionPromise = p;
931 : // Note ThenInternal() might nullify mCompletionPromise before return.
932 : // So we need to return p instead of mCompletionPromise.
933 2 : mReceiver->ThenInternal(mThenValue.forget(), mCallSite);
934 4 : return p;
935 : }
936 :
937 : template<typename... Ts>
938 0 : auto Then(Ts&&... aArgs)
939 : -> decltype(DeclVal<PromiseType>().Then(Forward<Ts>(aArgs)...))
940 : {
941 : return static_cast<RefPtr<PromiseType>>(*this)->Then(
942 0 : Forward<Ts>(aArgs)...);
943 : }
944 :
945 3 : void Track(MozPromiseRequestHolder<MozPromise>& aRequestHolder)
946 : {
947 3 : aRequestHolder.Track(do_AddRef(mThenValue));
948 3 : mReceiver->ThenInternal(mThenValue.forget(), mCallSite);
949 3 : }
950 :
951 : // Allow calling ->Then() again for more promise chaining or ->Track() to
952 : // end chaining and track the request for future disconnection.
953 3 : ThenCommand* operator->()
954 : {
955 3 : return this;
956 : }
957 :
958 : private:
959 : const char* mCallSite;
960 : RefPtr<ThenValueType> mThenValue;
961 : RefPtr<MozPromise> mReceiver;
962 : };
963 :
964 : public:
965 : template<typename ThisType,
966 : typename... Methods,
967 : typename ThenValueType = ThenValue<ThisType*, Methods...>,
968 : typename ReturnType = ThenCommand<ThenValueType>>
969 0 : ReturnType Then(nsISerialEventTarget* aResponseTarget,
970 : const char* aCallSite,
971 : ThisType* aThisVal,
972 : Methods... aMethods)
973 : {
974 : RefPtr<ThenValueType> thenValue =
975 0 : new ThenValueType(aResponseTarget, aThisVal, aMethods..., aCallSite);
976 0 : return ReturnType(aCallSite, thenValue.forget(), this);
977 : }
978 :
979 : template<typename... Functions,
980 : typename ThenValueType = ThenValue<Functions...>,
981 : typename ReturnType = ThenCommand<ThenValueType>>
982 6 : ReturnType Then(nsISerialEventTarget* aResponseTarget,
983 : const char* aCallSite,
984 : Functions&&... aFunctions)
985 : {
986 : RefPtr<ThenValueType> thenValue =
987 18 : new ThenValueType(aResponseTarget, Move(aFunctions)..., aCallSite);
988 12 : return ReturnType(aCallSite, thenValue.forget(), this);
989 : }
990 :
991 5 : void ChainTo(already_AddRefed<Private> aChainedPromise, const char* aCallSite)
992 : {
993 10 : MutexAutoLock lock(mMutex);
994 0 : MOZ_DIAGNOSTIC_ASSERT(!IsExclusive || !mHaveRequest);
995 5 : mHaveRequest = true;
996 10 : RefPtr<Private> chainedPromise = aChainedPromise;
997 5 : PROMISE_LOG("%s invoking Chain() [this=%p, chainedPromise=%p, isPending=%d]",
998 : aCallSite, this, chainedPromise.get(), (int) IsPending());
999 5 : if (!IsPending()) {
1000 4 : ForwardTo(chainedPromise);
1001 : } else {
1002 1 : mChainedPromises.AppendElement(chainedPromise);
1003 : }
1004 5 : }
1005 :
1006 : // Note we expose the function AssertIsDead() instead of IsDead() since
1007 : // checking IsDead() is a data race in the situation where the request is not
1008 : // dead. Therefore we enforce the form |Assert(IsDead())| by exposing
1009 : // AssertIsDead() only.
1010 13 : void AssertIsDead() override
1011 : {
1012 13 : PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic && mMagic3 == sMagic && mMagic4 == &mMutex);
1013 26 : MutexAutoLock lock(mMutex);
1014 13 : for (auto&& then : mThenValues) {
1015 0 : then->AssertIsDead();
1016 : }
1017 13 : for (auto&& chained : mChainedPromises) {
1018 0 : chained->AssertIsDead();
1019 : }
1020 13 : }
1021 :
1022 : protected:
1023 58 : bool IsPending() const { return mValue.IsNothing(); }
1024 :
1025 6 : ResolveOrRejectValue& Value()
1026 : {
1027 : // This method should only be called once the value has stabilized. As
1028 : // such, we don't need to acquire the lock here.
1029 6 : MOZ_DIAGNOSTIC_ASSERT(!IsPending());
1030 6 : return mValue;
1031 : }
1032 :
1033 13 : void DispatchAll()
1034 : {
1035 13 : mMutex.AssertCurrentThreadOwns();
1036 19 : for (size_t i = 0; i < mThenValues.Length(); ++i) {
1037 6 : mThenValues[i]->Dispatch(this);
1038 : }
1039 13 : mThenValues.Clear();
1040 :
1041 14 : for (size_t i = 0; i < mChainedPromises.Length(); ++i) {
1042 1 : ForwardTo(mChainedPromises[i]);
1043 : }
1044 13 : mChainedPromises.Clear();
1045 13 : }
1046 :
1047 5 : void ForwardTo(Private* aOther)
1048 : {
1049 5 : MOZ_ASSERT(!IsPending());
1050 5 : if (mValue.IsResolve()) {
1051 5 : aOther->Resolve(MaybeMove(mValue.ResolveValue()), "<chained promise>");
1052 : } else {
1053 0 : aOther->Reject(MaybeMove(mValue.RejectValue()), "<chained promise>");
1054 : }
1055 5 : }
1056 :
1057 13 : virtual ~MozPromise()
1058 : {
1059 13 : PROMISE_LOG("MozPromise::~MozPromise [this=%p]", this);
1060 13 : AssertIsDead();
1061 : // We can't guarantee a completion promise will always be revolved or
1062 : // rejected since ResolveOrRejectRunnable might not run when dispatch fails.
1063 13 : if (!mIsCompletionPromise) {
1064 11 : MOZ_ASSERT(!IsPending());
1065 11 : MOZ_ASSERT(mThenValues.IsEmpty());
1066 11 : MOZ_ASSERT(mChainedPromises.IsEmpty());
1067 : }
1068 : #ifdef PROMISE_DEBUG
1069 13 : mMagic1 = 0;
1070 13 : mMagic2 = 0;
1071 13 : mMagic3 = 0;
1072 13 : mMagic4 = nullptr;
1073 : #endif
1074 26 : };
1075 :
1076 : const char* mCreationSite; // For logging
1077 : Mutex mMutex;
1078 : ResolveOrRejectValue mValue;
1079 : #ifdef PROMISE_DEBUG
1080 : uint32_t mMagic1 = sMagic;
1081 : #endif
1082 : // Try shows we never have more than 3 elements when IsExclusive is false.
1083 : // So '3' is a good value to avoid heap allocation in most cases.
1084 : AutoTArray<RefPtr<ThenValueBase>, IsExclusive ? 1 : 3> mThenValues;
1085 : #ifdef PROMISE_DEBUG
1086 : uint32_t mMagic2 = sMagic;
1087 : #endif
1088 : nsTArray<RefPtr<Private>> mChainedPromises;
1089 : #ifdef PROMISE_DEBUG
1090 : uint32_t mMagic3 = sMagic;
1091 : #endif
1092 : bool mHaveRequest;
1093 : const bool mIsCompletionPromise;
1094 : #ifdef PROMISE_DEBUG
1095 : void* mMagic4;
1096 : #endif
1097 : };
1098 :
1099 : template<typename ResolveValueT, typename RejectValueT, bool IsExclusive>
1100 39 : class MozPromise<ResolveValueT, RejectValueT, IsExclusive>::Private
1101 : : public MozPromise<ResolveValueT, RejectValueT, IsExclusive>
1102 : {
1103 : public:
1104 13 : explicit Private(const char* aCreationSite, bool aIsCompletionPromise = false)
1105 13 : : MozPromise(aCreationSite, aIsCompletionPromise) {}
1106 :
1107 : template<typename ResolveValueT_>
1108 13 : void Resolve(ResolveValueT_&& aResolveValue, const char* aResolveSite)
1109 : {
1110 13 : PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic && mMagic3 == sMagic && mMagic4 == &mMutex);
1111 26 : MutexAutoLock lock(mMutex);
1112 13 : PROMISE_LOG("%s resolving MozPromise (%p created at %s)", aResolveSite, this, mCreationSite);
1113 13 : if (!IsPending()) {
1114 0 : PROMISE_LOG("%s ignored already resolved or rejected MozPromise (%p created at %s)", aResolveSite, this, mCreationSite);
1115 0 : return;
1116 : }
1117 13 : mValue.SetResolve(Forward<ResolveValueT_>(aResolveValue));
1118 13 : DispatchAll();
1119 : }
1120 :
1121 : template<typename RejectValueT_>
1122 0 : void Reject(RejectValueT_&& aRejectValue, const char* aRejectSite)
1123 : {
1124 0 : PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic && mMagic3 == sMagic && mMagic4 == &mMutex);
1125 0 : MutexAutoLock lock(mMutex);
1126 0 : PROMISE_LOG("%s rejecting MozPromise (%p created at %s)", aRejectSite, this, mCreationSite);
1127 0 : if (!IsPending()) {
1128 0 : PROMISE_LOG("%s ignored already resolved or rejected MozPromise (%p created at %s)", aRejectSite, this, mCreationSite);
1129 0 : return;
1130 : }
1131 0 : mValue.SetReject(Forward<RejectValueT_>(aRejectValue));
1132 0 : DispatchAll();
1133 : }
1134 :
1135 : template<typename ResolveOrRejectValue_>
1136 : void ResolveOrReject(ResolveOrRejectValue_&& aValue, const char* aSite)
1137 : {
1138 : PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic && mMagic3 == sMagic && mMagic4 == &mMutex);
1139 : MutexAutoLock lock(mMutex);
1140 : PROMISE_LOG("%s resolveOrRejecting MozPromise (%p created at %s)", aSite, this, mCreationSite);
1141 : if (!IsPending()) {
1142 : PROMISE_LOG("%s ignored already resolved or rejected MozPromise (%p created at %s)", aSite, this, mCreationSite);
1143 : return;
1144 : }
1145 : mValue = Forward<ResolveOrRejectValue_>(aValue);
1146 : DispatchAll();
1147 : }
1148 : };
1149 :
1150 : // A generic promise type that does the trick for simple use cases.
1151 : typedef MozPromise<bool, nsresult, /* IsExclusive = */ false> GenericPromise;
1152 :
1153 : /*
1154 : * Class to encapsulate a promise for a particular role. Use this as the member
1155 : * variable for a class whose method returns a promise.
1156 : */
1157 : template<typename PromiseType>
1158 : class MozPromiseHolder
1159 : {
1160 : public:
1161 4 : MozPromiseHolder()
1162 4 : : mMonitor(nullptr) {}
1163 :
1164 0 : MozPromiseHolder(MozPromiseHolder&& aOther)
1165 0 : : mMonitor(nullptr), mPromise(aOther.mPromise.forget()) {}
1166 :
1167 : // Move semantics.
1168 0 : MozPromiseHolder& operator=(MozPromiseHolder&& aOther)
1169 : {
1170 0 : MOZ_ASSERT(!mMonitor && !aOther.mMonitor);
1171 0 : MOZ_DIAGNOSTIC_ASSERT(!mPromise);
1172 0 : mPromise = aOther.mPromise;
1173 0 : aOther.mPromise = nullptr;
1174 0 : return *this;
1175 : }
1176 :
1177 2 : ~MozPromiseHolder() { MOZ_ASSERT(!mPromise); }
1178 :
1179 4 : already_AddRefed<PromiseType> Ensure(const char* aMethodName) {
1180 4 : if (mMonitor) {
1181 1 : mMonitor->AssertCurrentThreadOwns();
1182 : }
1183 4 : if (!mPromise) {
1184 4 : mPromise = new (typename PromiseType::Private)(aMethodName);
1185 : }
1186 8 : RefPtr<PromiseType> p = mPromise.get();
1187 8 : return p.forget();
1188 : }
1189 :
1190 : // Provide a Monitor that should always be held when accessing this instance.
1191 1 : void SetMonitor(Monitor* aMonitor) { mMonitor = aMonitor; }
1192 :
1193 13 : bool IsEmpty() const
1194 : {
1195 13 : if (mMonitor) {
1196 0 : mMonitor->AssertCurrentThreadOwns();
1197 : }
1198 13 : return !mPromise;
1199 : }
1200 :
1201 0 : already_AddRefed<typename PromiseType::Private> Steal()
1202 : {
1203 0 : if (mMonitor) {
1204 0 : mMonitor->AssertCurrentThreadOwns();
1205 : }
1206 :
1207 0 : RefPtr<typename PromiseType::Private> p = mPromise;
1208 0 : mPromise = nullptr;
1209 0 : return p.forget();
1210 : }
1211 :
1212 0 : void Resolve(const typename PromiseType::ResolveValueType& aResolveValue,
1213 : const char* aMethodName)
1214 : {
1215 0 : if (mMonitor) {
1216 0 : mMonitor->AssertCurrentThreadOwns();
1217 : }
1218 0 : MOZ_ASSERT(mPromise);
1219 0 : mPromise->Resolve(aResolveValue, aMethodName);
1220 0 : mPromise = nullptr;
1221 0 : }
1222 4 : void Resolve(typename PromiseType::ResolveValueType&& aResolveValue,
1223 : const char* aMethodName)
1224 : {
1225 4 : if (mMonitor) {
1226 1 : mMonitor->AssertCurrentThreadOwns();
1227 : }
1228 4 : MOZ_ASSERT(mPromise);
1229 4 : mPromise->Resolve(Move(aResolveValue), aMethodName);
1230 4 : mPromise = nullptr;
1231 4 : }
1232 :
1233 0 : void ResolveIfExists(const typename PromiseType::ResolveValueType& aResolveValue,
1234 : const char* aMethodName)
1235 : {
1236 0 : if (!IsEmpty()) {
1237 0 : Resolve(aResolveValue, aMethodName);
1238 : }
1239 0 : }
1240 3 : void ResolveIfExists(typename PromiseType::ResolveValueType&& aResolveValue,
1241 : const char* aMethodName)
1242 : {
1243 3 : if (!IsEmpty()) {
1244 3 : Resolve(Move(aResolveValue), aMethodName);
1245 : }
1246 3 : }
1247 :
1248 0 : void Reject(const typename PromiseType::RejectValueType& aRejectValue,
1249 : const char* aMethodName)
1250 : {
1251 0 : if (mMonitor) {
1252 0 : mMonitor->AssertCurrentThreadOwns();
1253 : }
1254 0 : MOZ_ASSERT(mPromise);
1255 0 : mPromise->Reject(aRejectValue, aMethodName);
1256 0 : mPromise = nullptr;
1257 0 : }
1258 0 : void Reject(typename PromiseType::RejectValueType&& aRejectValue,
1259 : const char* aMethodName)
1260 : {
1261 0 : if (mMonitor) {
1262 0 : mMonitor->AssertCurrentThreadOwns();
1263 : }
1264 0 : MOZ_ASSERT(mPromise);
1265 0 : mPromise->Reject(Move(aRejectValue), aMethodName);
1266 0 : mPromise = nullptr;
1267 0 : }
1268 :
1269 0 : void RejectIfExists(const typename PromiseType::RejectValueType& aRejectValue,
1270 : const char* aMethodName)
1271 : {
1272 0 : if (!IsEmpty()) {
1273 0 : Reject(aRejectValue, aMethodName);
1274 : }
1275 0 : }
1276 0 : void RejectIfExists(typename PromiseType::RejectValueType&& aRejectValue,
1277 : const char* aMethodName)
1278 : {
1279 0 : if (!IsEmpty()) {
1280 0 : Reject(Move(aRejectValue), aMethodName);
1281 : }
1282 0 : }
1283 :
1284 : private:
1285 : Monitor* mMonitor;
1286 : RefPtr<typename PromiseType::Private> mPromise;
1287 : };
1288 :
1289 : /*
1290 : * Class to encapsulate a MozPromise::Request reference. Use this as the member
1291 : * variable for a class waiting on a MozPromise.
1292 : */
1293 : template<typename PromiseType>
1294 : class MozPromiseRequestHolder
1295 : {
1296 : public:
1297 3 : MozPromiseRequestHolder() {}
1298 2 : ~MozPromiseRequestHolder() { MOZ_ASSERT(!mRequest); }
1299 :
1300 3 : void Track(already_AddRefed<typename PromiseType::Request> aRequest)
1301 : {
1302 3 : MOZ_DIAGNOSTIC_ASSERT(!Exists());
1303 3 : mRequest = aRequest;
1304 3 : }
1305 :
1306 3 : void Complete()
1307 : {
1308 3 : MOZ_DIAGNOSTIC_ASSERT(Exists());
1309 3 : mRequest = nullptr;
1310 3 : }
1311 :
1312 : // Disconnects and forgets an outstanding promise. The resolve/reject methods
1313 : // will never be called.
1314 0 : void Disconnect() {
1315 0 : MOZ_ASSERT(Exists());
1316 0 : mRequest->Disconnect();
1317 0 : mRequest = nullptr;
1318 0 : }
1319 :
1320 0 : void DisconnectIfExists() {
1321 0 : if (Exists()) {
1322 0 : Disconnect();
1323 : }
1324 0 : }
1325 :
1326 6 : bool Exists() const { return !!mRequest; }
1327 :
1328 : private:
1329 : RefPtr<typename PromiseType::Request> mRequest;
1330 : };
1331 :
1332 : // Asynchronous Potentially-Cross-Thread Method Calls.
1333 : //
1334 : // This machinery allows callers to schedule a promise-returning function
1335 : // (a method and object, or a function object like a lambda) to be invoked
1336 : // asynchronously on a given thread, while at the same time receiving a
1337 : // promise upon which to invoke Then() immediately. InvokeAsync dispatches a
1338 : // task to invoke the function on the proper thread and also chain the
1339 : // resulting promise to the one that the caller received, so that resolve/
1340 : // reject values are forwarded through.
1341 :
1342 : namespace detail {
1343 :
1344 : // Non-templated base class to allow us to use MOZ_COUNT_{C,D}TOR, which cause
1345 : // assertions when used on templated types.
1346 : class MethodCallBase
1347 : {
1348 : public:
1349 3 : MethodCallBase() { MOZ_COUNT_CTOR(MethodCallBase); }
1350 3 : virtual ~MethodCallBase() { MOZ_COUNT_DTOR(MethodCallBase); }
1351 : };
1352 :
1353 : template<typename PromiseType, typename MethodType, typename ThisType,
1354 : typename... Storages>
1355 9 : class MethodCall : public MethodCallBase
1356 : {
1357 : public:
1358 : template<typename... Args>
1359 3 : MethodCall(MethodType aMethod, ThisType* aThisVal, Args&&... aArgs)
1360 : : mMethod(aMethod)
1361 : , mThisVal(aThisVal)
1362 3 : , mArgs(Forward<Args>(aArgs)...)
1363 : {
1364 : static_assert(sizeof...(Storages) == sizeof...(Args), "Storages and Args should have equal sizes");
1365 3 : }
1366 :
1367 3 : RefPtr<PromiseType> Invoke()
1368 : {
1369 3 : return mArgs.apply(mThisVal.get(), mMethod);
1370 : }
1371 :
1372 : private:
1373 : MethodType mMethod;
1374 : RefPtr<ThisType> mThisVal;
1375 : RunnableMethodArguments<Storages...> mArgs;
1376 : };
1377 :
1378 : template<typename PromiseType, typename MethodType, typename ThisType,
1379 : typename... Storages>
1380 9 : class ProxyRunnable : public CancelableRunnable
1381 : {
1382 : public:
1383 3 : ProxyRunnable(
1384 : typename PromiseType::Private* aProxyPromise,
1385 : MethodCall<PromiseType, MethodType, ThisType, Storages...>* aMethodCall)
1386 : : CancelableRunnable("detail::ProxyRunnable")
1387 : , mProxyPromise(aProxyPromise)
1388 3 : , mMethodCall(aMethodCall)
1389 : {
1390 3 : }
1391 :
1392 3 : NS_IMETHOD Run() override
1393 : {
1394 6 : RefPtr<PromiseType> p = mMethodCall->Invoke();
1395 3 : mMethodCall = nullptr;
1396 3 : p->ChainTo(mProxyPromise.forget(), "<Proxy Promise>");
1397 6 : return NS_OK;
1398 : }
1399 :
1400 0 : nsresult Cancel() override
1401 : {
1402 0 : return Run();
1403 : }
1404 :
1405 : private:
1406 : RefPtr<typename PromiseType::Private> mProxyPromise;
1407 : nsAutoPtr<MethodCall<PromiseType, MethodType, ThisType, Storages...>> mMethodCall;
1408 : };
1409 :
1410 : template<typename... Storages,
1411 : typename PromiseType, typename ThisType, typename... ArgTypes,
1412 : typename... ActualArgTypes>
1413 : static RefPtr<PromiseType>
1414 3 : InvokeAsyncImpl(nsISerialEventTarget* aTarget, ThisType* aThisVal,
1415 : const char* aCallerName,
1416 : RefPtr<PromiseType>(ThisType::*aMethod)(ArgTypes...),
1417 : ActualArgTypes&&... aArgs)
1418 : {
1419 3 : MOZ_ASSERT(aTarget);
1420 :
1421 : typedef RefPtr<PromiseType>(ThisType::*MethodType)(ArgTypes...);
1422 : typedef detail::MethodCall<PromiseType, MethodType, ThisType, Storages...> MethodCallType;
1423 : typedef detail::ProxyRunnable<PromiseType, MethodType, ThisType, Storages...> ProxyRunnableType;
1424 :
1425 : MethodCallType* methodCall =
1426 5 : new MethodCallType(aMethod, aThisVal, Forward<ActualArgTypes>(aArgs)...);
1427 6 : RefPtr<typename PromiseType::Private> p = new (typename PromiseType::Private)(aCallerName);
1428 9 : RefPtr<ProxyRunnableType> r = new ProxyRunnableType(p, methodCall);
1429 3 : aTarget->Dispatch(r.forget());
1430 6 : return p.forget();
1431 : }
1432 :
1433 : constexpr bool Any()
1434 : {
1435 : return false;
1436 : }
1437 :
1438 : template <typename T1>
1439 : constexpr bool Any(T1 a)
1440 : {
1441 : return static_cast<bool>(a);
1442 : }
1443 :
1444 : template <typename T1, typename... Ts>
1445 : constexpr bool Any(T1 a, Ts... aOthers)
1446 : {
1447 : return a || Any(aOthers...);
1448 : }
1449 :
1450 : } // namespace detail
1451 :
1452 : // InvokeAsync with explicitly-specified storages.
1453 : // See ParameterStorage in nsThreadUtils.h for help.
1454 : template<typename... Storages,
1455 : typename PromiseType, typename ThisType, typename... ArgTypes,
1456 : typename... ActualArgTypes,
1457 : typename EnableIf<sizeof...(Storages) != 0, int>::Type = 0>
1458 : static RefPtr<PromiseType>
1459 0 : InvokeAsync(nsISerialEventTarget* aTarget, ThisType* aThisVal, const char* aCallerName,
1460 : RefPtr<PromiseType>(ThisType::*aMethod)(ArgTypes...),
1461 : ActualArgTypes&&... aArgs)
1462 : {
1463 : static_assert(sizeof...(Storages) == sizeof...(ArgTypes),
1464 : "Provided Storages and method's ArgTypes should have equal sizes");
1465 : static_assert(sizeof...(Storages) == sizeof...(ActualArgTypes),
1466 : "Provided Storages and ActualArgTypes should have equal sizes");
1467 : return detail::InvokeAsyncImpl<Storages...>(
1468 : aTarget, aThisVal, aCallerName, aMethod,
1469 0 : Forward<ActualArgTypes>(aArgs)...);
1470 : }
1471 :
1472 : // InvokeAsync with no explicitly-specified storages, will copy arguments and
1473 : // then move them out of the runnable into the target method parameters.
1474 : template<typename... Storages,
1475 : typename PromiseType, typename ThisType, typename... ArgTypes,
1476 : typename... ActualArgTypes,
1477 : typename EnableIf<sizeof...(Storages) == 0, int>::Type = 0>
1478 : static RefPtr<PromiseType>
1479 3 : InvokeAsync(nsISerialEventTarget* aTarget, ThisType* aThisVal, const char* aCallerName,
1480 : RefPtr<PromiseType>(ThisType::*aMethod)(ArgTypes...),
1481 : ActualArgTypes&&... aArgs)
1482 : {
1483 : static_assert(!detail::Any(IsPointer<typename RemoveReference<ActualArgTypes>::Type>::value...),
1484 : "Cannot pass pointer types through InvokeAsync, Storages must be provided");
1485 : static_assert(sizeof...(ArgTypes) == sizeof...(ActualArgTypes),
1486 : "Method's ArgTypes and ActualArgTypes should have equal sizes");
1487 : return detail::InvokeAsyncImpl<StoreCopyPassByRRef<typename Decay<ActualArgTypes>::Type>...>(
1488 : aTarget, aThisVal, aCallerName, aMethod,
1489 3 : Forward<ActualArgTypes>(aArgs)...);
1490 : }
1491 :
1492 : namespace detail {
1493 :
1494 : template<typename Function, typename PromiseType>
1495 0 : class ProxyFunctionRunnable : public CancelableRunnable
1496 : {
1497 : typedef typename Decay<Function>::Type FunctionStorage;
1498 : public:
1499 : template<typename F>
1500 0 : ProxyFunctionRunnable(typename PromiseType::Private* aProxyPromise,
1501 : F&& aFunction)
1502 : : CancelableRunnable("detail::ProxyFunctionRunnable")
1503 : , mProxyPromise(aProxyPromise)
1504 0 : , mFunction(new FunctionStorage(Forward<F>(aFunction)))
1505 : {
1506 0 : }
1507 :
1508 0 : NS_IMETHOD Run() override
1509 : {
1510 0 : RefPtr<PromiseType> p = (*mFunction)();
1511 0 : mFunction = nullptr;
1512 0 : p->ChainTo(mProxyPromise.forget(), "<Proxy Promise>");
1513 0 : return NS_OK;
1514 : }
1515 :
1516 0 : nsresult Cancel() override
1517 : {
1518 0 : return Run();
1519 : }
1520 :
1521 : private:
1522 : RefPtr<typename PromiseType::Private> mProxyPromise;
1523 : UniquePtr<FunctionStorage> mFunction;
1524 : };
1525 :
1526 : // Note: The following struct and function are not for public consumption (yet?)
1527 : // as we would prefer all calls to pass on-the-spot lambdas (or at least moved
1528 : // function objects). They could be moved outside of detail if really needed.
1529 :
1530 : // We prefer getting function objects by non-lvalue-ref (to avoid copying them
1531 : // and their captures). This struct is a tag that allows the use of objects
1532 : // through lvalue-refs where necessary.
1533 : struct AllowInvokeAsyncFunctionLVRef {};
1534 :
1535 : // Invoke a function object (e.g., lambda or std/mozilla::function)
1536 : // asynchronously; note that the object will be copied if provided by lvalue-ref.
1537 : // Return a promise that the function should eventually resolve or reject.
1538 : template<typename Function>
1539 : static auto
1540 0 : InvokeAsync(nsISerialEventTarget* aTarget, const char* aCallerName,
1541 : AllowInvokeAsyncFunctionLVRef, Function&& aFunction)
1542 : -> decltype(aFunction())
1543 : {
1544 : static_assert(IsRefcountedSmartPointer<decltype(aFunction())>::value
1545 : && IsMozPromise<typename RemoveSmartPointer<
1546 : decltype(aFunction())>::Type>::value,
1547 : "Function object must return RefPtr<MozPromise>");
1548 0 : MOZ_ASSERT(aTarget);
1549 : typedef typename RemoveSmartPointer<decltype(aFunction())>::Type PromiseType;
1550 : typedef detail::ProxyFunctionRunnable<Function, PromiseType> ProxyRunnableType;
1551 :
1552 : RefPtr<typename PromiseType::Private> p =
1553 0 : new (typename PromiseType::Private)(aCallerName);
1554 : RefPtr<ProxyRunnableType> r =
1555 0 : new ProxyRunnableType(p, Forward<Function>(aFunction));
1556 0 : aTarget->Dispatch(r.forget());
1557 0 : return p.forget();
1558 : }
1559 :
1560 : } // namespace detail
1561 :
1562 : // Invoke a function object (e.g., lambda) asynchronously.
1563 : // Return a promise that the function should eventually resolve or reject.
1564 : template<typename Function>
1565 : static auto
1566 0 : InvokeAsync(nsISerialEventTarget* aTarget, const char* aCallerName,
1567 : Function&& aFunction)
1568 : -> decltype(aFunction())
1569 : {
1570 : static_assert(!IsLvalueReference<Function>::value,
1571 : "Function object must not be passed by lvalue-ref (to avoid "
1572 : "unplanned copies); Consider move()ing the object.");
1573 : return detail::InvokeAsync(aTarget, aCallerName,
1574 : detail::AllowInvokeAsyncFunctionLVRef(),
1575 0 : Forward<Function>(aFunction));
1576 : }
1577 :
1578 : #undef PROMISE_LOG
1579 : #undef PROMISE_ASSERT
1580 : #undef PROMISE_DEBUG
1581 :
1582 : } // namespace mozilla
1583 :
1584 : #endif
|