/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim:set ts=2 sw=2 sts=2 et cindent: */ /* 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 AllocationPolicy_h_ #define AllocationPolicy_h_ #include #include "MediaInfo.h" #include "PlatformDecoderModule.h" #include "TimeUnits.h" #include "mozilla/MozPromise.h" #include "mozilla/NotNull.h" #include "mozilla/ReentrantMonitor.h" #include "mozilla/StaticMutex.h" namespace mozilla { /** * Before calling PDMFactory::CreateDecoder(), Alloc() must be called on the * policy to get a token object as a permission to create a decoder. The * token should stay alive until Shutdown() is called on the decoder. The * destructor of the token will restore the decoder count so it is available * for next calls of Alloc(). */ class AllocPolicy { NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AllocPolicy) public: class Token { NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Token) protected: virtual ~Token() = default; }; using Promise = MozPromise, bool, true>; // Acquire a token for decoder creation. Thread-safe. virtual RefPtr Alloc() = 0; protected: virtual ~AllocPolicy() = default; }; /** * This is a singleton which controls the number of decoders that can be created * concurrently. * Instance() will return the TrackType global AllocPolicy. * Instance() will always return a non-null value. */ class GlobalAllocPolicy { public: // Get the singleton for the given track type. Thread-safe. static NotNull Instance(TrackInfo::TrackType aTrack); private: // Protect access to Instance(). static StaticMutex sMutex MOZ_UNANNOTATED; }; /** This the actual base implementation underneath all AllocPolicy objects and * control how many decoders can be created concurrently. * Alloc() must be called to get a token object as a permission to perform an * action. The token should stay alive until Shutdown() is called on the * decoder. The destructor of the token will restore the decoder count so it is * available for next calls of Alloc(). **/ class AllocPolicyImpl : public AllocPolicy { public: explicit AllocPolicyImpl(int aDecoderLimit); RefPtr Alloc() override; protected: virtual ~AllocPolicyImpl(); void RejectAll(); int MaxDecoderLimit() const { return mMaxDecoderLimit; } private: class AutoDeallocToken; using PromisePrivate = Promise::Private; // Called by the destructor of TokenImpl to restore the decoder limit. void Dealloc(); // Decrement the decoder limit and resolve a promise if available. void ResolvePromise(ReentrantMonitorAutoEnter& aProofOfLock); const int mMaxDecoderLimit; ReentrantMonitor mMonitor MOZ_UNANNOTATED; // The number of decoders available for creation. int mDecoderLimit; // Requests to acquire tokens. std::queue> mPromises; }; /** * This class allows to track and serialise a single decoder allocation at a * time */ class SingleAllocPolicy : public AllocPolicyImpl { using TrackType = TrackInfo::TrackType; public: SingleAllocPolicy(TrackType aTrack, TaskQueue* aOwnerThread) : AllocPolicyImpl(1), mTrack(aTrack), mOwnerThread(aOwnerThread) {} RefPtr Alloc() override; // Cancel the request to GlobalAllocPolicy and reject the current token // request. Note this must happen before mOwnerThread->BeginShutdown(). void Cancel(); private: class AutoDeallocCombinedToken; virtual ~SingleAllocPolicy(); const TrackType mTrack; RefPtr mOwnerThread; MozPromiseHolder mPendingPromise; MozPromiseRequestHolder mTokenRequest; }; class AllocationWrapper final : public MediaDataDecoder { using Token = AllocPolicy::Token; public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AllocationWrapper, final); AllocationWrapper(already_AddRefed aDecoder, already_AddRefed aToken); RefPtr Init() override { return mDecoder->Init(); } RefPtr Decode(MediaRawData* aSample) override { return mDecoder->Decode(aSample); } bool CanDecodeBatch() const override { return mDecoder->CanDecodeBatch(); } RefPtr DecodeBatch( nsTArray>&& aSamples) override { return mDecoder->DecodeBatch(std::move(aSamples)); } RefPtr Drain() override { return mDecoder->Drain(); } RefPtr Flush() override { return mDecoder->Flush(); } bool IsHardwareAccelerated(nsACString& aFailureReason) const override { return mDecoder->IsHardwareAccelerated(aFailureReason); } nsCString GetDescriptionName() const override { return mDecoder->GetDescriptionName(); } nsCString GetProcessName() const override { return mDecoder->GetProcessName(); } nsCString GetCodecName() const override { return mDecoder->GetCodecName(); } void SetSeekThreshold(const media::TimeUnit& aTime) override { mDecoder->SetSeekThreshold(aTime); } bool SupportDecoderRecycling() const override { return mDecoder->SupportDecoderRecycling(); } RefPtr Shutdown() override; ConversionRequired NeedsConversion() const override { return mDecoder->NeedsConversion(); } typedef MozPromise, MediaResult, /* IsExclusive = */ true> AllocateDecoderPromise; // Will create a decoder has soon as one can be created according to the // AllocPolicy (or GlobalAllocPolicy if aPolicy is null) // Warning: all aParams members must be valid until the promise has been // resolved, as some contains raw pointers to objects. static RefPtr CreateDecoder( const CreateDecoderParams& aParams, AllocPolicy* aPolicy = nullptr); private: ~AllocationWrapper(); RefPtr mDecoder; RefPtr mToken; }; } // namespace mozilla #endif