From d8bbc7858622b6d9c278469aab701ca0b609cddf Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 15 May 2024 05:35:49 +0200 Subject: Merging upstream version 126.0. Signed-off-by: Daniel Baumann --- xpcom/threads/MozPromise.h | 286 +++++++++++++++++++++++++++++++++------------ 1 file changed, 209 insertions(+), 77 deletions(-) (limited to 'xpcom/threads/MozPromise.h') 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 -# include - -# 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 +#include + +#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 @@ -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 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 mDispatchRv; -# endif +#endif }; /* @@ -935,6 +936,98 @@ class MozPromise : public MozPromiseBase { RefPtr mCompletionPromise; }; + template + class MapValue final : public ThenValueBase { + friend class ThenCommand; + constexpr static const bool SupportChaining = true; + using ResolveValueT_ = std::invoke_result_t; + using PromiseType = MozPromise; + + public: + explicit MapValue(nsISerialEventTarget* aResponseTarget, + ResolveFunction&& f, const char* aCallSite) + : ThenValueBase(aResponseTarget, aCallSite), + mResolveFunction(Some(std::forward(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 to MozPromise. + 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 mResolveFunction; + RefPtr mCompletionPromise; + }; + + template + class MapErrValue final : public ThenValueBase { + friend class ThenCommand; + constexpr static const bool SupportChaining = true; + using RejectValueT_ = std::invoke_result_t; + using PromiseType = MozPromise; + + public: + explicit MapErrValue(nsISerialEventTarget* aResponseTarget, + RejectFunction&& f, const char* aCallSite) + : ThenValueBase(aResponseTarget, aCallSite), + mRejectFunction(Some(std::forward(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 to MozPromise. + 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 mRejectFunction; + RefPtr mCompletionPromise; + }; + public: void ThenInternal(already_AddRefed 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 - 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 friend class MozPromise; @@ -1016,6 +1113,20 @@ class MozPromise : public MozPromiseBase { std::forward(aArgs)...); } + template + auto Map(Ts&&... aArgs) -> decltype(std::declval().Map( + std::forward(aArgs)...)) { + return static_cast>(*this)->Map( + std::forward(aArgs)...); + } + + template + auto MapErr(Ts&&... aArgs) -> decltype(std::declval().MapErr( + std::forward(aArgs)...)) { + return static_cast>(*this)->MapErr( + std::forward(aArgs)...); + } + void Track(MozPromiseRequestHolder& 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 + auto Map(nsISerialEventTarget* aResponseTarget, const char* aCallSite, + Function&& function) { + RefPtr> thenValue = new MapValue( + aResponseTarget, std::forward(function), aCallSite); + return ThenCommand>(aCallSite, thenValue.forget(), this); + } + + // Shorthand for a `Then` which simply forwards the resolve-value, but + // performs some additional work with the reject-value. + template + auto MapErr(nsISerialEventTarget* aResponseTarget, const char* aCallSite, + Function&& function) { + RefPtr> thenValue = new MapErrValue( + aResponseTarget, std::forward(function), aCallSite); + return ThenCommand>(aCallSite, thenValue.forget(), + this); + } + void ChainTo(already_AddRefed 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 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, IsExclusive ? 1 : 3> mThenValues; -# ifdef PROMISE_DEBUG +#ifdef PROMISE_DEBUG uint32_t mMagic2 = sMagic; -# endif +#endif nsTArray> 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 @@ -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 -- cgit v1.2.3