diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-15 03:35:49 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-15 03:35:49 +0000 |
commit | d8bbc7858622b6d9c278469aab701ca0b609cddf (patch) | |
tree | eff41dc61d9f714852212739e6b3738b82a2af87 /xpcom/threads/MozPromise.h | |
parent | Releasing progress-linux version 125.0.3-1~progress7.99u1. (diff) | |
download | firefox-d8bbc7858622b6d9c278469aab701ca0b609cddf.tar.xz firefox-d8bbc7858622b6d9c278469aab701ca0b609cddf.zip |
Merging upstream version 126.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'xpcom/threads/MozPromise.h')
-rw-r--r-- | xpcom/threads/MozPromise.h | 286 |
1 files changed, 209 insertions, 77 deletions
diff --git a/xpcom/threads/MozPromise.h b/xpcom/threads/MozPromise.h index af4ac657fd..c53037e119 100644 --- a/xpcom/threads/MozPromise.h +++ b/xpcom/threads/MozPromise.h @@ -4,44 +4,45 @@ * 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/. */ -#if !defined(MozPromise_h_) -# define MozPromise_h_ - -# include <type_traits> -# include <utility> - -# 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/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 +#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/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 { @@ -51,8 +52,8 @@ class Promise; extern LazyLogModule gMozPromiseLog; -# define PROMISE_LOG(x, ...) \ - MOZ_LOG(gMozPromiseLog, mozilla::LogLevel::Debug, (x, ##__VA_ARGS__)) +#define PROMISE_LOG(x, ...) \ + MOZ_LOG(gMozPromiseLog, mozilla::LogLevel::Debug, (x, ##__VA_ARGS__)) namespace detail { template <typename F> @@ -235,10 +236,10 @@ class MozPromise : public MozPromiseBase { mMutex("MozPromise Mutex"), mHaveRequest(false), mIsCompletionPromise(aIsCompletionPromise) -# ifdef PROMISE_DEBUG +#ifdef PROMISE_DEBUG , mMagic4(&mMutex) -# endif +#endif { PROMISE_LOG("%s creating MozPromise (%p)", mCreationSite, this); } @@ -501,12 +502,12 @@ class MozPromise : public MozPromiseBase { MOZ_ASSERT(aResponseTarget); } -# ifdef PROMISE_DEBUG +#ifdef PROMISE_DEBUG ~ThenValueBase() { mMagic1 = 0; mMagic2 = 0; } -# endif +#endif void AssertIsDead() { PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic); @@ -520,7 +521,7 @@ class MozPromise : public MozPromiseBase { if (MozPromiseBase* p = CompletionPromise()) { p->AssertIsDead(); } else { -# ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED +#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED if (MOZ_UNLIKELY(!Request::mDisconnected)) { MOZ_CRASH_UNSAFE_PRINTF( "MozPromise::ThenValue created from '%s' destroyed without being " @@ -529,7 +530,7 @@ class MozPromise : public MozPromiseBase { mDispatchRv ? GetStaticErrorName(*mDispatchRv) : "not dispatched"); } -# endif +#endif } } @@ -620,23 +621,23 @@ class MozPromise : public MozPromiseBase { } void SetDispatchRv(nsresult aRv) { -# ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED +#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED mDispatchRv = Some(aRv); -# endif +#endif } nsCOMPtr<nsISerialEventTarget> mResponseTarget; // May be released on any thread. -# ifdef PROMISE_DEBUG +#ifdef PROMISE_DEBUG uint32_t mMagic1 = sMagic; -# endif +#endif const char* mCallSite; -# ifdef PROMISE_DEBUG +#ifdef PROMISE_DEBUG uint32_t mMagic2 = sMagic; -# endif -# ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED +#endif +#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED Maybe<nsresult> mDispatchRv; -# endif +#endif }; /* @@ -935,6 +936,98 @@ class MozPromise : public MozPromiseBase { 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, const char* 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, const char* 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, const char* aCallSite) { @@ -957,17 +1050,21 @@ class MozPromise : public MozPromiseBase { 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). + * 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. + * 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 ThenCommand { - // Allow Promise1::ThenCommand to access the private constructor, + class MOZ_TEMPORARY_CLASS ThenCommand { + // Allow Promise1::ThenCommand to access the private constructor // Promise2::ThenCommand(ThenCommand&&). template <typename, typename, bool> friend class MozPromise; @@ -1016,6 +1113,20 @@ class MozPromise : public MozPromiseBase { 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); @@ -1052,6 +1163,27 @@ class MozPromise : public MozPromiseBase { 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, const char* 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, const char* 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, const char* aCallSite) { MutexAutoLock lock(mMutex); @@ -1088,7 +1220,7 @@ class MozPromise : public MozPromiseBase { } } -# ifdef MOZ_WIDGET_ANDROID +#ifdef MOZ_WIDGET_ANDROID // Creates a C++ MozPromise from its Java counterpart, GeckoResult. [[nodiscard]] static RefPtr<MozPromise> FromGeckoResult( java::GeckoResult::Param aGeckoResult) { @@ -1103,7 +1235,7 @@ class MozPromise : public MozPromiseBase { aGeckoResult->NativeThen(resolve, reject); return p; } -# endif +#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 @@ -1165,12 +1297,12 @@ class MozPromise : public MozPromiseBase { MOZ_ASSERT(mThenValues.IsEmpty()); MOZ_ASSERT(mChainedPromises.IsEmpty()); } -# ifdef PROMISE_DEBUG +#ifdef PROMISE_DEBUG mMagic1 = 0; mMagic2 = 0; mMagic3 = 0; mMagic4 = nullptr; -# endif +#endif }; const char* mCreationSite; // For logging @@ -1179,24 +1311,24 @@ class MozPromise : public MozPromiseBase { bool mUseSynchronousTaskDispatch = false; bool mUseDirectTaskDispatch = false; uint32_t mPriority = nsIRunnablePriority::PRIORITY_NORMAL; -# ifdef PROMISE_DEBUG +#ifdef PROMISE_DEBUG uint32_t mMagic1 = sMagic; -# endif +#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 +#ifdef PROMISE_DEBUG uint32_t mMagic2 = sMagic; -# endif +#endif nsTArray<RefPtr<Private>> mChainedPromises; -# ifdef PROMISE_DEBUG +#ifdef PROMISE_DEBUG uint32_t mMagic3 = sMagic; -# endif +#endif bool mHaveRequest; const bool mIsCompletionPromise; -# ifdef PROMISE_DEBUG +#ifdef PROMISE_DEBUG void* mMagic4; -# endif +#endif }; template <typename ResolveValueT, typename RejectValueT, bool IsExclusive> @@ -1718,9 +1850,9 @@ static auto InvokeAsync(nsISerialEventTarget* aTarget, const char* aCallerName, return p; } -# undef PROMISE_LOG -# undef PROMISE_ASSERT -# undef PROMISE_DEBUG +#undef PROMISE_LOG +#undef PROMISE_ASSERT +#undef PROMISE_DEBUG } // namespace mozilla |