mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 02:09:05 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			1864 lines
		
	
	
	
		
			67 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1864 lines
		
	
	
	
		
			67 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | 
						|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | 
						|
/* This Source Code Form is subject to the terms of the Mozilla Public
 | 
						|
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
						|
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | 
						|
 | 
						|
#ifndef XPCOM_THREADS_MOZPROMISE_H_
 | 
						|
#define XPCOM_THREADS_MOZPROMISE_H_
 | 
						|
 | 
						|
#include <type_traits>
 | 
						|
#include <utility>
 | 
						|
 | 
						|
#include "mozilla/Attributes.h"
 | 
						|
#include "mozilla/ErrorNames.h"
 | 
						|
#include "mozilla/Logging.h"
 | 
						|
#include "mozilla/Maybe.h"
 | 
						|
#include "mozilla/Monitor.h"
 | 
						|
#include "mozilla/Mutex.h"
 | 
						|
#include "mozilla/RefPtr.h"
 | 
						|
#include "mozilla/StaticString.h"
 | 
						|
#include "mozilla/UniquePtr.h"
 | 
						|
#include "mozilla/Variant.h"
 | 
						|
#include "nsIDirectTaskDispatcher.h"
 | 
						|
#include "nsISerialEventTarget.h"
 | 
						|
#include "nsTArray.h"
 | 
						|
#include "nsThreadUtils.h"
 | 
						|
 | 
						|
#ifdef MOZ_WIDGET_ANDROID
 | 
						|
#  include "mozilla/jni/GeckoResultUtils.h"
 | 
						|
#endif
 | 
						|
 | 
						|
#if MOZ_DIAGNOSTIC_ASSERT_ENABLED
 | 
						|
#  define PROMISE_DEBUG
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef PROMISE_DEBUG
 | 
						|
#  define PROMISE_ASSERT MOZ_RELEASE_ASSERT
 | 
						|
#else
 | 
						|
#  define PROMISE_ASSERT(...) \
 | 
						|
    do {                      \
 | 
						|
    } while (0)
 | 
						|
#endif
 | 
						|
 | 
						|
#if DEBUG
 | 
						|
#  include "nsPrintfCString.h"
 | 
						|
#endif
 | 
						|
 | 
						|
namespace mozilla {
 | 
						|
 | 
						|
namespace dom {
 | 
						|
class Promise;
 | 
						|
}
 | 
						|
 | 
						|
extern LazyLogModule gMozPromiseLog;
 | 
						|
 | 
						|
#define PROMISE_LOG(x, ...) \
 | 
						|
  MOZ_LOG(gMozPromiseLog, mozilla::LogLevel::Debug, (x, ##__VA_ARGS__))
 | 
						|
 | 
						|
namespace detail {
 | 
						|
template <typename F>
 | 
						|
struct MethodTraitsHelper : MethodTraitsHelper<decltype(&F::operator())> {};
 | 
						|
template <typename ThisType, typename Ret, typename... ArgTypes>
 | 
						|
struct MethodTraitsHelper<Ret (ThisType::*)(ArgTypes...)> {
 | 
						|
  using ReturnType = Ret;
 | 
						|
  static const size_t ArgSize = sizeof...(ArgTypes);
 | 
						|
};
 | 
						|
template <typename ThisType, typename Ret, typename... ArgTypes>
 | 
						|
struct MethodTraitsHelper<Ret (ThisType::*)(ArgTypes...) const> {
 | 
						|
  using ReturnType = Ret;
 | 
						|
  static const size_t ArgSize = sizeof...(ArgTypes);
 | 
						|
};
 | 
						|
template <typename ThisType, typename Ret, typename... ArgTypes>
 | 
						|
struct MethodTraitsHelper<Ret (ThisType::*)(ArgTypes...) volatile> {
 | 
						|
  using ReturnType = Ret;
 | 
						|
  static const size_t ArgSize = sizeof...(ArgTypes);
 | 
						|
};
 | 
						|
template <typename ThisType, typename Ret, typename... ArgTypes>
 | 
						|
struct MethodTraitsHelper<Ret (ThisType::*)(ArgTypes...) const volatile> {
 | 
						|
  using ReturnType = Ret;
 | 
						|
  static const size_t ArgSize = sizeof...(ArgTypes);
 | 
						|
};
 | 
						|
template <typename T>
 | 
						|
struct MethodTrait : MethodTraitsHelper<std::remove_reference_t<T>> {};
 | 
						|
 | 
						|
}  // namespace detail
 | 
						|
 | 
						|
template <typename T>
 | 
						|
using MethodReturnType = typename detail::MethodTrait<T>::ReturnType;
 | 
						|
 | 
						|
template <typename MethodType>
 | 
						|
constexpr bool TakesAnyArguments =
 | 
						|
    detail::MethodTrait<MethodType>::ArgSize != 0;
 | 
						|
 | 
						|
template <typename ResolveValueT, typename RejectValueT, bool IsExclusive>
 | 
						|
class MozPromise;
 | 
						|
 | 
						|
template <typename T>
 | 
						|
constexpr bool IsMozPromise = false;
 | 
						|
 | 
						|
template <typename ResolveValueT, typename RejectValueT, bool IsExclusive>
 | 
						|
constexpr bool
 | 
						|
    IsMozPromise<MozPromise<ResolveValueT, RejectValueT, IsExclusive>> = true;
 | 
						|
 | 
						|
/*
 | 
						|
 * A promise manages an asynchronous request that may or may not be able to be
 | 
						|
 * fulfilled immediately. When an API returns a promise, the consumer may attach
 | 
						|
 * callbacks to be invoked (asynchronously, on a specified thread) when the
 | 
						|
 * request is either completed (resolved) or cannot be completed (rejected).
 | 
						|
 * Whereas JS promise callbacks are dispatched from Microtask checkpoints,
 | 
						|
 * MozPromises resolution/rejection make a normal round-trip through the event
 | 
						|
 * loop, which simplifies their ordering semantics relative to other native
 | 
						|
 * code.
 | 
						|
 *
 | 
						|
 * MozPromises attempt to mirror the spirit of JS Promises to the extent that
 | 
						|
 * is possible (and desirable) in C++. While the intent is that MozPromises
 | 
						|
 * feel familiar to programmers who are accustomed to their JS-implemented
 | 
						|
 * cousin, we don't shy away from imposing restrictions and adding features that
 | 
						|
 * make sense for the use cases we encounter.
 | 
						|
 *
 | 
						|
 * A MozPromise is ThreadSafe, and may be ->Then()ed on any thread. The Then()
 | 
						|
 * call accepts resolve and reject callbacks, and returns a magic object which
 | 
						|
 * will be implicitly converted to a MozPromise::Request or a MozPromise object
 | 
						|
 * depending on how the return value is used. The magic object serves several
 | 
						|
 * purposes for the consumer.
 | 
						|
 *
 | 
						|
 *   (1) When converting to a MozPromise::Request, it allows the caller to
 | 
						|
 *       cancel the delivery of the resolve/reject value if it has not already
 | 
						|
 *       occurred, via Disconnect() (this must be done on the target thread to
 | 
						|
 *       avoid racing).
 | 
						|
 *
 | 
						|
 *   (2) When converting to a MozPromise (which is called a completion promise),
 | 
						|
 *       it allows promise chaining so ->Then() can be called again to attach
 | 
						|
 *       more resolve and reject callbacks. If the resolve/reject callback
 | 
						|
 *       returns a new MozPromise, that promise is chained to the completion
 | 
						|
 *       promise, such that its resolve/reject value will be forwarded along
 | 
						|
 *       when it arrives. If the resolve/reject callback returns void, the
 | 
						|
 *       completion promise is resolved/rejected with the same value that was
 | 
						|
 *       passed to the callback.
 | 
						|
 *
 | 
						|
 * The MozPromise APIs skirt traditional XPCOM convention by returning nsRefPtrs
 | 
						|
 * (rather than already_AddRefed) from various methods. This is done to allow
 | 
						|
 * elegant chaining of calls without cluttering up the code with intermediate
 | 
						|
 * variables, and without introducing separate API variants for callers that
 | 
						|
 * want a return value (from, say, ->Then()) from those that don't.
 | 
						|
 *
 | 
						|
 * When IsExclusive is true, the MozPromise does a release-mode assertion that
 | 
						|
 * there is at most one call to either Then(...) or ChainTo(...).
 | 
						|
 */
 | 
						|
 | 
						|
class MozPromiseRefcountable {
 | 
						|
 public:
 | 
						|
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MozPromiseRefcountable)
 | 
						|
 protected:
 | 
						|
  virtual ~MozPromiseRefcountable() = default;
 | 
						|
};
 | 
						|
 | 
						|
class MozPromiseBase : public MozPromiseRefcountable {
 | 
						|
 public:
 | 
						|
  virtual void AssertIsDead() = 0;
 | 
						|
};
 | 
						|
 | 
						|
template <typename T>
 | 
						|
class MozPromiseHolder;
 | 
						|
template <typename T>
 | 
						|
class MozPromiseRequestHolder;
 | 
						|
template <typename ResolveValueT, typename RejectValueT, bool IsExclusive>
 | 
						|
class MozPromise : public MozPromiseBase {
 | 
						|
  static const uint32_t sMagic = 0xcecace11;
 | 
						|
 | 
						|
  // Return a |T&&| to enable move when IsExclusive is true or
 | 
						|
  // a |const T&| to enforce copy otherwise.
 | 
						|
  template <typename T,
 | 
						|
            typename R = std::conditional_t<IsExclusive, T&&, const T&>>
 | 
						|
  static R MaybeMove(T& aX) {
 | 
						|
    return static_cast<R>(aX);
 | 
						|
  }
 | 
						|
 | 
						|
 public:
 | 
						|
  using ResolveValueType = ResolveValueT;
 | 
						|
  using RejectValueType = RejectValueT;
 | 
						|
  class ResolveOrRejectValue {
 | 
						|
   public:
 | 
						|
    template <typename ResolveValueType_>
 | 
						|
    void SetResolve(ResolveValueType_&& aResolveValue) {
 | 
						|
      MOZ_ASSERT(IsNothing());
 | 
						|
      mValue = Storage(VariantIndex<ResolveIndex>{},
 | 
						|
                       std::forward<ResolveValueType_>(aResolveValue));
 | 
						|
    }
 | 
						|
 | 
						|
    template <typename RejectValueType_>
 | 
						|
    void SetReject(RejectValueType_&& aRejectValue) {
 | 
						|
      MOZ_ASSERT(IsNothing());
 | 
						|
      mValue = Storage(VariantIndex<RejectIndex>{},
 | 
						|
                       std::forward<RejectValueType_>(aRejectValue));
 | 
						|
    }
 | 
						|
 | 
						|
    template <typename ResolveValueType_>
 | 
						|
    static ResolveOrRejectValue MakeResolve(ResolveValueType_&& aResolveValue) {
 | 
						|
      ResolveOrRejectValue val;
 | 
						|
      val.SetResolve(std::forward<ResolveValueType_>(aResolveValue));
 | 
						|
      return val;
 | 
						|
    }
 | 
						|
 | 
						|
    template <typename RejectValueType_>
 | 
						|
    static ResolveOrRejectValue MakeReject(RejectValueType_&& aRejectValue) {
 | 
						|
      ResolveOrRejectValue val;
 | 
						|
      val.SetReject(std::forward<RejectValueType_>(aRejectValue));
 | 
						|
      return val;
 | 
						|
    }
 | 
						|
 | 
						|
    bool IsResolve() const { return mValue.template is<ResolveIndex>(); }
 | 
						|
    bool IsReject() const { return mValue.template is<RejectIndex>(); }
 | 
						|
    bool IsNothing() const { return mValue.template is<NothingIndex>(); }
 | 
						|
 | 
						|
    const ResolveValueType& ResolveValue() const {
 | 
						|
      return mValue.template as<ResolveIndex>();
 | 
						|
    }
 | 
						|
    ResolveValueType& ResolveValue() {
 | 
						|
      return mValue.template as<ResolveIndex>();
 | 
						|
    }
 | 
						|
    const RejectValueType& RejectValue() const {
 | 
						|
      return mValue.template as<RejectIndex>();
 | 
						|
    }
 | 
						|
    RejectValueType& RejectValue() { return mValue.template as<RejectIndex>(); }
 | 
						|
 | 
						|
   private:
 | 
						|
    enum { NothingIndex, ResolveIndex, RejectIndex };
 | 
						|
    using Storage = Variant<Nothing, ResolveValueType, RejectValueType>;
 | 
						|
    Storage mValue = Storage(VariantIndex<NothingIndex>{});
 | 
						|
  };
 | 
						|
 | 
						|
 protected:
 | 
						|
  // MozPromise is the public type, and never constructed directly. Construct
 | 
						|
  // a MozPromise::Private, defined below.
 | 
						|
  MozPromise(StaticString aCreationSite, bool aIsCompletionPromise)
 | 
						|
      : mCreationSite(aCreationSite),
 | 
						|
        mMutex("MozPromise Mutex"),
 | 
						|
        mHaveRequest(false),
 | 
						|
        mIsCompletionPromise(aIsCompletionPromise)
 | 
						|
#ifdef PROMISE_DEBUG
 | 
						|
        ,
 | 
						|
        mMagic4(&mMutex)
 | 
						|
#endif
 | 
						|
  {
 | 
						|
    PROMISE_LOG("%s creating MozPromise (%p)", mCreationSite.get(), this);
 | 
						|
  }
 | 
						|
 | 
						|
 public:
 | 
						|
  // MozPromise::Private allows us to separate the public interface (upon which
 | 
						|
  // consumers of the promise may invoke methods like Then()) from the private
 | 
						|
  // interface (upon which the creator of the promise may invoke Resolve() or
 | 
						|
  // Reject()). APIs should create and store a MozPromise::Private (usually
 | 
						|
  // via a MozPromiseHolder), and return a MozPromise to consumers.
 | 
						|
  //
 | 
						|
  // NB: We can include the definition of this class inline once B2G ICS is
 | 
						|
  // gone.
 | 
						|
  class Private;
 | 
						|
 | 
						|
  template <typename ResolveValueType_>
 | 
						|
  [[nodiscard]] static RefPtr<MozPromise> CreateAndResolve(
 | 
						|
      ResolveValueType_&& aResolveValue, StaticString aResolveSite) {
 | 
						|
    static_assert(std::is_convertible_v<ResolveValueType_, ResolveValueT>,
 | 
						|
                  "Resolve() argument must be implicitly convertible to "
 | 
						|
                  "MozPromise's ResolveValueT");
 | 
						|
    RefPtr<typename MozPromise::Private> p =
 | 
						|
        new MozPromise::Private(aResolveSite);
 | 
						|
    p->Resolve(std::forward<ResolveValueType_>(aResolveValue), aResolveSite);
 | 
						|
    return p;
 | 
						|
  }
 | 
						|
 | 
						|
  template <typename RejectValueType_>
 | 
						|
  [[nodiscard]] static RefPtr<MozPromise> CreateAndReject(
 | 
						|
      RejectValueType_&& aRejectValue, StaticString aRejectSite) {
 | 
						|
    static_assert(std::is_convertible_v<RejectValueType_, RejectValueT>,
 | 
						|
                  "Reject() argument must be implicitly convertible to "
 | 
						|
                  "MozPromise's RejectValueT");
 | 
						|
    RefPtr<typename MozPromise::Private> p =
 | 
						|
        new MozPromise::Private(aRejectSite);
 | 
						|
    p->Reject(std::forward<RejectValueType_>(aRejectValue), aRejectSite);
 | 
						|
    return p;
 | 
						|
  }
 | 
						|
 | 
						|
  template <typename ResolveOrRejectValueType_>
 | 
						|
  [[nodiscard]] static RefPtr<MozPromise> CreateAndResolveOrReject(
 | 
						|
      ResolveOrRejectValueType_&& aValue, StaticString aSite) {
 | 
						|
    RefPtr<typename MozPromise::Private> p = new MozPromise::Private(aSite);
 | 
						|
    p->ResolveOrReject(std::forward<ResolveOrRejectValueType_>(aValue), aSite);
 | 
						|
    return p;
 | 
						|
  }
 | 
						|
 | 
						|
  using AllPromiseType = MozPromise<CopyableTArray<ResolveValueType>,
 | 
						|
                                    RejectValueType, IsExclusive>;
 | 
						|
  using AllSettledPromiseType =
 | 
						|
      MozPromise<CopyableTArray<ResolveOrRejectValue>, bool, IsExclusive>;
 | 
						|
 | 
						|
 private:
 | 
						|
  class AllPromiseHolder : public MozPromiseRefcountable {
 | 
						|
   public:
 | 
						|
    explicit AllPromiseHolder(size_t aDependentPromises)
 | 
						|
        : mPromise(new typename AllPromiseType::Private(__func__)),
 | 
						|
          mOutstandingPromises(aDependentPromises) {
 | 
						|
      MOZ_ASSERT(aDependentPromises > 0);
 | 
						|
      mResolveValues.SetLength(aDependentPromises);
 | 
						|
    }
 | 
						|
 | 
						|
    template <typename ResolveValueType_>
 | 
						|
    void Resolve(size_t aIndex, ResolveValueType_&& aResolveValue) {
 | 
						|
      if (!mPromise) {
 | 
						|
        // Already rejected.
 | 
						|
        return;
 | 
						|
      }
 | 
						|
 | 
						|
      mResolveValues[aIndex].emplace(
 | 
						|
          std::forward<ResolveValueType_>(aResolveValue));
 | 
						|
      if (--mOutstandingPromises == 0) {
 | 
						|
        nsTArray<ResolveValueType> resolveValues;
 | 
						|
        resolveValues.SetCapacity(mResolveValues.Length());
 | 
						|
        for (auto&& resolveValue : mResolveValues) {
 | 
						|
          resolveValues.AppendElement(std::move(resolveValue.ref()));
 | 
						|
        }
 | 
						|
 | 
						|
        mPromise->Resolve(std::move(resolveValues), __func__);
 | 
						|
        mPromise = nullptr;
 | 
						|
        mResolveValues.Clear();
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    template <typename RejectValueType_>
 | 
						|
    void Reject(RejectValueType_&& aRejectValue) {
 | 
						|
      if (!mPromise) {
 | 
						|
        // Already rejected.
 | 
						|
        return;
 | 
						|
      }
 | 
						|
 | 
						|
      mPromise->Reject(std::forward<RejectValueType_>(aRejectValue), __func__);
 | 
						|
      mPromise = nullptr;
 | 
						|
      mResolveValues.Clear();
 | 
						|
    }
 | 
						|
 | 
						|
    AllPromiseType* Promise() { return mPromise; }
 | 
						|
 | 
						|
   private:
 | 
						|
    nsTArray<Maybe<ResolveValueType>> mResolveValues;
 | 
						|
    RefPtr<typename AllPromiseType::Private> mPromise;
 | 
						|
    size_t mOutstandingPromises;
 | 
						|
  };
 | 
						|
 | 
						|
  // Trying to pass ResolveOrRejectValue by value fails static analysis checks,
 | 
						|
  // so we need to use either a const& or an rvalue reference, depending on
 | 
						|
  // whether IsExclusive is true or not.
 | 
						|
  using ResolveOrRejectValueParam =
 | 
						|
      std::conditional_t<IsExclusive, ResolveOrRejectValue&&,
 | 
						|
                         const ResolveOrRejectValue&>;
 | 
						|
 | 
						|
  using ResolveValueTypeParam =
 | 
						|
      std::conditional_t<IsExclusive, ResolveValueType&&,
 | 
						|
                         const ResolveValueType&>;
 | 
						|
 | 
						|
  using RejectValueTypeParam =
 | 
						|
      std::conditional_t<IsExclusive, RejectValueType&&,
 | 
						|
                         const RejectValueType&>;
 | 
						|
 | 
						|
  class AllSettledPromiseHolder : public MozPromiseRefcountable {
 | 
						|
   public:
 | 
						|
    explicit AllSettledPromiseHolder(size_t aDependentPromises)
 | 
						|
        : mPromise(new typename AllSettledPromiseType::Private(__func__)),
 | 
						|
          mOutstandingPromises(aDependentPromises) {
 | 
						|
      MOZ_ASSERT(aDependentPromises > 0);
 | 
						|
      mValues.SetLength(aDependentPromises);
 | 
						|
    }
 | 
						|
 | 
						|
    void Settle(size_t aIndex, ResolveOrRejectValueParam aValue) {
 | 
						|
      if (!mPromise) {
 | 
						|
        // Already rejected.
 | 
						|
        return;
 | 
						|
      }
 | 
						|
 | 
						|
      mValues[aIndex].emplace(MaybeMove(aValue));
 | 
						|
      if (--mOutstandingPromises == 0) {
 | 
						|
        nsTArray<ResolveOrRejectValue> values;
 | 
						|
        values.SetCapacity(mValues.Length());
 | 
						|
        for (auto&& value : mValues) {
 | 
						|
          values.AppendElement(std::move(value.ref()));
 | 
						|
        }
 | 
						|
 | 
						|
        mPromise->Resolve(std::move(values), __func__);
 | 
						|
        mPromise = nullptr;
 | 
						|
        mValues.Clear();
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    AllSettledPromiseType* Promise() { return mPromise; }
 | 
						|
 | 
						|
   private:
 | 
						|
    nsTArray<Maybe<ResolveOrRejectValue>> mValues;
 | 
						|
    RefPtr<typename AllSettledPromiseType::Private> mPromise;
 | 
						|
    size_t mOutstandingPromises;
 | 
						|
  };
 | 
						|
 | 
						|
 public:
 | 
						|
  [[nodiscard]] static RefPtr<AllPromiseType> All(
 | 
						|
      nsISerialEventTarget* aProcessingTarget,
 | 
						|
      nsTArray<RefPtr<MozPromise>>& aPromises) {
 | 
						|
    if (aPromises.Length() == 0) {
 | 
						|
      return AllPromiseType::CreateAndResolve(
 | 
						|
          CopyableTArray<ResolveValueType>(), __func__);
 | 
						|
    }
 | 
						|
 | 
						|
    RefPtr<AllPromiseHolder> holder = new AllPromiseHolder(aPromises.Length());
 | 
						|
    RefPtr<AllPromiseType> promise = holder->Promise();
 | 
						|
    for (size_t i = 0; i < aPromises.Length(); ++i) {
 | 
						|
      aPromises[i]->Then(
 | 
						|
          aProcessingTarget, __func__,
 | 
						|
          [holder, i](ResolveValueTypeParam aResolveValue) -> void {
 | 
						|
            holder->Resolve(i, MaybeMove(aResolveValue));
 | 
						|
          },
 | 
						|
          [holder](RejectValueTypeParam aRejectValue) -> void {
 | 
						|
            holder->Reject(MaybeMove(aRejectValue));
 | 
						|
          });
 | 
						|
    }
 | 
						|
    return promise;
 | 
						|
  }
 | 
						|
 | 
						|
  [[nodiscard]] static RefPtr<AllSettledPromiseType> AllSettled(
 | 
						|
      nsISerialEventTarget* aProcessingTarget,
 | 
						|
      nsTArray<RefPtr<MozPromise>>& aPromises) {
 | 
						|
    if (aPromises.Length() == 0) {
 | 
						|
      return AllSettledPromiseType::CreateAndResolve(
 | 
						|
          CopyableTArray<ResolveOrRejectValue>(), __func__);
 | 
						|
    }
 | 
						|
 | 
						|
    RefPtr<AllSettledPromiseHolder> holder =
 | 
						|
        new AllSettledPromiseHolder(aPromises.Length());
 | 
						|
    RefPtr<AllSettledPromiseType> promise = holder->Promise();
 | 
						|
    for (size_t i = 0; i < aPromises.Length(); ++i) {
 | 
						|
      aPromises[i]->Then(aProcessingTarget, __func__,
 | 
						|
                         [holder, i](ResolveOrRejectValueParam aValue) -> void {
 | 
						|
                           holder->Settle(i, MaybeMove(aValue));
 | 
						|
                         });
 | 
						|
    }
 | 
						|
    return promise;
 | 
						|
  }
 | 
						|
 | 
						|
  class Request : public MozPromiseRefcountable {
 | 
						|
   public:
 | 
						|
    virtual void Disconnect() = 0;
 | 
						|
 | 
						|
   protected:
 | 
						|
    Request() : mComplete(false), mDisconnected(false) {}
 | 
						|
    virtual ~Request() = default;
 | 
						|
 | 
						|
    bool mComplete;
 | 
						|
    bool mDisconnected;
 | 
						|
  };
 | 
						|
 | 
						|
 protected:
 | 
						|
  /*
 | 
						|
   * A ThenValue tracks a single consumer waiting on the promise. When a
 | 
						|
   * consumer invokes promise->Then(...), a ThenValue is created. Once the
 | 
						|
   * Promise is resolved or rejected, a {Resolve,Reject}Runnable is dispatched,
 | 
						|
   * which invokes the resolve/reject method and then deletes the ThenValue.
 | 
						|
   */
 | 
						|
  class ThenValueBase : public Request {
 | 
						|
    friend class MozPromise;
 | 
						|
    static const uint32_t sMagic = 0xfadece11;
 | 
						|
 | 
						|
   public:
 | 
						|
    class ResolveOrRejectRunnable final
 | 
						|
        : public PrioritizableCancelableRunnable {
 | 
						|
     public:
 | 
						|
      ResolveOrRejectRunnable(ThenValueBase* aThenValue, MozPromise* aPromise)
 | 
						|
          : PrioritizableCancelableRunnable(
 | 
						|
                aPromise->mPriority,
 | 
						|
                "MozPromise::ThenValueBase::ResolveOrRejectRunnable"),
 | 
						|
            mThenValue(aThenValue),
 | 
						|
            mPromise(aPromise) {
 | 
						|
        MOZ_DIAGNOSTIC_ASSERT(!mPromise->IsPending());
 | 
						|
      }
 | 
						|
 | 
						|
      ~ResolveOrRejectRunnable() {
 | 
						|
        if (mThenValue) {
 | 
						|
          mThenValue->AssertIsDead();
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      NS_IMETHOD Run() override {
 | 
						|
        PROMISE_LOG("ResolveOrRejectRunnable::Run() [this=%p]", this);
 | 
						|
        mThenValue->DoResolveOrReject(mPromise->Value());
 | 
						|
        mThenValue = nullptr;
 | 
						|
        mPromise = nullptr;
 | 
						|
        return NS_OK;
 | 
						|
      }
 | 
						|
 | 
						|
      nsresult Cancel() override { return Run(); }
 | 
						|
 | 
						|
     private:
 | 
						|
      RefPtr<ThenValueBase> mThenValue;
 | 
						|
      RefPtr<MozPromise> mPromise;
 | 
						|
    };
 | 
						|
 | 
						|
    ThenValueBase(nsISerialEventTarget* aResponseTarget, StaticString aCallSite)
 | 
						|
        : mResponseTarget(aResponseTarget), mCallSite(aCallSite) {
 | 
						|
      MOZ_ASSERT(aResponseTarget);
 | 
						|
    }
 | 
						|
 | 
						|
#ifdef PROMISE_DEBUG
 | 
						|
    ~ThenValueBase() {
 | 
						|
      mMagic1 = 0;
 | 
						|
      mMagic2 = 0;
 | 
						|
    }
 | 
						|
#endif
 | 
						|
 | 
						|
    void AssertIsDead() {
 | 
						|
      PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic);
 | 
						|
      // We want to assert that this ThenValues is dead - that is to say, that
 | 
						|
      // there are no consumers waiting for the result. In the case of a normal
 | 
						|
      // ThenValue, we check that it has been disconnected, which is the way
 | 
						|
      // that the consumer signals that it no longer wishes to hear about the
 | 
						|
      // result. If this ThenValue has a completion promise (which is mutually
 | 
						|
      // exclusive with being disconnectable), we recursively assert that every
 | 
						|
      // ThenValue associated with the completion promise is dead.
 | 
						|
      if (MozPromiseBase* p = CompletionPromise()) {
 | 
						|
        p->AssertIsDead();
 | 
						|
      } else {
 | 
						|
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
 | 
						|
        if (MOZ_UNLIKELY(!Request::mDisconnected)) {
 | 
						|
          MOZ_CRASH_UNSAFE_PRINTF(
 | 
						|
              "MozPromise::ThenValue created from '%s' destroyed without being "
 | 
						|
              "either disconnected, resolved, or rejected (dispatchRv: %s)",
 | 
						|
              mCallSite.get(),
 | 
						|
              mDispatchRv ? GetStaticErrorName(*mDispatchRv)
 | 
						|
                          : "not dispatched");
 | 
						|
        }
 | 
						|
#endif
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    void Dispatch(MozPromise* aPromise) {
 | 
						|
      PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic);
 | 
						|
      aPromise->mMutex.AssertCurrentThreadOwns();
 | 
						|
      MOZ_ASSERT(!aPromise->IsPending());
 | 
						|
 | 
						|
      nsCOMPtr<nsIRunnable> r = new ResolveOrRejectRunnable(this, aPromise);
 | 
						|
      PROMISE_LOG(
 | 
						|
          "%s Then() call made from %s [Runnable=%p, Promise=%p, ThenValue=%p] "
 | 
						|
          "%s dispatch",
 | 
						|
          aPromise->mValue.IsResolve() ? "Resolving" : "Rejecting",
 | 
						|
          mCallSite.get(), r.get(), aPromise, this,
 | 
						|
          aPromise->mUseSynchronousTaskDispatch ? "synchronous"
 | 
						|
          : aPromise->mUseDirectTaskDispatch    ? "directtask"
 | 
						|
                                                : "normal");
 | 
						|
 | 
						|
      if (aPromise->mUseSynchronousTaskDispatch &&
 | 
						|
          mResponseTarget->IsOnCurrentThread()) {
 | 
						|
        PROMISE_LOG("ThenValue::Dispatch running task synchronously [this=%p]",
 | 
						|
                    this);
 | 
						|
        r->Run();
 | 
						|
        return;
 | 
						|
      }
 | 
						|
 | 
						|
      if (aPromise->mUseDirectTaskDispatch &&
 | 
						|
          mResponseTarget->IsOnCurrentThread()) {
 | 
						|
        PROMISE_LOG(
 | 
						|
            "ThenValue::Dispatch dispatch task via direct task queue [this=%p]",
 | 
						|
            this);
 | 
						|
        nsCOMPtr<nsIDirectTaskDispatcher> dispatcher =
 | 
						|
            do_QueryInterface(mResponseTarget);
 | 
						|
        if (dispatcher) {
 | 
						|
          SetDispatchRv(dispatcher->DispatchDirectTask(r.forget()));
 | 
						|
          return;
 | 
						|
        }
 | 
						|
        NS_WARNING(
 | 
						|
            nsPrintfCString(
 | 
						|
                "Direct Task dispatching not available for thread \"%s\"",
 | 
						|
                PR_GetThreadName(PR_GetCurrentThread()))
 | 
						|
                .get());
 | 
						|
        MOZ_DIAGNOSTIC_ASSERT(
 | 
						|
            false,
 | 
						|
            "mResponseTarget must implement nsIDirectTaskDispatcher for direct "
 | 
						|
            "task dispatching");
 | 
						|
      }
 | 
						|
 | 
						|
      // Promise consumers are allowed to disconnect the Request object and
 | 
						|
      // then shut down the thread or task queue that the promise result would
 | 
						|
      // be dispatched on. So we unfortunately can't assert that promise
 | 
						|
      // dispatch succeeds. :-(
 | 
						|
      // We do record whether or not it succeeds so that if the ThenValueBase is
 | 
						|
      // then destroyed and it was not disconnected, we can include that
 | 
						|
      // information in the assertion message.
 | 
						|
      SetDispatchRv(mResponseTarget->Dispatch(r.forget()));
 | 
						|
    }
 | 
						|
 | 
						|
    void Disconnect() override {
 | 
						|
      MOZ_DIAGNOSTIC_ASSERT(mResponseTarget->IsOnCurrentThread());
 | 
						|
      MOZ_DIAGNOSTIC_ASSERT(!Request::mComplete);
 | 
						|
      Request::mDisconnected = true;
 | 
						|
 | 
						|
      // We could support rejecting the completion promise on disconnection, but
 | 
						|
      // then we'd need to have some sort of default reject value. The use cases
 | 
						|
      // of disconnection and completion promise chaining seem pretty
 | 
						|
      // orthogonal, so let's use assert against it.
 | 
						|
      MOZ_DIAGNOSTIC_ASSERT(!CompletionPromise());
 | 
						|
    }
 | 
						|
 | 
						|
   protected:
 | 
						|
    virtual MozPromiseBase* CompletionPromise() const = 0;
 | 
						|
    virtual void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) = 0;
 | 
						|
 | 
						|
    void DoResolveOrReject(ResolveOrRejectValue& aValue) {
 | 
						|
      PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic);
 | 
						|
      MOZ_DIAGNOSTIC_ASSERT(mResponseTarget->IsOnCurrentThread());
 | 
						|
      Request::mComplete = true;
 | 
						|
      if (Request::mDisconnected) {
 | 
						|
        PROMISE_LOG(
 | 
						|
            "ThenValue::DoResolveOrReject disconnected - bailing out [this=%p]",
 | 
						|
            this);
 | 
						|
        return;
 | 
						|
      }
 | 
						|
 | 
						|
      // Invoke the resolve or reject method.
 | 
						|
      DoResolveOrRejectInternal(aValue);
 | 
						|
    }
 | 
						|
 | 
						|
    void SetDispatchRv(nsresult aRv) {
 | 
						|
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
 | 
						|
      mDispatchRv = Some(aRv);
 | 
						|
#endif
 | 
						|
    }
 | 
						|
 | 
						|
    nsCOMPtr<nsISerialEventTarget>
 | 
						|
        mResponseTarget;  // May be released on any thread.
 | 
						|
#ifdef PROMISE_DEBUG
 | 
						|
    uint32_t mMagic1 = sMagic;
 | 
						|
#endif
 | 
						|
    StaticString mCallSite;
 | 
						|
#ifdef PROMISE_DEBUG
 | 
						|
    uint32_t mMagic2 = sMagic;
 | 
						|
#endif
 | 
						|
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
 | 
						|
    Maybe<nsresult> mDispatchRv;
 | 
						|
#endif
 | 
						|
  };
 | 
						|
 | 
						|
  /*
 | 
						|
   * Helper to make the resolve/reject value argument "optional".
 | 
						|
   */
 | 
						|
  template <typename ThisType, typename MethodType, typename ValueType>
 | 
						|
  static MethodReturnType<MethodType> InvokeMethod(ThisType* aThisVal,
 | 
						|
                                                   MethodType aMethod,
 | 
						|
                                                   ValueType&& aValue) {
 | 
						|
    if constexpr (TakesAnyArguments<MethodType>) {
 | 
						|
      return (aThisVal->*aMethod)(std::forward<ValueType>(aValue));
 | 
						|
    } else {
 | 
						|
      return (aThisVal->*aMethod)();
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  template <bool SupportChaining, typename PromiseType, typename ThisType,
 | 
						|
            typename MethodType, typename ValueType>
 | 
						|
  static RefPtr<PromiseType> InvokeCallbackMethod(ThisType* aThisVal,
 | 
						|
                                                  MethodType aMethod,
 | 
						|
                                                  ValueType&& aValue) {
 | 
						|
    if constexpr (SupportChaining) {
 | 
						|
      return InvokeMethod(aThisVal, aMethod, std::forward<ValueType>(aValue));
 | 
						|
    } else {
 | 
						|
      InvokeMethod(aThisVal, aMethod, std::forward<ValueType>(aValue));
 | 
						|
      return nullptr;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  template <typename PromiseType>
 | 
						|
  static void MaybeChain(PromiseType* aFrom,
 | 
						|
                         RefPtr<typename PromiseType::Private>&& aTo) {
 | 
						|
    if (aTo) {
 | 
						|
      MOZ_DIAGNOSTIC_ASSERT(
 | 
						|
          aFrom,
 | 
						|
          "Can't do promise chaining for a non-promise-returning method.");
 | 
						|
      aFrom->ChainTo(aTo.forget(), "<chained completion promise>");
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  template <typename>
 | 
						|
  class ThenCommand;
 | 
						|
 | 
						|
  template <typename...>
 | 
						|
  class ThenValue;
 | 
						|
 | 
						|
  template <typename ThisType, typename ResolveMethodType,
 | 
						|
            typename RejectMethodType>
 | 
						|
  class ThenValue<ThisType*, ResolveMethodType, RejectMethodType>
 | 
						|
      : public ThenValueBase {
 | 
						|
    friend class ThenCommand<ThenValue>;
 | 
						|
 | 
						|
    using R1 = RemoveSmartPointer<MethodReturnType<ResolveMethodType>>;
 | 
						|
    using R2 = RemoveSmartPointer<MethodReturnType<RejectMethodType>>;
 | 
						|
    constexpr static bool SupportChaining =
 | 
						|
        IsMozPromise<R1> && std::is_same_v<R1, R2>;
 | 
						|
 | 
						|
    // Fall back to MozPromise when promise chaining is not supported to make
 | 
						|
    // code compile.
 | 
						|
    using PromiseType = std::conditional_t<SupportChaining, R1, MozPromise>;
 | 
						|
 | 
						|
   public:
 | 
						|
    ThenValue(nsISerialEventTarget* aResponseTarget, ThisType* aThisVal,
 | 
						|
              ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod,
 | 
						|
              StaticString aCallSite)
 | 
						|
        : ThenValueBase(aResponseTarget, aCallSite),
 | 
						|
          mThisVal(aThisVal),
 | 
						|
          mResolveMethod(aResolveMethod),
 | 
						|
          mRejectMethod(aRejectMethod) {}
 | 
						|
 | 
						|
    void Disconnect() override {
 | 
						|
      ThenValueBase::Disconnect();
 | 
						|
 | 
						|
      // If a Request has been disconnected, we don't guarantee that the
 | 
						|
      // resolve/reject runnable will be dispatched. Null out our refcounted
 | 
						|
      // this-value now so that it's released predictably on the dispatch
 | 
						|
      // thread.
 | 
						|
      mThisVal = nullptr;
 | 
						|
    }
 | 
						|
 | 
						|
   protected:
 | 
						|
    MozPromiseBase* CompletionPromise() const override {
 | 
						|
      return mCompletionPromise;
 | 
						|
    }
 | 
						|
 | 
						|
    void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) override {
 | 
						|
      RefPtr<PromiseType> result =
 | 
						|
          aValue.IsResolve()
 | 
						|
              ? InvokeCallbackMethod<SupportChaining, PromiseType>(
 | 
						|
                    mThisVal.get(), mResolveMethod,
 | 
						|
                    MaybeMove(aValue.ResolveValue()))
 | 
						|
              : InvokeCallbackMethod<SupportChaining, PromiseType>(
 | 
						|
                    mThisVal.get(), mRejectMethod,
 | 
						|
                    MaybeMove(aValue.RejectValue()));
 | 
						|
 | 
						|
      // Null out mThisVal after invoking the callback so that any references
 | 
						|
      // are released predictably on the dispatch thread. Otherwise, it would be
 | 
						|
      // released on whatever thread last drops its reference to the ThenValue,
 | 
						|
      // which may or may not be ok.
 | 
						|
      mThisVal = nullptr;
 | 
						|
 | 
						|
      MaybeChain<PromiseType>(result, std::move(mCompletionPromise));
 | 
						|
    }
 | 
						|
 | 
						|
   private:
 | 
						|
    RefPtr<ThisType>
 | 
						|
        mThisVal;  // Only accessed and refcounted on dispatch thread.
 | 
						|
    ResolveMethodType mResolveMethod;
 | 
						|
    RejectMethodType mRejectMethod;
 | 
						|
    RefPtr<typename PromiseType::Private> mCompletionPromise;
 | 
						|
  };
 | 
						|
 | 
						|
  template <typename ThisType, typename ResolveRejectMethodType>
 | 
						|
  class ThenValue<ThisType*, ResolveRejectMethodType> : public ThenValueBase {
 | 
						|
    friend class ThenCommand<ThenValue>;
 | 
						|
 | 
						|
    using R1 = RemoveSmartPointer<MethodReturnType<ResolveRejectMethodType>>;
 | 
						|
    constexpr static bool SupportChaining = IsMozPromise<R1>;
 | 
						|
 | 
						|
    // Fall back to MozPromise when promise chaining is not supported to make
 | 
						|
    // code compile.
 | 
						|
    using PromiseType = std::conditional_t<SupportChaining, R1, MozPromise>;
 | 
						|
 | 
						|
   public:
 | 
						|
    ThenValue(nsISerialEventTarget* aResponseTarget, ThisType* aThisVal,
 | 
						|
              ResolveRejectMethodType aResolveRejectMethod,
 | 
						|
              StaticString aCallSite)
 | 
						|
        : ThenValueBase(aResponseTarget, aCallSite),
 | 
						|
          mThisVal(aThisVal),
 | 
						|
          mResolveRejectMethod(aResolveRejectMethod) {}
 | 
						|
 | 
						|
    void Disconnect() override {
 | 
						|
      ThenValueBase::Disconnect();
 | 
						|
 | 
						|
      // If a Request has been disconnected, we don't guarantee that the
 | 
						|
      // resolve/reject runnable will be dispatched. Null out our refcounted
 | 
						|
      // this-value now so that it's released predictably on the dispatch
 | 
						|
      // thread.
 | 
						|
      mThisVal = nullptr;
 | 
						|
    }
 | 
						|
 | 
						|
   protected:
 | 
						|
    MozPromiseBase* CompletionPromise() const override {
 | 
						|
      return mCompletionPromise;
 | 
						|
    }
 | 
						|
 | 
						|
    void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) override {
 | 
						|
      RefPtr<PromiseType> result =
 | 
						|
          InvokeCallbackMethod<SupportChaining, PromiseType>(
 | 
						|
              mThisVal.get(), mResolveRejectMethod, MaybeMove(aValue));
 | 
						|
 | 
						|
      // Null out mThisVal after invoking the callback so that any references
 | 
						|
      // are released predictably on the dispatch thread. Otherwise, it would be
 | 
						|
      // released on whatever thread last drops its reference to the ThenValue,
 | 
						|
      // which may or may not be ok.
 | 
						|
      mThisVal = nullptr;
 | 
						|
 | 
						|
      MaybeChain<PromiseType>(result, std::move(mCompletionPromise));
 | 
						|
    }
 | 
						|
 | 
						|
   private:
 | 
						|
    RefPtr<ThisType>
 | 
						|
        mThisVal;  // Only accessed and refcounted on dispatch thread.
 | 
						|
    ResolveRejectMethodType mResolveRejectMethod;
 | 
						|
    RefPtr<typename PromiseType::Private> mCompletionPromise;
 | 
						|
  };
 | 
						|
 | 
						|
  // NB: We could use std::function here instead of a template if it were
 | 
						|
  // supported. :-(
 | 
						|
  template <typename ResolveFunction, typename RejectFunction>
 | 
						|
  class ThenValue<ResolveFunction, RejectFunction> : public ThenValueBase {
 | 
						|
    friend class ThenCommand<ThenValue>;
 | 
						|
 | 
						|
    using R1 = RemoveSmartPointer<MethodReturnType<ResolveFunction>>;
 | 
						|
    using R2 = RemoveSmartPointer<MethodReturnType<RejectFunction>>;
 | 
						|
    constexpr static bool SupportChaining =
 | 
						|
        IsMozPromise<R1> && std::is_same_v<R1, R2>;
 | 
						|
 | 
						|
    // Fall back to MozPromise when promise chaining is not supported to make
 | 
						|
    // code compile.
 | 
						|
    using PromiseType = std::conditional_t<SupportChaining, R1, MozPromise>;
 | 
						|
 | 
						|
   public:
 | 
						|
    ThenValue(nsISerialEventTarget* aResponseTarget,
 | 
						|
              ResolveFunction&& aResolveFunction,
 | 
						|
              RejectFunction&& aRejectFunction, StaticString aCallSite)
 | 
						|
        : ThenValueBase(aResponseTarget, aCallSite) {
 | 
						|
      mResolveFunction.emplace(std::move(aResolveFunction));
 | 
						|
      mRejectFunction.emplace(std::move(aRejectFunction));
 | 
						|
    }
 | 
						|
 | 
						|
    void Disconnect() override {
 | 
						|
      ThenValueBase::Disconnect();
 | 
						|
 | 
						|
      // If a Request has been disconnected, we don't guarantee that the
 | 
						|
      // resolve/reject runnable will be dispatched. Destroy our callbacks
 | 
						|
      // now so that any references in closures are released predictable on
 | 
						|
      // the dispatch thread.
 | 
						|
      mResolveFunction.reset();
 | 
						|
      mRejectFunction.reset();
 | 
						|
    }
 | 
						|
 | 
						|
   protected:
 | 
						|
    MozPromiseBase* CompletionPromise() const override {
 | 
						|
      return mCompletionPromise;
 | 
						|
    }
 | 
						|
 | 
						|
    void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) override {
 | 
						|
      // Note: The usage of InvokeCallbackMethod here requires that
 | 
						|
      // ResolveFunction/RejectFunction are capture-lambdas (i.e. anonymous
 | 
						|
      // classes with ::operator()), since it allows us to share code more
 | 
						|
      // easily. We could fix this if need be, though it's quite easy to work
 | 
						|
      // around by just capturing something.
 | 
						|
      RefPtr<PromiseType> result =
 | 
						|
          aValue.IsResolve()
 | 
						|
              ? InvokeCallbackMethod<SupportChaining, PromiseType>(
 | 
						|
                    mResolveFunction.ptr(), &ResolveFunction::operator(),
 | 
						|
                    MaybeMove(aValue.ResolveValue()))
 | 
						|
              : InvokeCallbackMethod<SupportChaining, PromiseType>(
 | 
						|
                    mRejectFunction.ptr(), &RejectFunction::operator(),
 | 
						|
                    MaybeMove(aValue.RejectValue()));
 | 
						|
 | 
						|
      // Destroy callbacks after invocation so that any references in closures
 | 
						|
      // are released predictably on the dispatch thread. Otherwise, they would
 | 
						|
      // be released on whatever thread last drops its reference to the
 | 
						|
      // ThenValue, which may or may not be ok.
 | 
						|
      mResolveFunction.reset();
 | 
						|
      mRejectFunction.reset();
 | 
						|
 | 
						|
      MaybeChain<PromiseType>(result, std::move(mCompletionPromise));
 | 
						|
    }
 | 
						|
 | 
						|
   private:
 | 
						|
    Maybe<ResolveFunction>
 | 
						|
        mResolveFunction;  // Only accessed and deleted on dispatch thread.
 | 
						|
    Maybe<RejectFunction>
 | 
						|
        mRejectFunction;  // Only accessed and deleted on dispatch thread.
 | 
						|
    RefPtr<typename PromiseType::Private> mCompletionPromise;
 | 
						|
  };
 | 
						|
 | 
						|
  template <typename ResolveRejectFunction>
 | 
						|
  class ThenValue<ResolveRejectFunction> : public ThenValueBase {
 | 
						|
    friend class ThenCommand<ThenValue>;
 | 
						|
 | 
						|
    using R1 = RemoveSmartPointer<MethodReturnType<ResolveRejectFunction>>;
 | 
						|
    constexpr static bool SupportChaining = IsMozPromise<R1>;
 | 
						|
 | 
						|
    // Fall back to MozPromise when promise chaining is not supported to make
 | 
						|
    // code compile.
 | 
						|
    using PromiseType = std::conditional_t<SupportChaining, R1, MozPromise>;
 | 
						|
 | 
						|
   public:
 | 
						|
    ThenValue(nsISerialEventTarget* aResponseTarget,
 | 
						|
              ResolveRejectFunction&& aResolveRejectFunction,
 | 
						|
              StaticString aCallSite)
 | 
						|
        : ThenValueBase(aResponseTarget, aCallSite) {
 | 
						|
      mResolveRejectFunction.emplace(std::move(aResolveRejectFunction));
 | 
						|
    }
 | 
						|
 | 
						|
    void Disconnect() override {
 | 
						|
      ThenValueBase::Disconnect();
 | 
						|
 | 
						|
      // If a Request has been disconnected, we don't guarantee that the
 | 
						|
      // resolve/reject runnable will be dispatched. Destroy our callbacks
 | 
						|
      // now so that any references in closures are released predictable on
 | 
						|
      // the dispatch thread.
 | 
						|
      mResolveRejectFunction.reset();
 | 
						|
    }
 | 
						|
 | 
						|
   protected:
 | 
						|
    MozPromiseBase* CompletionPromise() const override {
 | 
						|
      return mCompletionPromise;
 | 
						|
    }
 | 
						|
 | 
						|
    void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) override {
 | 
						|
      // Note: The usage of InvokeCallbackMethod here requires that
 | 
						|
      // ResolveRejectFunction is capture-lambdas (i.e. anonymous
 | 
						|
      // classes with ::operator()), since it allows us to share code more
 | 
						|
      // easily. We could fix this if need be, though it's quite easy to work
 | 
						|
      // around by just capturing something.
 | 
						|
      RefPtr<PromiseType> result =
 | 
						|
          InvokeCallbackMethod<SupportChaining, PromiseType>(
 | 
						|
              mResolveRejectFunction.ptr(), &ResolveRejectFunction::operator(),
 | 
						|
              MaybeMove(aValue));
 | 
						|
 | 
						|
      // Destroy callbacks after invocation so that any references in closures
 | 
						|
      // are released predictably on the dispatch thread. Otherwise, they would
 | 
						|
      // be released on whatever thread last drops its reference to the
 | 
						|
      // ThenValue, which may or may not be ok.
 | 
						|
      mResolveRejectFunction.reset();
 | 
						|
 | 
						|
      MaybeChain<PromiseType>(result, std::move(mCompletionPromise));
 | 
						|
    }
 | 
						|
 | 
						|
   private:
 | 
						|
    Maybe<ResolveRejectFunction>
 | 
						|
        mResolveRejectFunction;  // Only accessed and deleted on dispatch
 | 
						|
                                 // thread.
 | 
						|
    RefPtr<typename PromiseType::Private> mCompletionPromise;
 | 
						|
  };
 | 
						|
 | 
						|
  template <typename ResolveFunction>
 | 
						|
  class MapValue final : public ThenValueBase {
 | 
						|
    friend class ThenCommand<MapValue>;
 | 
						|
    constexpr static const bool SupportChaining = true;
 | 
						|
    using ResolveValueT_ = std::invoke_result_t<ResolveFunction, ResolveValueT>;
 | 
						|
    using PromiseType = MozPromise<ResolveValueT_, RejectValueT, IsExclusive>;
 | 
						|
 | 
						|
   public:
 | 
						|
    explicit MapValue(nsISerialEventTarget* aResponseTarget,
 | 
						|
                      ResolveFunction&& f, StaticString aCallSite)
 | 
						|
        : ThenValueBase(aResponseTarget, aCallSite),
 | 
						|
          mResolveFunction(Some(std::forward<ResolveFunction>(f))) {}
 | 
						|
 | 
						|
   protected:
 | 
						|
    void Disconnect() override {
 | 
						|
      ThenValueBase::Disconnect();
 | 
						|
      mResolveFunction.reset();
 | 
						|
    }
 | 
						|
 | 
						|
    MozPromiseBase* CompletionPromise() const override {
 | 
						|
      return mCompletionPromise;
 | 
						|
    }
 | 
						|
 | 
						|
    void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) override {
 | 
						|
      // Note that promise-chaining is always supported here; this function can
 | 
						|
      // only transform from MozPromise<A, B, k> to MozPromise<A2, B, k>.
 | 
						|
      auto value = MaybeMove(aValue);
 | 
						|
      typename PromiseType::ResolveOrRejectValue output;
 | 
						|
 | 
						|
      if (value.IsResolve()) {
 | 
						|
        output.SetResolve((*mResolveFunction)(std::move(value.ResolveValue())));
 | 
						|
      } else {
 | 
						|
        output.SetReject(std::move(value.RejectValue()));
 | 
						|
      }
 | 
						|
 | 
						|
      if (mCompletionPromise) {
 | 
						|
        mCompletionPromise->ResolveOrReject(std::move(output),
 | 
						|
                                            ThenValueBase::mCallSite);
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
   private:
 | 
						|
    Maybe<ResolveFunction> mResolveFunction;
 | 
						|
    RefPtr<typename PromiseType::Private> mCompletionPromise;
 | 
						|
  };
 | 
						|
 | 
						|
  template <typename RejectFunction>
 | 
						|
  class MapErrValue final : public ThenValueBase {
 | 
						|
    friend class ThenCommand<MapErrValue>;
 | 
						|
    constexpr static const bool SupportChaining = true;
 | 
						|
    using RejectValueT_ = std::invoke_result_t<RejectFunction, RejectValueT>;
 | 
						|
    using PromiseType = MozPromise<ResolveValueT, RejectValueT_, IsExclusive>;
 | 
						|
 | 
						|
   public:
 | 
						|
    explicit MapErrValue(nsISerialEventTarget* aResponseTarget,
 | 
						|
                         RejectFunction&& f, StaticString aCallSite)
 | 
						|
        : ThenValueBase(aResponseTarget, aCallSite),
 | 
						|
          mRejectFunction(Some(std::forward<RejectFunction>(f))) {}
 | 
						|
 | 
						|
   protected:
 | 
						|
    void Disconnect() override {
 | 
						|
      ThenValueBase::Disconnect();
 | 
						|
      mRejectFunction.reset();
 | 
						|
    }
 | 
						|
 | 
						|
    MozPromiseBase* CompletionPromise() const override {
 | 
						|
      return mCompletionPromise;
 | 
						|
    }
 | 
						|
 | 
						|
    void DoResolveOrRejectInternal(ResolveOrRejectValue& aValue) override {
 | 
						|
      // Note that promise-chaining is always supported here; this function can
 | 
						|
      // only transform from MozPromise<A, B, k> to MozPromise<A, B2, k>.
 | 
						|
      auto value = MaybeMove(aValue);
 | 
						|
      typename PromiseType::ResolveOrRejectValue output;
 | 
						|
 | 
						|
      if (value.IsResolve()) {
 | 
						|
        output.SetResolve(std::move(value.ResolveValue()));
 | 
						|
      } else {
 | 
						|
        output.SetReject((*mRejectFunction)(std::move(value.RejectValue())));
 | 
						|
      }
 | 
						|
 | 
						|
      if (mCompletionPromise) {
 | 
						|
        mCompletionPromise->ResolveOrReject(std::move(output),
 | 
						|
                                            ThenValueBase::mCallSite);
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
   private:
 | 
						|
    Maybe<RejectFunction> mRejectFunction;
 | 
						|
    RefPtr<typename PromiseType::Private> mCompletionPromise;
 | 
						|
  };
 | 
						|
 | 
						|
 public:
 | 
						|
  void ThenInternal(already_AddRefed<ThenValueBase> aThenValue,
 | 
						|
                    StaticString aCallSite) {
 | 
						|
    PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic &&
 | 
						|
                   mMagic3 == sMagic && mMagic4 == &mMutex);
 | 
						|
    RefPtr<ThenValueBase> thenValue = aThenValue;
 | 
						|
    MutexAutoLock lock(mMutex);
 | 
						|
    MOZ_DIAGNOSTIC_ASSERT(
 | 
						|
        !IsExclusive || !mHaveRequest,
 | 
						|
        "Using an exclusive promise in a non-exclusive fashion");
 | 
						|
    mHaveRequest = true;
 | 
						|
    PROMISE_LOG("%s invoking Then() [this=%p, aThenValue=%p, isPending=%d]",
 | 
						|
                aCallSite.get(), this, thenValue.get(), (int)IsPending());
 | 
						|
    if (!IsPending()) {
 | 
						|
      thenValue->Dispatch(this);
 | 
						|
    } else {
 | 
						|
      mThenValues.AppendElement(thenValue.forget());
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
 protected:
 | 
						|
  /*
 | 
						|
   * A command object to store all information needed to make a request to the
 | 
						|
   * promise. This allows us to delay the request until further use is known
 | 
						|
   * (whether it is ->Then() again for more promise chaining or ->Track() to
 | 
						|
   * terminate chaining and issue the request).
 | 
						|
   *
 | 
						|
   * This allows a unified syntax for promise chaining and disconnection, and
 | 
						|
   * feels more like its JS counterpart.
 | 
						|
   *
 | 
						|
   * Note that a ThenCommand is always exclusive, even if its source or result
 | 
						|
   * promises are not. To attach multiple continuations, explicitly convert it
 | 
						|
   * to a promise first.
 | 
						|
   */
 | 
						|
  template <typename ThenValueType>
 | 
						|
  class MOZ_TEMPORARY_CLASS ThenCommand {
 | 
						|
    // Allow Promise1::ThenCommand to access the private constructor
 | 
						|
    // Promise2::ThenCommand(ThenCommand&&).
 | 
						|
    template <typename, typename, bool>
 | 
						|
    friend class MozPromise;
 | 
						|
 | 
						|
    using PromiseType = typename ThenValueType::PromiseType;
 | 
						|
    using Private = typename PromiseType::Private;
 | 
						|
 | 
						|
    ThenCommand(StaticString aCallSite,
 | 
						|
                already_AddRefed<ThenValueType> aThenValue,
 | 
						|
                MozPromise* aReceiver)
 | 
						|
        : mCallSite(aCallSite), mThenValue(aThenValue), mReceiver(aReceiver) {}
 | 
						|
 | 
						|
    ThenCommand(ThenCommand&& aOther) noexcept = default;
 | 
						|
 | 
						|
   public:
 | 
						|
    ~ThenCommand() {
 | 
						|
      // Issue the request now if the return value of Then() is not used.
 | 
						|
      if (mThenValue) {
 | 
						|
        mReceiver->ThenInternal(mThenValue.forget(), mCallSite);
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    // Allow RefPtr<MozPromise> p = somePromise->Then();
 | 
						|
    //       p->Then(thread1, ...);
 | 
						|
    //       p->Then(thread2, ...);
 | 
						|
    operator RefPtr<PromiseType>() {
 | 
						|
      static_assert(
 | 
						|
          ThenValueType::SupportChaining,
 | 
						|
          "The resolve/reject callback needs to return a RefPtr<MozPromise> "
 | 
						|
          "in order to do promise chaining.");
 | 
						|
 | 
						|
      // mCompletionPromise must be created before ThenInternal() to avoid race.
 | 
						|
      RefPtr<Private> p =
 | 
						|
          new Private("<completion promise>", true /* aIsCompletionPromise */);
 | 
						|
      mThenValue->mCompletionPromise = p;
 | 
						|
      // Note ThenInternal() might nullify mCompletionPromise before return.
 | 
						|
      // So we need to return p instead of mCompletionPromise.
 | 
						|
      mReceiver->ThenInternal(mThenValue.forget(), mCallSite);
 | 
						|
      return p;
 | 
						|
    }
 | 
						|
 | 
						|
    template <typename... Ts>
 | 
						|
    auto Then(Ts&&... aArgs) -> decltype(std::declval<PromiseType>().Then(
 | 
						|
        std::forward<Ts>(aArgs)...)) {
 | 
						|
      return static_cast<RefPtr<PromiseType>>(*this)->Then(
 | 
						|
          std::forward<Ts>(aArgs)...);
 | 
						|
    }
 | 
						|
 | 
						|
    template <typename... Ts>
 | 
						|
    auto Map(Ts&&... aArgs) -> decltype(std::declval<PromiseType>().Map(
 | 
						|
        std::forward<Ts>(aArgs)...)) {
 | 
						|
      return static_cast<RefPtr<PromiseType>>(*this)->Map(
 | 
						|
          std::forward<Ts>(aArgs)...);
 | 
						|
    }
 | 
						|
 | 
						|
    template <typename... Ts>
 | 
						|
    auto MapErr(Ts&&... aArgs) -> decltype(std::declval<PromiseType>().MapErr(
 | 
						|
        std::forward<Ts>(aArgs)...)) {
 | 
						|
      return static_cast<RefPtr<PromiseType>>(*this)->MapErr(
 | 
						|
          std::forward<Ts>(aArgs)...);
 | 
						|
    }
 | 
						|
 | 
						|
    void Track(MozPromiseRequestHolder<MozPromise>& aRequestHolder) {
 | 
						|
      aRequestHolder.Track(do_AddRef(mThenValue));
 | 
						|
      mReceiver->ThenInternal(mThenValue.forget(), mCallSite);
 | 
						|
    }
 | 
						|
 | 
						|
    // Allow calling ->Then() again for more promise chaining or ->Track() to
 | 
						|
    // end chaining and track the request for future disconnection.
 | 
						|
    ThenCommand* operator->() { return this; }
 | 
						|
 | 
						|
   private:
 | 
						|
    StaticString mCallSite;
 | 
						|
    RefPtr<ThenValueType> mThenValue;
 | 
						|
    RefPtr<MozPromise> mReceiver;
 | 
						|
  };
 | 
						|
 | 
						|
 public:
 | 
						|
  template <typename ThisType, typename... Methods,
 | 
						|
            typename ThenValueType = ThenValue<ThisType*, Methods...>,
 | 
						|
            typename ReturnType = ThenCommand<ThenValueType>>
 | 
						|
  ReturnType Then(nsISerialEventTarget* aResponseTarget, StaticString aCallSite,
 | 
						|
                  ThisType* aThisVal, Methods... aMethods) {
 | 
						|
    RefPtr<ThenValueType> thenValue =
 | 
						|
        new ThenValueType(aResponseTarget, aThisVal, aMethods..., aCallSite);
 | 
						|
    return ReturnType(aCallSite, thenValue.forget(), this);
 | 
						|
  }
 | 
						|
 | 
						|
  template <typename... Functions,
 | 
						|
            typename ThenValueType = ThenValue<Functions...>,
 | 
						|
            typename ReturnType = ThenCommand<ThenValueType>>
 | 
						|
  ReturnType Then(nsISerialEventTarget* aResponseTarget, StaticString aCallSite,
 | 
						|
                  Functions&&... aFunctions) {
 | 
						|
    RefPtr<ThenValueType> thenValue =
 | 
						|
        new ThenValueType(aResponseTarget, std::move(aFunctions)..., aCallSite);
 | 
						|
    return ReturnType(aCallSite, thenValue.forget(), this);
 | 
						|
  }
 | 
						|
 | 
						|
  // Shorthand for a `Then` which simply forwards the reject-value, but performs
 | 
						|
  // some additional work with the resolve-value.
 | 
						|
  template <typename Function>
 | 
						|
  auto Map(nsISerialEventTarget* aResponseTarget, StaticString aCallSite,
 | 
						|
           Function&& function) {
 | 
						|
    RefPtr<MapValue<Function>> thenValue = new MapValue<Function>(
 | 
						|
        aResponseTarget, std::forward<Function>(function), aCallSite);
 | 
						|
    return ThenCommand<MapValue<Function>>(aCallSite, thenValue.forget(), this);
 | 
						|
  }
 | 
						|
 | 
						|
  // Shorthand for a `Then` which simply forwards the resolve-value, but
 | 
						|
  // performs some additional work with the reject-value.
 | 
						|
  template <typename Function>
 | 
						|
  auto MapErr(nsISerialEventTarget* aResponseTarget, StaticString aCallSite,
 | 
						|
              Function&& function) {
 | 
						|
    RefPtr<MapErrValue<Function>> thenValue = new MapErrValue<Function>(
 | 
						|
        aResponseTarget, std::forward<Function>(function), aCallSite);
 | 
						|
    return ThenCommand<MapErrValue<Function>>(aCallSite, thenValue.forget(),
 | 
						|
                                              this);
 | 
						|
  }
 | 
						|
 | 
						|
  void ChainTo(already_AddRefed<Private> aChainedPromise,
 | 
						|
               StaticString aCallSite) {
 | 
						|
    MutexAutoLock lock(mMutex);
 | 
						|
    MOZ_DIAGNOSTIC_ASSERT(
 | 
						|
        !IsExclusive || !mHaveRequest,
 | 
						|
        "Using an exclusive promise in a non-exclusive fashion");
 | 
						|
    mHaveRequest = true;
 | 
						|
    RefPtr<Private> chainedPromise = aChainedPromise;
 | 
						|
    PROMISE_LOG(
 | 
						|
        "%s invoking Chain() [this=%p, chainedPromise=%p, isPending=%d]",
 | 
						|
        aCallSite.get(), this, chainedPromise.get(), (int)IsPending());
 | 
						|
 | 
						|
    // We want to use the same type of dispatching method with the chained
 | 
						|
    // promises.
 | 
						|
 | 
						|
    // We need to ensure that the UseSynchronousTaskDispatch branch isn't taken
 | 
						|
    // at compilation time to ensure we're not triggering the static_assert in
 | 
						|
    // UseSynchronousTaskDispatch method. if constexpr (IsExclusive) ensures
 | 
						|
    // that.
 | 
						|
    if (mUseDirectTaskDispatch) {
 | 
						|
      chainedPromise->UseDirectTaskDispatch(aCallSite);
 | 
						|
    } else if constexpr (IsExclusive) {
 | 
						|
      if (mUseSynchronousTaskDispatch) {
 | 
						|
        chainedPromise->UseSynchronousTaskDispatch(aCallSite);
 | 
						|
      }
 | 
						|
    } else {
 | 
						|
      chainedPromise->SetTaskPriority(mPriority, aCallSite);
 | 
						|
    }
 | 
						|
 | 
						|
    if (!IsPending()) {
 | 
						|
      ForwardTo(chainedPromise);
 | 
						|
    } else {
 | 
						|
      mChainedPromises.AppendElement(chainedPromise);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
#ifdef MOZ_WIDGET_ANDROID
 | 
						|
  // Creates a C++ MozPromise from its Java counterpart, GeckoResult.
 | 
						|
  [[nodiscard]] static RefPtr<MozPromise> FromGeckoResult(
 | 
						|
      java::GeckoResult::Param aGeckoResult) {
 | 
						|
    using jni::GeckoResultCallback;
 | 
						|
    RefPtr<Private> p = new Private("GeckoResult Glue", false);
 | 
						|
    auto resolve = GeckoResultCallback::CreateAndAttach<ResolveValueType>(
 | 
						|
        [p](ResolveValueType&& aArg) {
 | 
						|
          p->Resolve(MaybeMove(aArg), __func__);
 | 
						|
        });
 | 
						|
    auto reject = GeckoResultCallback::CreateAndAttach<RejectValueType>(
 | 
						|
        [p](RejectValueType&& aArg) { p->Reject(MaybeMove(aArg), __func__); });
 | 
						|
    aGeckoResult->NativeThen(resolve, reject);
 | 
						|
    return p;
 | 
						|
  }
 | 
						|
#endif
 | 
						|
 | 
						|
  // Note we expose the function AssertIsDead() instead of IsDead() since
 | 
						|
  // checking IsDead() is a data race in the situation where the request is not
 | 
						|
  // dead. Therefore we enforce the form |Assert(IsDead())| by exposing
 | 
						|
  // AssertIsDead() only.
 | 
						|
  void AssertIsDead() override {
 | 
						|
    PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic &&
 | 
						|
                   mMagic3 == sMagic && mMagic4 == &mMutex);
 | 
						|
    MutexAutoLock lock(mMutex);
 | 
						|
    for (auto&& then : mThenValues) {
 | 
						|
      then->AssertIsDead();
 | 
						|
    }
 | 
						|
    for (auto&& chained : mChainedPromises) {
 | 
						|
      chained->AssertIsDead();
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  bool IsResolved() const { return mValue.IsResolve(); }
 | 
						|
 | 
						|
 protected:
 | 
						|
  bool IsPending() const { return mValue.IsNothing(); }
 | 
						|
 | 
						|
  ResolveOrRejectValue& Value() {
 | 
						|
    // This method should only be called once the value has stabilized. As
 | 
						|
    // such, we don't need to acquire the lock here.
 | 
						|
    MOZ_DIAGNOSTIC_ASSERT(!IsPending());
 | 
						|
    return mValue;
 | 
						|
  }
 | 
						|
 | 
						|
  void DispatchAll() {
 | 
						|
    mMutex.AssertCurrentThreadOwns();
 | 
						|
    for (auto&& thenValue : mThenValues) {
 | 
						|
      thenValue->Dispatch(this);
 | 
						|
    }
 | 
						|
    mThenValues.Clear();
 | 
						|
 | 
						|
    for (auto&& chainedPromise : mChainedPromises) {
 | 
						|
      ForwardTo(chainedPromise);
 | 
						|
    }
 | 
						|
    mChainedPromises.Clear();
 | 
						|
  }
 | 
						|
 | 
						|
  void ForwardTo(Private* aOther) {
 | 
						|
    MOZ_ASSERT(!IsPending());
 | 
						|
    if (mValue.IsResolve()) {
 | 
						|
      aOther->Resolve(MaybeMove(mValue.ResolveValue()), "<chained promise>");
 | 
						|
    } else {
 | 
						|
      aOther->Reject(MaybeMove(mValue.RejectValue()), "<chained promise>");
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  virtual ~MozPromise() {
 | 
						|
    PROMISE_LOG("MozPromise::~MozPromise [this=%p]", this);
 | 
						|
    AssertIsDead();
 | 
						|
    // We can't guarantee a completion promise will always be revolved or
 | 
						|
    // rejected since ResolveOrRejectRunnable might not run when dispatch fails.
 | 
						|
    if (!mIsCompletionPromise) {
 | 
						|
      MOZ_ASSERT(!IsPending());
 | 
						|
      MOZ_ASSERT(mThenValues.IsEmpty());
 | 
						|
      MOZ_ASSERT(mChainedPromises.IsEmpty());
 | 
						|
    }
 | 
						|
#ifdef PROMISE_DEBUG
 | 
						|
    mMagic1 = 0;
 | 
						|
    mMagic2 = 0;
 | 
						|
    mMagic3 = 0;
 | 
						|
    mMagic4 = nullptr;
 | 
						|
#endif
 | 
						|
  };
 | 
						|
 | 
						|
  StaticString mCreationSite;  // For logging
 | 
						|
  Mutex mMutex MOZ_UNANNOTATED;
 | 
						|
  ResolveOrRejectValue mValue;
 | 
						|
  bool mUseSynchronousTaskDispatch = false;
 | 
						|
  bool mUseDirectTaskDispatch = false;
 | 
						|
  uint32_t mPriority = nsIRunnablePriority::PRIORITY_NORMAL;
 | 
						|
#ifdef PROMISE_DEBUG
 | 
						|
  uint32_t mMagic1 = sMagic;
 | 
						|
#endif
 | 
						|
  // Try shows we never have more than 3 elements when IsExclusive is false.
 | 
						|
  // So '3' is a good value to avoid heap allocation in most cases.
 | 
						|
  AutoTArray<RefPtr<ThenValueBase>, IsExclusive ? 1 : 3> mThenValues;
 | 
						|
#ifdef PROMISE_DEBUG
 | 
						|
  uint32_t mMagic2 = sMagic;
 | 
						|
#endif
 | 
						|
  nsTArray<RefPtr<Private>> mChainedPromises;
 | 
						|
#ifdef PROMISE_DEBUG
 | 
						|
  uint32_t mMagic3 = sMagic;
 | 
						|
#endif
 | 
						|
  bool mHaveRequest;
 | 
						|
  const bool mIsCompletionPromise;
 | 
						|
#ifdef PROMISE_DEBUG
 | 
						|
  void* mMagic4;
 | 
						|
#endif
 | 
						|
};
 | 
						|
 | 
						|
template <typename ResolveValueT, typename RejectValueT, bool IsExclusive>
 | 
						|
class MozPromise<ResolveValueT, RejectValueT, IsExclusive>::Private
 | 
						|
    : public MozPromise<ResolveValueT, RejectValueT, IsExclusive> {
 | 
						|
 public:
 | 
						|
  explicit Private(StaticString aCreationSite,
 | 
						|
                   bool aIsCompletionPromise = false)
 | 
						|
      : MozPromise(aCreationSite, aIsCompletionPromise) {}
 | 
						|
 | 
						|
  template <typename ResolveValueT_>
 | 
						|
  void Resolve(ResolveValueT_&& aResolveValue, StaticString aResolveSite) {
 | 
						|
    PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic &&
 | 
						|
                   mMagic3 == sMagic && mMagic4 == &mMutex);
 | 
						|
    MutexAutoLock lock(mMutex);
 | 
						|
    PROMISE_LOG("%s resolving MozPromise (%p created at %s)",
 | 
						|
                aResolveSite.get(), this, mCreationSite.get());
 | 
						|
    if (!IsPending()) {
 | 
						|
      PROMISE_LOG(
 | 
						|
          "%s ignored already resolved or rejected MozPromise (%p created at "
 | 
						|
          "%s)",
 | 
						|
          aResolveSite.get(), this, mCreationSite.get());
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    mValue.SetResolve(std::forward<ResolveValueT_>(aResolveValue));
 | 
						|
    DispatchAll();
 | 
						|
  }
 | 
						|
 | 
						|
  template <typename RejectValueT_>
 | 
						|
  void Reject(RejectValueT_&& aRejectValue, StaticString aRejectSite) {
 | 
						|
    PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic &&
 | 
						|
                   mMagic3 == sMagic && mMagic4 == &mMutex);
 | 
						|
    MutexAutoLock lock(mMutex);
 | 
						|
    PROMISE_LOG("%s rejecting MozPromise (%p created at %s)", aRejectSite.get(),
 | 
						|
                this, mCreationSite.get());
 | 
						|
    if (!IsPending()) {
 | 
						|
      PROMISE_LOG(
 | 
						|
          "%s ignored already resolved or rejected MozPromise (%p created at "
 | 
						|
          "%s)",
 | 
						|
          aRejectSite.get(), this, mCreationSite.get());
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    mValue.SetReject(std::forward<RejectValueT_>(aRejectValue));
 | 
						|
    DispatchAll();
 | 
						|
  }
 | 
						|
 | 
						|
  template <typename ResolveOrRejectValue_>
 | 
						|
  void ResolveOrReject(ResolveOrRejectValue_&& aValue, StaticString aSite) {
 | 
						|
    PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic &&
 | 
						|
                   mMagic3 == sMagic && mMagic4 == &mMutex);
 | 
						|
    MutexAutoLock lock(mMutex);
 | 
						|
    PROMISE_LOG("%s resolveOrRejecting MozPromise (%p created at %s)",
 | 
						|
                aSite.get(), this, mCreationSite.get());
 | 
						|
    if (!IsPending()) {
 | 
						|
      PROMISE_LOG(
 | 
						|
          "%s ignored already resolved or rejected MozPromise (%p created at "
 | 
						|
          "%s)",
 | 
						|
          aSite.get(), this, mCreationSite.get());
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    mValue = std::forward<ResolveOrRejectValue_>(aValue);
 | 
						|
    DispatchAll();
 | 
						|
  }
 | 
						|
 | 
						|
  // If the caller and target are both on the same thread, run the the resolve
 | 
						|
  // or reject callback synchronously. Otherwise, the task will be dispatched
 | 
						|
  // via the target Dispatch method.
 | 
						|
  void UseSynchronousTaskDispatch(const char* aSite) {
 | 
						|
    static_assert(
 | 
						|
        IsExclusive,
 | 
						|
        "Synchronous dispatch can only be used with exclusive promises");
 | 
						|
    PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic &&
 | 
						|
                   mMagic3 == sMagic && mMagic4 == &mMutex);
 | 
						|
    MutexAutoLock lock(mMutex);
 | 
						|
    PROMISE_LOG("%s UseSynchronousTaskDispatch MozPromise (%p created at %s)",
 | 
						|
                aSite, this, mCreationSite.get());
 | 
						|
    MOZ_ASSERT(IsPending(),
 | 
						|
               "A Promise must not have been already resolved or rejected to "
 | 
						|
               "set dispatch state");
 | 
						|
    mUseSynchronousTaskDispatch = true;
 | 
						|
  }
 | 
						|
 | 
						|
  // If the caller and target are both on the same thread, run the
 | 
						|
  // resolve/reject callback off the direct task queue instead. This avoids a
 | 
						|
  // full trip to the back of the event queue for each additional asynchronous
 | 
						|
  // step when using MozPromise, and is similar (but not identical to) the
 | 
						|
  // microtask semantics of JS promises.
 | 
						|
  void UseDirectTaskDispatch(const char* aSite) {
 | 
						|
    PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic &&
 | 
						|
                   mMagic3 == sMagic && mMagic4 == &mMutex);
 | 
						|
    MutexAutoLock lock(mMutex);
 | 
						|
    PROMISE_LOG("%s UseDirectTaskDispatch MozPromise (%p created at %s)", aSite,
 | 
						|
                this, mCreationSite.get());
 | 
						|
    MOZ_ASSERT(IsPending(),
 | 
						|
               "A Promise must not have been already resolved or rejected to "
 | 
						|
               "set dispatch state");
 | 
						|
    MOZ_ASSERT(!mUseSynchronousTaskDispatch,
 | 
						|
               "Promise already set for synchronous dispatch");
 | 
						|
    mUseDirectTaskDispatch = true;
 | 
						|
  }
 | 
						|
 | 
						|
  // If the resolve/reject will be handled on a thread supporting priorities,
 | 
						|
  // one may want to tweak the priority of the task by passing a
 | 
						|
  // nsIRunnablePriority::PRIORITY_* to SetTaskPriority.
 | 
						|
  void SetTaskPriority(uint32_t aPriority, const char* aSite) {
 | 
						|
    PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic &&
 | 
						|
                   mMagic3 == sMagic && mMagic4 == &mMutex);
 | 
						|
    MutexAutoLock lock(mMutex);
 | 
						|
    PROMISE_LOG("%s TaskPriority MozPromise (%p created at %s)", aSite, this,
 | 
						|
                mCreationSite.get());
 | 
						|
    MOZ_ASSERT(IsPending(),
 | 
						|
               "A Promise must not have been already resolved or rejected to "
 | 
						|
               "set dispatch state");
 | 
						|
    MOZ_ASSERT(!mUseSynchronousTaskDispatch,
 | 
						|
               "Promise already set for synchronous dispatch");
 | 
						|
    MOZ_ASSERT(!mUseDirectTaskDispatch,
 | 
						|
               "Promise already set for direct dispatch");
 | 
						|
    mPriority = aPriority;
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
// A generic promise type that does the trick for simple use cases.
 | 
						|
//
 | 
						|
// Vaguely deprecated: prefer explicitly naming the resolve- and reject-type.
 | 
						|
// Additionally, prefer `mozilla::Ok` as the resolve-type if the boolean's value
 | 
						|
// is irrelevant.
 | 
						|
using GenericPromise = MozPromise<bool, nsresult, /* IsExclusive = */ true>;
 | 
						|
 | 
						|
// A generic, non-exclusive promise type that does the trick for simple use
 | 
						|
// cases.
 | 
						|
//
 | 
						|
// Vaguely deprecated, as above.
 | 
						|
using GenericNonExclusivePromise =
 | 
						|
    MozPromise<bool, nsresult, /* IsExclusive = */ false>;
 | 
						|
 | 
						|
/*
 | 
						|
 * Class to encapsulate a promise for a particular role. Use this as the member
 | 
						|
 * variable for a class whose method returns a promise.
 | 
						|
 */
 | 
						|
template <typename PromiseType, typename ImplType>
 | 
						|
class MozPromiseHolderBase {
 | 
						|
 public:
 | 
						|
  MozPromiseHolderBase() = default;
 | 
						|
 | 
						|
  MozPromiseHolderBase(MozPromiseHolderBase&& aOther) noexcept = default;
 | 
						|
  MozPromiseHolderBase& operator=(MozPromiseHolderBase&& aOther) noexcept =
 | 
						|
      default;
 | 
						|
 | 
						|
  ~MozPromiseHolderBase() { MOZ_ASSERT(!mPromise); }
 | 
						|
 | 
						|
  already_AddRefed<PromiseType> Ensure(StaticString aMethodName) {
 | 
						|
    static_cast<ImplType*>(this)->Check();
 | 
						|
    if (!mPromise) {
 | 
						|
      mPromise = new (typename PromiseType::Private)(aMethodName);
 | 
						|
    }
 | 
						|
    RefPtr<PromiseType> p = mPromise.get();
 | 
						|
    return p.forget();
 | 
						|
  }
 | 
						|
 | 
						|
  bool IsEmpty() const {
 | 
						|
    static_cast<const ImplType*>(this)->Check();
 | 
						|
    return !mPromise;
 | 
						|
  }
 | 
						|
 | 
						|
  already_AddRefed<typename PromiseType::Private> Steal() {
 | 
						|
    static_cast<ImplType*>(this)->Check();
 | 
						|
    return mPromise.forget();
 | 
						|
  }
 | 
						|
 | 
						|
  template <typename ResolveValueType_>
 | 
						|
  void Resolve(ResolveValueType_&& aResolveValue, StaticString aMethodName) {
 | 
						|
    static_assert(std::is_convertible_v<ResolveValueType_,
 | 
						|
                                        typename PromiseType::ResolveValueType>,
 | 
						|
                  "Resolve() argument must be implicitly convertible to "
 | 
						|
                  "MozPromise's ResolveValueT");
 | 
						|
 | 
						|
    static_cast<ImplType*>(this)->Check();
 | 
						|
    MOZ_ASSERT(mPromise);
 | 
						|
    mPromise->Resolve(std::forward<ResolveValueType_>(aResolveValue),
 | 
						|
                      aMethodName);
 | 
						|
    mPromise = nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  template <typename ResolveValueType_>
 | 
						|
  void ResolveIfExists(ResolveValueType_&& aResolveValue,
 | 
						|
                       StaticString aMethodName) {
 | 
						|
    if (!IsEmpty()) {
 | 
						|
      Resolve(std::forward<ResolveValueType_>(aResolveValue), aMethodName);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  template <typename RejectValueType_>
 | 
						|
  void Reject(RejectValueType_&& aRejectValue, StaticString aMethodName) {
 | 
						|
    static_assert(std::is_convertible_v<RejectValueType_,
 | 
						|
                                        typename PromiseType::RejectValueType>,
 | 
						|
                  "Reject() argument must be implicitly convertible to "
 | 
						|
                  "MozPromise's RejectValueT");
 | 
						|
 | 
						|
    static_cast<ImplType*>(this)->Check();
 | 
						|
    MOZ_ASSERT(mPromise);
 | 
						|
    mPromise->Reject(std::forward<RejectValueType_>(aRejectValue), aMethodName);
 | 
						|
    mPromise = nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  template <typename RejectValueType_>
 | 
						|
  void RejectIfExists(RejectValueType_&& aRejectValue,
 | 
						|
                      StaticString aMethodName) {
 | 
						|
    if (!IsEmpty()) {
 | 
						|
      Reject(std::forward<RejectValueType_>(aRejectValue), aMethodName);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  template <typename ResolveOrRejectValueType_>
 | 
						|
  void ResolveOrReject(ResolveOrRejectValueType_&& aValue,
 | 
						|
                       StaticString aMethodName) {
 | 
						|
    static_cast<ImplType*>(this)->Check();
 | 
						|
    MOZ_ASSERT(mPromise);
 | 
						|
    mPromise->ResolveOrReject(std::forward<ResolveOrRejectValueType_>(aValue),
 | 
						|
                              aMethodName);
 | 
						|
    mPromise = nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  template <typename ResolveOrRejectValueType_>
 | 
						|
  void ResolveOrRejectIfExists(ResolveOrRejectValueType_&& aValue,
 | 
						|
                               StaticString aMethodName) {
 | 
						|
    if (!IsEmpty()) {
 | 
						|
      ResolveOrReject(std::forward<ResolveOrRejectValueType_>(aValue),
 | 
						|
                      aMethodName);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  void UseSynchronousTaskDispatch(const char* aSite) {
 | 
						|
    MOZ_ASSERT(mPromise);
 | 
						|
    mPromise->UseSynchronousTaskDispatch(aSite);
 | 
						|
  }
 | 
						|
 | 
						|
  void UseDirectTaskDispatch(const char* aSite) {
 | 
						|
    MOZ_ASSERT(mPromise);
 | 
						|
    mPromise->UseDirectTaskDispatch(aSite);
 | 
						|
  }
 | 
						|
 | 
						|
  void SetTaskPriority(uint32_t aPriority, const char* aSite) {
 | 
						|
    MOZ_ASSERT(mPromise);
 | 
						|
    mPromise->SetTaskPriority(aPriority, aSite);
 | 
						|
  }
 | 
						|
 | 
						|
 private:
 | 
						|
  RefPtr<typename PromiseType::Private> mPromise;
 | 
						|
};
 | 
						|
 | 
						|
template <typename PromiseType>
 | 
						|
class MozPromiseHolder
 | 
						|
    : public MozPromiseHolderBase<PromiseType, MozPromiseHolder<PromiseType>> {
 | 
						|
 public:
 | 
						|
  using MozPromiseHolderBase<
 | 
						|
      PromiseType, MozPromiseHolder<PromiseType>>::MozPromiseHolderBase;
 | 
						|
  static constexpr void Check(){};
 | 
						|
};
 | 
						|
 | 
						|
template <typename PromiseType>
 | 
						|
class MozMonitoredPromiseHolder
 | 
						|
    : public MozPromiseHolderBase<PromiseType,
 | 
						|
                                  MozMonitoredPromiseHolder<PromiseType>> {
 | 
						|
 public:
 | 
						|
  // Provide a Monitor that should always be held when accessing this instance.
 | 
						|
  explicit MozMonitoredPromiseHolder(Monitor* const aMonitor)
 | 
						|
      : mMonitor(aMonitor) {
 | 
						|
    MOZ_ASSERT(aMonitor);
 | 
						|
  }
 | 
						|
 | 
						|
  MozMonitoredPromiseHolder(MozMonitoredPromiseHolder&& aOther) = delete;
 | 
						|
  MozMonitoredPromiseHolder& operator=(MozMonitoredPromiseHolder&& aOther) =
 | 
						|
      delete;
 | 
						|
 | 
						|
  void Check() const { mMonitor->AssertCurrentThreadOwns(); }
 | 
						|
 | 
						|
 private:
 | 
						|
  Monitor* const mMonitor;
 | 
						|
};
 | 
						|
 | 
						|
/*
 | 
						|
 * Class to encapsulate a MozPromise::Request reference. Use this as the member
 | 
						|
 * variable for a class waiting on a MozPromise.
 | 
						|
 */
 | 
						|
template <typename PromiseType>
 | 
						|
class MozPromiseRequestHolder {
 | 
						|
 public:
 | 
						|
  MozPromiseRequestHolder() = default;
 | 
						|
  ~MozPromiseRequestHolder() { MOZ_ASSERT(!mRequest); }
 | 
						|
 | 
						|
  void Track(already_AddRefed<typename PromiseType::Request> aRequest) {
 | 
						|
    MOZ_DIAGNOSTIC_ASSERT(!Exists());
 | 
						|
    mRequest = aRequest;
 | 
						|
  }
 | 
						|
 | 
						|
  void Complete() {
 | 
						|
    MOZ_DIAGNOSTIC_ASSERT(Exists());
 | 
						|
    mRequest = nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  // Disconnects and forgets an outstanding promise. The resolve/reject methods
 | 
						|
  // will never be called.
 | 
						|
  void Disconnect() {
 | 
						|
    MOZ_ASSERT(Exists());
 | 
						|
    RefPtr request = std::move(mRequest);
 | 
						|
    request->Disconnect();
 | 
						|
  }
 | 
						|
 | 
						|
  void DisconnectIfExists() {
 | 
						|
    if (Exists()) {
 | 
						|
      Disconnect();
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  bool Exists() const { return !!mRequest; }
 | 
						|
 | 
						|
 private:
 | 
						|
  RefPtr<typename PromiseType::Request> mRequest;
 | 
						|
};
 | 
						|
 | 
						|
// Asynchronous Potentially-Cross-Thread Method Calls.
 | 
						|
//
 | 
						|
// This machinery allows callers to schedule a promise-returning function
 | 
						|
// (a method and object, or a function object like a lambda) to be invoked
 | 
						|
// asynchronously on a given thread, while at the same time receiving a
 | 
						|
// promise upon which to invoke Then() immediately. InvokeAsync dispatches a
 | 
						|
// task to invoke the function on the proper thread and also chain the
 | 
						|
// resulting promise to the one that the caller received, so that resolve/
 | 
						|
// reject values are forwarded through.
 | 
						|
 | 
						|
namespace detail {
 | 
						|
 | 
						|
// Non-templated base class to allow us to use MOZ_COUNT_{C,D}TOR, which cause
 | 
						|
// assertions when used on templated types.
 | 
						|
class MethodCallBase {
 | 
						|
 public:
 | 
						|
  MOZ_COUNTED_DEFAULT_CTOR(MethodCallBase)
 | 
						|
  MOZ_COUNTED_DTOR_VIRTUAL(MethodCallBase)
 | 
						|
};
 | 
						|
 | 
						|
template <typename PromiseType, typename MethodType, typename ThisType,
 | 
						|
          typename... Storages>
 | 
						|
class MethodCall : public MethodCallBase {
 | 
						|
 public:
 | 
						|
  template <typename... Args>
 | 
						|
  MethodCall(MethodType aMethod, ThisType* aThisVal, Args&&... aArgs)
 | 
						|
      : mMethod(aMethod),
 | 
						|
        mThisVal(aThisVal),
 | 
						|
        mArgs(std::forward<Args>(aArgs)...) {
 | 
						|
    static_assert(sizeof...(Storages) == sizeof...(Args),
 | 
						|
                  "Storages and Args should have equal sizes");
 | 
						|
  }
 | 
						|
 | 
						|
  RefPtr<PromiseType> Invoke() { return mArgs.apply(mThisVal.get(), mMethod); }
 | 
						|
 | 
						|
 private:
 | 
						|
  MethodType mMethod;
 | 
						|
  RefPtr<ThisType> mThisVal;
 | 
						|
  RunnableMethodArguments<Storages...> mArgs;
 | 
						|
};
 | 
						|
 | 
						|
template <typename PromiseType, typename MethodType, typename ThisType,
 | 
						|
          typename... Storages>
 | 
						|
class ProxyRunnable : public CancelableRunnable {
 | 
						|
 public:
 | 
						|
  ProxyRunnable(
 | 
						|
      typename PromiseType::Private* aProxyPromise,
 | 
						|
      MethodCall<PromiseType, MethodType, ThisType, Storages...>* aMethodCall)
 | 
						|
      : CancelableRunnable("detail::ProxyRunnable"),
 | 
						|
        mProxyPromise(aProxyPromise),
 | 
						|
        mMethodCall(aMethodCall) {}
 | 
						|
 | 
						|
  NS_IMETHOD Run() override {
 | 
						|
    RefPtr<PromiseType> p = mMethodCall->Invoke();
 | 
						|
    mMethodCall = nullptr;
 | 
						|
    p->ChainTo(mProxyPromise.forget(), "<Proxy Promise>");
 | 
						|
    return NS_OK;
 | 
						|
  }
 | 
						|
 | 
						|
  nsresult Cancel() override { return Run(); }
 | 
						|
 | 
						|
 private:
 | 
						|
  RefPtr<typename PromiseType::Private> mProxyPromise;
 | 
						|
  UniquePtr<MethodCall<PromiseType, MethodType, ThisType, Storages...>>
 | 
						|
      mMethodCall;
 | 
						|
};
 | 
						|
 | 
						|
template <typename... Storages, typename PromiseType, typename ThisType,
 | 
						|
          typename... ArgTypes, typename... ActualArgTypes>
 | 
						|
static RefPtr<PromiseType> InvokeAsyncImpl(
 | 
						|
    nsISerialEventTarget* aTarget, ThisType* aThisVal, StaticString aCallerName,
 | 
						|
    RefPtr<PromiseType> (ThisType::*aMethod)(ArgTypes...),
 | 
						|
    ActualArgTypes&&... aArgs) {
 | 
						|
  MOZ_ASSERT(aTarget);
 | 
						|
 | 
						|
  typedef RefPtr<PromiseType> (ThisType::*MethodType)(ArgTypes...);
 | 
						|
  typedef detail::MethodCall<PromiseType, MethodType, ThisType, Storages...>
 | 
						|
      MethodCallType;
 | 
						|
  typedef detail::ProxyRunnable<PromiseType, MethodType, ThisType, Storages...>
 | 
						|
      ProxyRunnableType;
 | 
						|
 | 
						|
  MethodCallType* methodCall = new MethodCallType(
 | 
						|
      aMethod, aThisVal, std::forward<ActualArgTypes>(aArgs)...);
 | 
						|
  RefPtr<typename PromiseType::Private> p =
 | 
						|
      new (typename PromiseType::Private)(aCallerName);
 | 
						|
  RefPtr<ProxyRunnableType> r = new ProxyRunnableType(p, methodCall);
 | 
						|
  aTarget->Dispatch(r.forget());
 | 
						|
  return p;
 | 
						|
}
 | 
						|
 | 
						|
constexpr bool Any() { return false; }
 | 
						|
 | 
						|
template <typename T1>
 | 
						|
constexpr bool Any(T1 a) {
 | 
						|
  return static_cast<bool>(a);
 | 
						|
}
 | 
						|
 | 
						|
template <typename T1, typename... Ts>
 | 
						|
constexpr bool Any(T1 a, Ts... aOthers) {
 | 
						|
  return a || Any(aOthers...);
 | 
						|
}
 | 
						|
 | 
						|
}  // namespace detail
 | 
						|
 | 
						|
// InvokeAsync with explicitly-specified storages.
 | 
						|
// See ParameterStorage in nsThreadUtils.h for help.
 | 
						|
template <typename... Storages, typename PromiseType, typename ThisType,
 | 
						|
          typename... ArgTypes, typename... ActualArgTypes,
 | 
						|
          std::enable_if_t<sizeof...(Storages) != 0, int> = 0>
 | 
						|
static RefPtr<PromiseType> InvokeAsync(
 | 
						|
    nsISerialEventTarget* aTarget, ThisType* aThisVal, StaticString aCallerName,
 | 
						|
    RefPtr<PromiseType> (ThisType::*aMethod)(ArgTypes...),
 | 
						|
    ActualArgTypes&&... aArgs) {
 | 
						|
  static_assert(
 | 
						|
      sizeof...(Storages) == sizeof...(ArgTypes),
 | 
						|
      "Provided Storages and method's ArgTypes should have equal sizes");
 | 
						|
  static_assert(sizeof...(Storages) == sizeof...(ActualArgTypes),
 | 
						|
                "Provided Storages and ActualArgTypes should have equal sizes");
 | 
						|
  return detail::InvokeAsyncImpl<Storages...>(
 | 
						|
      aTarget, aThisVal, aCallerName, aMethod,
 | 
						|
      std::forward<ActualArgTypes>(aArgs)...);
 | 
						|
}
 | 
						|
 | 
						|
// InvokeAsync with no explicitly-specified storages, will copy arguments and
 | 
						|
// then move them out of the runnable into the target method parameters.
 | 
						|
template <typename... Storages, typename PromiseType, typename ThisType,
 | 
						|
          typename... ArgTypes, typename... ActualArgTypes,
 | 
						|
          std::enable_if_t<sizeof...(Storages) == 0, int> = 0>
 | 
						|
static RefPtr<PromiseType> InvokeAsync(
 | 
						|
    nsISerialEventTarget* aTarget, ThisType* aThisVal, StaticString aCallerName,
 | 
						|
    RefPtr<PromiseType> (ThisType::*aMethod)(ArgTypes...),
 | 
						|
    ActualArgTypes&&... aArgs) {
 | 
						|
  static_assert(
 | 
						|
      !detail::Any(
 | 
						|
          std::is_pointer_v<std::remove_reference_t<ActualArgTypes>>...),
 | 
						|
      "Cannot pass pointer types through InvokeAsync, Storages must be "
 | 
						|
      "provided");
 | 
						|
  static_assert(sizeof...(ArgTypes) == sizeof...(ActualArgTypes),
 | 
						|
                "Method's ArgTypes and ActualArgTypes should have equal sizes");
 | 
						|
  return detail::InvokeAsyncImpl<
 | 
						|
      StoreCopyPassByRRef<std::decay_t<ActualArgTypes>>...>(
 | 
						|
      aTarget, aThisVal, aCallerName, aMethod,
 | 
						|
      std::forward<ActualArgTypes>(aArgs)...);
 | 
						|
}
 | 
						|
 | 
						|
namespace detail {
 | 
						|
 | 
						|
template <typename Function, typename PromiseType>
 | 
						|
class ProxyFunctionRunnable : public CancelableRunnable {
 | 
						|
  using FunctionStorage = std::decay_t<Function>;
 | 
						|
 | 
						|
 public:
 | 
						|
  template <typename F>
 | 
						|
  ProxyFunctionRunnable(typename PromiseType::Private* aProxyPromise,
 | 
						|
                        F&& aFunction)
 | 
						|
      : CancelableRunnable("detail::ProxyFunctionRunnable"),
 | 
						|
        mProxyPromise(aProxyPromise),
 | 
						|
        mFunction(new FunctionStorage(std::forward<F>(aFunction))) {}
 | 
						|
 | 
						|
  NS_IMETHOD Run() override {
 | 
						|
    RefPtr<PromiseType> p = (*mFunction)();
 | 
						|
    mFunction = nullptr;
 | 
						|
    p->ChainTo(mProxyPromise.forget(), "<Proxy Promise>");
 | 
						|
    return NS_OK;
 | 
						|
  }
 | 
						|
 | 
						|
  nsresult Cancel() override { return Run(); }
 | 
						|
 | 
						|
 private:
 | 
						|
  RefPtr<typename PromiseType::Private> mProxyPromise;
 | 
						|
  UniquePtr<FunctionStorage> mFunction;
 | 
						|
};
 | 
						|
 | 
						|
template <typename T>
 | 
						|
constexpr static bool IsRefPtrMozPromise = false;
 | 
						|
template <typename T, typename U, bool B>
 | 
						|
constexpr static bool IsRefPtrMozPromise<RefPtr<MozPromise<T, U, B>>> = true;
 | 
						|
 | 
						|
}  // namespace detail
 | 
						|
 | 
						|
// Invoke a function object (e.g., lambda) asynchronously.
 | 
						|
// Return a promise that the function should eventually resolve or reject.
 | 
						|
template <typename Function>
 | 
						|
static auto InvokeAsync(nsISerialEventTarget* aTarget, StaticString aCallerName,
 | 
						|
                        Function&& aFunction) -> decltype(aFunction()) {
 | 
						|
  static_assert(!std::is_lvalue_reference_v<Function>,
 | 
						|
                "Function object must not be passed by lvalue-ref (to avoid "
 | 
						|
                "unplanned copies); Consider move()ing the object.");
 | 
						|
 | 
						|
  static_assert(detail::IsRefPtrMozPromise<decltype(aFunction())>,
 | 
						|
                "Function object must return RefPtr<MozPromise>");
 | 
						|
  MOZ_ASSERT(aTarget);
 | 
						|
  typedef RemoveSmartPointer<decltype(aFunction())> PromiseType;
 | 
						|
  typedef detail::ProxyFunctionRunnable<Function, PromiseType>
 | 
						|
      ProxyRunnableType;
 | 
						|
 | 
						|
  auto p = MakeRefPtr<typename PromiseType::Private>(aCallerName);
 | 
						|
  auto r = MakeRefPtr<ProxyRunnableType>(p, std::forward<Function>(aFunction));
 | 
						|
  aTarget->Dispatch(r.forget());
 | 
						|
  return p;
 | 
						|
}
 | 
						|
 | 
						|
#undef PROMISE_LOG
 | 
						|
#undef PROMISE_ASSERT
 | 
						|
#undef PROMISE_DEBUG
 | 
						|
 | 
						|
}  // namespace mozilla
 | 
						|
 | 
						|
#endif
 |